1/* 2Copyright (c) 2000 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 24#include <iostream> 25#include <sstream> 26#include <fstream> 27#include "tinyxml.h" 28using namespace std; 29 30 31bool TiXmlBase::condenseWhiteSpace = true; 32 33 34void TiXmlBase::PutString( const std::string& str, std::ostream* stream ) 35{ 36 // Scan for the all important '&' 37 unsigned int i=0, j=0; 38 39 while ( i < str.length() ) 40 { 41 unsigned next = str.find( '&', i ); 42 43 if ( next == string::npos ) 44 { 45 stream->write( &str.at( i ), str.length() - i ); 46 return; 47 } 48 49 // We found an entity. 50 if ( next - i > 0 ) 51 stream->write( &str.at( i ), next - i ); 52 i = next; 53 54 // Check for the special "&#x" entitity 55 if ( i < str.length() - 2 56 && str[i] == '&' 57 && str[i+1] == '#' 58 && str[i+2] == 'x' ) 59 { 60 stream->put( str[i] ); 61 } 62 else 63 { 64 for ( j=0; j<NUM_ENTITY; ++j ) 65 { 66 if ( str[i] == entity[j].chr ) 67 { 68 stream->write( entity[j].str, entity[j].strLength ); 69 break; 70 } 71 } 72 if ( j == NUM_ENTITY ) 73 { 74 stream->put( str[i] ); 75 } 76 } 77 ++i; 78 } 79} 80 81 82TiXmlNode::TiXmlNode( NodeType _type ) 83{ 84 parent = 0; 85 type = _type; 86 firstChild = 0; 87 lastChild = 0; 88 prev = 0; 89 next = 0; 90} 91 92 93TiXmlNode::~TiXmlNode() 94{ 95 TiXmlNode* node = firstChild; 96 TiXmlNode* temp = 0; 97 98 while ( node ) 99 { 100 temp = node; 101 node = node->next; 102 delete temp; 103 } 104} 105 106 107void TiXmlNode::Clear() 108{ 109 TiXmlNode* node = firstChild; 110 TiXmlNode* temp = 0; 111 112 while ( node ) 113 { 114 temp = node; 115 node = node->next; 116 delete temp; 117 } 118 119 firstChild = 0; 120 lastChild = 0; 121} 122 123 124TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) 125{ 126 node->parent = this; 127 128 node->prev = lastChild; 129 node->next = 0; 130 131 if ( lastChild ) 132 lastChild->next = node; 133 else 134 firstChild = node; // it was an empty list. 135 136 lastChild = node; 137 return node; 138} 139 140 141TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) 142{ 143 TiXmlNode* node = addThis.Clone(); 144 if ( !node ) 145 return 0; 146 147 return LinkEndChild( node ); 148} 149 150 151TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) 152{ 153 if ( beforeThis->parent != this ) 154 return 0; 155 156 TiXmlNode* node = addThis.Clone(); 157 if ( !node ) 158 return 0; 159 node->parent = this; 160 161 node->next = beforeThis; 162 node->prev = beforeThis->prev; 163 beforeThis->prev->next = node; 164 beforeThis->prev = node; 165 return node; 166} 167 168 169TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) 170{ 171 if ( afterThis->parent != this ) 172 return 0; 173 174 TiXmlNode* node = addThis.Clone(); 175 if ( !node ) 176 return 0; 177 node->parent = this; 178 179 node->prev = afterThis; 180 node->next = afterThis->next; 181 afterThis->next->prev = node; 182 afterThis->next = node; 183 return node; 184} 185 186 187TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) 188{ 189 if ( replaceThis->parent != this ) 190 return 0; 191 192 TiXmlNode* node = withThis.Clone(); 193 if ( !node ) 194 return 0; 195 196 node->next = replaceThis->next; 197 node->prev = replaceThis->prev; 198 199 if ( replaceThis->next ) 200 replaceThis->next->prev = node; 201 else 202 lastChild = node; 203 204 if ( replaceThis->prev ) 205 replaceThis->prev->next = node; 206 else 207 firstChild = node; 208 209 delete replaceThis; 210 node->parent = this; 211 return node; 212} 213 214 215bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) 216{ 217 if ( removeThis->parent != this ) 218 { 219 assert( 0 ); 220 return false; 221 } 222 223 if ( removeThis->next ) 224 removeThis->next->prev = removeThis->prev; 225 else 226 lastChild = removeThis->prev; 227 228 if ( removeThis->prev ) 229 removeThis->prev->next = removeThis->next; 230 else 231 firstChild = removeThis->next; 232 233 delete removeThis; 234 return true; 235} 236 237 238TiXmlNode* TiXmlNode::FirstChild( const std::string& value ) const 239{ 240 TiXmlNode* node; 241 for ( node = firstChild; node; node = node->next ) 242 { 243 if ( node->Value() == value ) 244 return node; 245 } 246 return 0; 247} 248 249 250TiXmlNode* TiXmlNode::LastChild( const std::string& value ) const 251{ 252 TiXmlNode* node; 253 for ( node = lastChild; node; node = node->prev ) 254 { 255 if ( node->Value() == value ) 256 return node; 257 } 258 return 0; 259} 260 261 262TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) const 263{ 264 if ( !previous ) 265 { 266 return FirstChild(); 267 } 268 else 269 { 270 assert( previous->parent == this ); 271 return previous->NextSibling(); 272 } 273} 274 275 276TiXmlNode* TiXmlNode::IterateChildren( const std::string& val, TiXmlNode* previous ) const 277{ 278 if ( !previous ) 279 { 280 return FirstChild( val ); 281 } 282 else 283 { 284 assert( previous->parent == this ); 285 return previous->NextSibling( val ); 286 } 287} 288 289 290TiXmlNode* TiXmlNode::NextSibling( const std::string& value ) const 291{ 292 TiXmlNode* node; 293 for ( node = next; node; node = node->next ) 294 { 295 if ( node->Value() == value ) 296 return node; 297 } 298 return 0; 299} 300 301 302TiXmlNode* TiXmlNode::PreviousSibling( const std::string& value ) const 303{ 304 TiXmlNode* node; 305 for ( node = prev; node; node = node->prev ) 306 { 307 if ( node->Value() == value ) 308 return node; 309 } 310 return 0; 311} 312 313 314void TiXmlElement::RemoveAttribute( const std::string& name ) 315{ 316 TiXmlAttribute* node = attributeSet.Find( name ); 317 if ( node ) 318 { 319 attributeSet.Remove( node ); 320 delete node; 321 } 322} 323 324 325TiXmlElement* TiXmlNode::FirstChildElement() const 326{ 327 TiXmlNode* node; 328 329 for ( node = FirstChild(); 330 node; 331 node = node->NextSibling() ) 332 { 333 if ( node->ToElement() ) 334 return node->ToElement(); 335 } 336 return 0; 337} 338 339 340TiXmlElement* TiXmlNode::FirstChildElement( const std::string& value ) const 341{ 342 TiXmlNode* node; 343 344 for ( node = FirstChild( value ); 345 node; 346 node = node->NextSibling( value ) ) 347 { 348 if ( node->ToElement() ) 349 return node->ToElement(); 350 } 351 return 0; 352} 353 354 355TiXmlElement* TiXmlNode::NextSiblingElement() const 356{ 357 TiXmlNode* node; 358 359 for ( node = NextSibling(); 360 node; 361 node = node->NextSibling() ) 362 { 363 if ( node->ToElement() ) 364 return node->ToElement(); 365 } 366 return 0; 367} 368 369 370TiXmlElement* TiXmlNode::NextSiblingElement( const std::string& value ) const 371{ 372 TiXmlNode* node; 373 374 for ( node = NextSibling( value ); 375 node; 376 node = node->NextSibling( value ) ) 377 { 378 if ( node->ToElement() ) 379 return node->ToElement(); 380 } 381 return 0; 382} 383 384 385 386TiXmlDocument* TiXmlNode::GetDocument() const 387{ 388 const TiXmlNode* node; 389 390 for( node = this; node; node = node->parent ) 391 { 392 if ( node->ToDocument() ) 393 return node->ToDocument(); 394 } 395 return 0; 396} 397 398 399// TiXmlElement::TiXmlElement() 400// : TiXmlNode( TiXmlNode::ELEMENT ) 401// { 402// } 403 404TiXmlElement::TiXmlElement( const std::string& _value ) 405 : TiXmlNode( TiXmlNode::ELEMENT ) 406{ 407 firstChild = lastChild = 0; 408 value = _value; 409} 410 411TiXmlElement::~TiXmlElement() 412{ 413 while( attributeSet.First() ) 414 { 415 TiXmlAttribute* node = attributeSet.First(); 416 attributeSet.Remove( node ); 417 delete node; 418 } 419} 420 421const std::string* TiXmlElement::Attribute( const std::string& name ) const 422{ 423 TiXmlAttribute* node = attributeSet.Find( name ); 424 425 if ( node ) 426 return &(node->Value() ); 427 428 return 0; 429} 430 431 432const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const 433{ 434 const std::string* s = Attribute( name ); 435 if ( s ) 436 *i = atoi( s->c_str() ); 437 else 438 *i = 0; 439 return s; 440} 441 442 443void TiXmlElement::SetAttribute( const std::string& name, int val ) 444{ 445 char buf[64]; 446 sprintf( buf, "%d", val ); 447 448 std::string v = buf; 449 450 SetAttribute( name, v ); 451} 452 453 454void TiXmlElement::SetAttribute( const std::string& name, const std::string& value ) 455{ 456 TiXmlAttribute* node = attributeSet.Find( name ); 457 if ( node ) 458 { 459 node->SetValue( value ); 460 return; 461 } 462 463 TiXmlAttribute* attrib = new TiXmlAttribute( name, value ); 464 if ( attrib ) 465 { 466 attributeSet.Add( attrib ); 467 } 468 else 469 { 470 TiXmlDocument* document = GetDocument(); 471 if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY ); 472 } 473} 474 475 476void TiXmlElement::Print( FILE* cfile, int depth ) const 477{ 478 int i; 479 for ( i=0; i<depth; i++ ) 480 { 481 fprintf( cfile, " " ); 482 } 483 484 fprintf( cfile, "<%s", value.c_str() ); 485 486 TiXmlAttribute* attrib; 487 for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) 488 { 489 fprintf( cfile, " " ); 490 attrib->Print( cfile, depth ); 491 } 492 493 // There are 3 different formatting approaches: 494 // 1) An element without children is printed as a <foo /> node 495 // 2) An element with only a text child is printed as <foo> text </foo> 496 // 3) An element with children is printed on multiple lines. 497 TiXmlNode* node; 498 if ( !firstChild ) 499 { 500 fprintf( cfile, " />" ); 501 } 502 else if ( firstChild == lastChild && firstChild->ToText() ) 503 { 504 fprintf( cfile, ">" ); 505 firstChild->Print( cfile, depth + 1 ); 506 fprintf( cfile, "</%s>", value.c_str() ); 507 } 508 else 509 { 510 fprintf( cfile, ">" ); 511 512 for ( node = firstChild; node; node=node->NextSibling() ) 513 { 514 if ( !node->ToText() ) 515 { 516 fprintf( cfile, "\n" ); 517 } 518 node->Print( cfile, depth+1 ); 519 } 520 fprintf( cfile, "\n" ); 521 for( i=0; i<depth; ++i ) 522 fprintf( cfile, " " ); 523 fprintf( cfile, "</%s>", value.c_str() ); 524 } 525} 526 527 528void TiXmlElement::StreamOut( std::ostream* stream ) const 529{ 530 (*stream) << "<" << value; 531 532 TiXmlAttribute* attrib; 533 for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) 534 { 535 (*stream) << " "; 536 attrib->StreamOut( stream ); 537 } 538 539 // If this node has children, give it a closing tag. Else 540 // make it an empty tag. 541 TiXmlNode* node; 542 if ( firstChild ) 543 { 544 (*stream) << ">"; 545 546 for ( node = firstChild; node; node=node->NextSibling() ) 547 { 548 node->StreamOut( stream ); 549 } 550 (*stream) << "</" << value << ">"; 551 } 552 else 553 { 554 (*stream) << " />"; 555 } 556} 557 558 559TiXmlNode* TiXmlElement::Clone() const 560{ 561 TiXmlElement* clone = new TiXmlElement( Value() ); 562 563 if ( !clone ) 564 return 0; 565 566 CopyToClone( clone ); 567 568 // Clone the attributes, then clone the children. 569 TiXmlAttribute* attribute = 0; 570 for( attribute = attributeSet.First(); 571 attribute; 572 attribute = attribute->Next() ) 573 { 574 clone->SetAttribute( attribute->Name(), attribute->Value() ); 575 } 576 577 TiXmlNode* node = 0; 578 for ( node = firstChild; node; node = node->NextSibling() ) 579 { 580 clone->LinkEndChild( node->Clone() ); 581 } 582 return clone; 583} 584 585 586TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) 587{ 588 error = false; 589// ignoreWhiteSpace = true; 590} 591 592 593TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) 594{ 595// ignoreWhiteSpace = true; 596 value = documentName; 597 error = false; 598} 599 600 601bool TiXmlDocument::LoadFile() 602{ 603 return LoadFile( value ); 604} 605 606 607bool TiXmlDocument::SaveFile() const 608{ 609 return SaveFile( value ); 610} 611 612 613bool TiXmlDocument::LoadFile( const std::string& filename ) 614{ 615 // Delete the existing data: 616 Clear(); 617 value = filename; 618 619 FILE* file = fopen( filename.c_str(), "r" ); 620 621 if ( file ) 622 { 623 // Get the file size, so we can pre-allocate the string. HUGE speed impact. 624 long length = 0; 625 fseek( file, 0, SEEK_END ); 626 length = ftell( file ); 627 fseek( file, 0, SEEK_SET ); 628 629 // If we have a file, assume it is all one big XML file, and read it in. 630 // The document parser may decide the document ends sooner than the entire file, however. 631 std::string data; 632 data.reserve( length ); 633 634 const int BUF_SIZE = 2048; 635 char buf[BUF_SIZE]; 636 637 while( fgets( buf, BUF_SIZE, file ) ) 638 { 639 data += buf; 640 } 641 fclose( file ); 642 643 Parse( data.c_str() ); 644 if ( !Error() ) 645 { 646 return true; 647 } 648 } 649 SetError( TIXML_ERROR_OPENING_FILE ); 650 return false; 651} 652 653 654bool TiXmlDocument::SaveFile( const std::string& filename ) const 655{ 656 // The old c stuff lives on... 657 FILE* fp = fopen( filename.c_str(), "w" ); 658 if ( fp ) 659 { 660 Print( fp, 0 ); 661 fclose( fp ); 662 return true; 663 } 664 return false; 665} 666 667 668TiXmlNode* TiXmlDocument::Clone() const 669{ 670 TiXmlDocument* clone = new TiXmlDocument(); 671 if ( !clone ) 672 return 0; 673 674 CopyToClone( clone ); 675 clone->error = error; 676 clone->errorDesc = errorDesc; 677 678 TiXmlNode* node = 0; 679 for ( node = firstChild; node; node = node->NextSibling() ) 680 { 681 clone->LinkEndChild( node->Clone() ); 682 } 683 return clone; 684} 685 686 687void TiXmlDocument::Print( FILE* cfile, int depth ) const 688{ 689 TiXmlNode* node; 690 for ( node=FirstChild(); node; node=node->NextSibling() ) 691 { 692 node->Print( cfile, depth ); 693 fprintf( cfile, "\n" ); 694 } 695} 696 697 698void TiXmlDocument::StreamOut( std::ostream* out ) const 699{ 700 TiXmlNode* node; 701 for ( node=FirstChild(); node; node=node->NextSibling() ) 702 { 703 node->StreamOut( out ); 704 705 // Special rule for streams: stop after the root element. 706 // The stream in code will only read one element, so don't 707 // write more than one. 708 if ( node->ToElement() ) 709 break; 710 } 711} 712 713 714TiXmlAttribute* TiXmlAttribute::Next() const 715{ 716 // We are using knowledge of the sentinel. The sentinel 717 // have a value or name. 718 if ( next->value.empty() && next->name.empty() ) 719 return 0; 720 return next; 721} 722 723 724TiXmlAttribute* TiXmlAttribute::Previous() const 725{ 726 // We are using knowledge of the sentinel. The sentinel 727 // have a value or name. 728 if ( prev->value.empty() && prev->name.empty() ) 729 return 0; 730 return prev; 731} 732 733 734void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const 735{ 736 ostringstream stream( ostringstream::out ); 737 stream.str().reserve( 500 ); 738 739 StreamOut( &stream ); 740 fprintf( cfile, "%s", stream.str().c_str() ); 741} 742 743 744void TiXmlAttribute::StreamOut( std::ostream* stream ) const 745{ 746 if ( value.find( '\"' ) != std::string::npos ) 747 { 748 PutString( name, stream ); 749 (*stream) << "=" << "'"; 750 PutString( value, stream ); 751 (*stream) << "'"; 752 } 753 else 754 { 755 PutString( name, stream ); 756 (*stream) << "=" << "\""; 757 PutString( value, stream ); 758 (*stream) << "\""; 759 } 760} 761 762 763void TiXmlAttribute::SetIntValue( int value ) 764{ 765 std::string s; 766 std::ostringstream stream( s ); 767 stream << value; 768 SetValue( stream.str() ); 769} 770 771 772void TiXmlAttribute::SetDoubleValue( double value ) 773{ 774 std::string s; 775 std::ostringstream stream( s ); 776 stream << value; 777 SetValue( stream.str() ); 778} 779 780 781const int TiXmlAttribute::IntValue() const 782{ 783 int v; 784 std::istringstream string( value ); 785 string >> v; 786 return v; 787} 788 789 790const double TiXmlAttribute::DoubleValue() const 791{ 792 double v; 793 std::istringstream string( value ); 794 string >> v; 795 return v; 796} 797 798 799void TiXmlComment::Print( FILE* cfile, int depth ) const 800{ 801 ostringstream stream( ostringstream::out ); 802 stream.str().reserve( 1000 ); 803 804 for ( int i=0; i<depth; i++ ) 805 { 806 fprintf( cfile, " " ); 807 } 808 StreamOut( &stream ); 809 fprintf( cfile, "%s", stream.str().c_str() ); 810} 811 812 813void TiXmlComment::StreamOut( std::ostream* stream ) const 814{ 815 (*stream) << "<!--"; 816 PutString( value, stream ); 817 (*stream) << "-->"; 818} 819 820 821TiXmlNode* TiXmlComment::Clone() const 822{ 823 TiXmlComment* clone = new TiXmlComment(); 824 825 if ( !clone ) 826 return 0; 827 828 CopyToClone( clone ); 829 return clone; 830} 831 832 833void TiXmlText::Print( FILE* cfile, int depth ) const 834{ 835 ostringstream stream( ostringstream::out ); 836 stream.str().reserve( 1000 ); 837 StreamOut( &stream ); 838 fprintf( cfile, "%s", stream.str().c_str() ); 839} 840 841 842void TiXmlText::StreamOut( std::ostream* stream ) const 843{ 844 PutString( value, stream ); 845} 846 847 848TiXmlNode* TiXmlText::Clone() const 849{ 850 TiXmlText* clone = 0; 851 clone = new TiXmlText( "" ); 852 853 if ( !clone ) 854 return 0; 855 856 CopyToClone( clone ); 857 return clone; 858} 859 860 861TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, 862 const std::string& _encoding, 863 const std::string& _standalone ) 864 : TiXmlNode( TiXmlNode::DECLARATION ) 865 , version( _version ) 866 , encoding( _encoding ) 867 , standalone( _standalone ) 868{ 869} 870 871 872void TiXmlDeclaration::Print( FILE* cfile, int depth ) const 873{ 874 ostringstream stream( ostringstream::out ); 875 stream.str().reserve( 200 ); 876 StreamOut( &stream ); 877 fprintf( cfile, "%s", stream.str().c_str() ); 878} 879 880 881void TiXmlDeclaration::StreamOut( std::ostream* stream ) const 882{ 883 (*stream) << "<?xml "; 884 885 if ( !version.empty() ) 886 { 887 (*stream) << "version=\""; 888 PutString( version, stream ); 889 (*stream) << "\" "; 890 } 891 if ( !encoding.empty() ) 892 { 893 (*stream) << "encoding=\""; 894 PutString( encoding, stream ); 895 (*stream ) << "\" "; 896 } 897 if ( !standalone.empty() ) 898 { 899 (*stream) << "standalone=\""; 900 PutString( standalone, stream ); 901 (*stream) << "\" "; 902 } 903 (*stream) << "?>"; 904} 905 906 907TiXmlNode* TiXmlDeclaration::Clone() const 908{ 909 TiXmlDeclaration* clone = new TiXmlDeclaration(); 910 911 if ( !clone ) 912 return 0; 913 914 CopyToClone( clone ); 915 clone->version = version; 916 clone->encoding = encoding; 917 clone->standalone = standalone; 918 return clone; 919} 920 921 922void TiXmlUnknown::Print( FILE* cfile, int depth ) const 923{ 924 ostringstream stream( ostringstream::out ); 925 stream.str().reserve( 200 ); 926 StreamOut( &stream ); 927 928 for ( int i=0; i<depth; i++ ) 929 fprintf( cfile, " " ); 930 fprintf( cfile, "%s", stream.str().c_str() ); 931} 932 933 934void TiXmlUnknown::StreamOut( std::ostream* stream ) const 935{ 936 (*stream) << "<" << value << ">"; // Don't use entities hear! It is unknown. 937} 938 939 940TiXmlNode* TiXmlUnknown::Clone() const 941{ 942 TiXmlUnknown* clone = new TiXmlUnknown(); 943 944 if ( !clone ) 945 return 0; 946 947 CopyToClone( clone ); 948 return clone; 949} 950 951 952TiXmlAttributeSet::TiXmlAttributeSet() 953{ 954 sentinel.next = &sentinel; 955 sentinel.prev = &sentinel; 956} 957 958 959TiXmlAttributeSet::~TiXmlAttributeSet() 960{ 961 assert( sentinel.next == &sentinel ); 962 assert( sentinel.prev == &sentinel ); 963} 964 965 966void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) 967{ 968 assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. 969 970 addMe->next = &sentinel; 971 addMe->prev = sentinel.prev; 972 973 sentinel.prev->next = addMe; 974 sentinel.prev = addMe; 975} 976 977void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) 978{ 979 TiXmlAttribute* node; 980 981 for( node = sentinel.next; node != &sentinel; node = node->next ) 982 { 983 if ( node == removeMe ) 984 { 985 node->prev->next = node->next; 986 node->next->prev = node->prev; 987 node->next = 0; 988 node->prev = 0; 989 return; 990 } 991 } 992 assert( 0 ); // we tried to remove a non-linked attribute. 993} 994 995 996TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const 997{ 998 TiXmlAttribute* node; 999 1000 for( node = sentinel.next; node != &sentinel; node = node->next ) 1001 { 1002 if ( node->Name() == name ) 1003 return node; 1004 } 1005 return 0; 1006} 1007 1008