| /* | |
| www.sourceforge.net/projects/tinyxml | |
| Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) | |
| This software is provided 'as-is', without any express or implied | |
| warranty. In no event will the authors be held liable for any | |
| damages arising from the use of this software. | |
| Permission is granted to anyone to use this software for any | |
| purpose, including commercial applications, and to alter it and | |
| redistribute it freely, subject to the following restrictions: | |
| 1. The origin of this software must not be misrepresented; you must | |
| not claim that you wrote the original software. If you use this | |
| software in a product, an acknowledgment in the product documentation | |
| would be appreciated but is not required. | |
| 2. Altered source versions must be plainly marked as such, and | |
| must not be misrepresented as being the original software. | |
| 3. This notice may not be removed or altered from any source | |
| distribution. | |
| */ | |
| #include <ctype.h> | |
| #include "tinyxml.h" | |
| #ifdef TIXML_USE_STL | |
| #include <sstream> | |
| #endif | |
| bool TiXmlBase::condenseWhiteSpace = true; | |
| void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream ) | |
| { | |
| TIXML_STRING buffer; | |
| PutString( str, &buffer ); | |
| (*stream) << buffer; | |
| } | |
| void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) | |
| { | |
| int i=0; | |
| while( i<(int)str.length() ) | |
| { | |
| unsigned char c = (unsigned char) str[i]; | |
| if ( c == '&' | |
| && i < ( (int)str.length() - 2 ) | |
| && str[i+1] == '#' | |
| && str[i+2] == 'x' ) | |
| { | |
| // Hexadecimal character reference. | |
| // Pass through unchanged. | |
| // © -- copyright symbol, for example. | |
| // | |
| // The -1 is a bug fix from Rob Laveaux. It keeps | |
| // an overflow from happening if there is no ';'. | |
| // There are actually 2 ways to exit this loop - | |
| // while fails (error case) and break (semicolon found). | |
| // However, there is no mechanism (currently) for | |
| // this function to return an error. | |
| while ( i<(int)str.length()-1 ) | |
| { | |
| outString->append( str.c_str() + i, 1 ); | |
| ++i; | |
| if ( str[i] == ';' ) | |
| break; | |
| } | |
| } | |
| else if ( c == '&' ) | |
| { | |
| outString->append( entity[0].str, entity[0].strLength ); | |
| ++i; | |
| } | |
| else if ( c == '<' ) | |
| { | |
| outString->append( entity[1].str, entity[1].strLength ); | |
| ++i; | |
| } | |
| else if ( c == '>' ) | |
| { | |
| outString->append( entity[2].str, entity[2].strLength ); | |
| ++i; | |
| } | |
| else if ( c == '\"' ) | |
| { | |
| outString->append( entity[3].str, entity[3].strLength ); | |
| ++i; | |
| } | |
| else if ( c == '\'' ) | |
| { | |
| outString->append( entity[4].str, entity[4].strLength ); | |
| ++i; | |
| } | |
| else if ( c < 32 ) | |
| { | |
| // Easy pass at non-alpha/numeric/symbol | |
| // Below 32 is symbolic. | |
| char buf[ 32 ]; | |
| #if defined(TIXML_SNPRINTF) | |
| TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); | |
| #else | |
| sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); | |
| #endif | |
| //*ME: warning C4267: convert 'size_t' to 'int' | |
| //*ME: Int-Cast to make compiler happy ... | |
| outString->append( buf, (int)strlen( buf ) ); | |
| ++i; | |
| } | |
| else | |
| { | |
| //char realc = (char) c; | |
| //outString->append( &realc, 1 ); | |
| *outString += (char) c; // somewhat more efficient function call. | |
| ++i; | |
| } | |
| } | |
| } | |
| // <-- Strange class for a bug fix. Search for STL_STRING_BUG | |
| TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str ) | |
| { | |
| buffer = new char[ str.length()+1 ]; | |
| if ( buffer ) | |
| { | |
| strcpy( buffer, str.c_str() ); | |
| } | |
| } | |
| TiXmlBase::StringToBuffer::~StringToBuffer() | |
| { | |
| delete [] buffer; | |
| } | |
| // End strange bug fix. --> | |
| TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() | |
| { | |
| parent = 0; | |
| type = _type; | |
| firstChild = 0; | |
| lastChild = 0; | |
| prev = 0; | |
| next = 0; | |
| } | |
| TiXmlNode::~TiXmlNode() | |
| { | |
| TiXmlNode* node = firstChild; | |
| TiXmlNode* temp = 0; | |
| while ( node ) | |
| { | |
| temp = node; | |
| node = node->next; | |
| delete temp; | |
| } | |
| } | |
| void TiXmlNode::CopyTo( TiXmlNode* target ) const | |
| { | |
| target->SetValue (value.c_str() ); | |
| target->userData = userData; | |
| } | |
| void TiXmlNode::Clear() | |
| { | |
| TiXmlNode* node = firstChild; | |
| TiXmlNode* temp = 0; | |
| while ( node ) | |
| { | |
| temp = node; | |
| node = node->next; | |
| delete temp; | |
| } | |
| firstChild = 0; | |
| lastChild = 0; | |
| } | |
| TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) | |
| { | |
| node->parent = this; | |
| node->prev = lastChild; | |
| node->next = 0; | |
| if ( lastChild ) | |
| lastChild->next = node; | |
| else | |
| firstChild = node; // it was an empty list. | |
| lastChild = node; | |
| return node; | |
| } | |
| TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) | |
| { | |
| TiXmlNode* node = addThis.Clone(); | |
| if ( !node ) | |
| return 0; | |
| return LinkEndChild( node ); | |
| } | |
| TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) | |
| { | |
| if ( !beforeThis || beforeThis->parent != this ) | |
| return 0; | |
| TiXmlNode* node = addThis.Clone(); | |
| if ( !node ) | |
| return 0; | |
| node->parent = this; | |
| node->next = beforeThis; | |
| node->prev = beforeThis->prev; | |
| if ( beforeThis->prev ) | |
| { | |
| beforeThis->prev->next = node; | |
| } | |
| else | |
| { | |
| assert( firstChild == beforeThis ); | |
| firstChild = node; | |
| } | |
| beforeThis->prev = node; | |
| return node; | |
| } | |
| TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) | |
| { | |
| if ( !afterThis || afterThis->parent != this ) | |
| return 0; | |
| TiXmlNode* node = addThis.Clone(); | |
| if ( !node ) | |
| return 0; | |
| node->parent = this; | |
| node->prev = afterThis; | |
| node->next = afterThis->next; | |
| if ( afterThis->next ) | |
| { | |
| afterThis->next->prev = node; | |
| } | |
| else | |
| { | |
| assert( lastChild == afterThis ); | |
| lastChild = node; | |
| } | |
| afterThis->next = node; | |
| return node; | |
| } | |
| TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) | |
| { | |
| if ( replaceThis->parent != this ) | |
| return 0; | |
| TiXmlNode* node = withThis.Clone(); | |
| if ( !node ) | |
| return 0; | |
| node->next = replaceThis->next; | |
| node->prev = replaceThis->prev; | |
| if ( replaceThis->next ) | |
| replaceThis->next->prev = node; | |
| else | |
| lastChild = node; | |
| if ( replaceThis->prev ) | |
| replaceThis->prev->next = node; | |
| else | |
| firstChild = node; | |
| delete replaceThis; | |
| node->parent = this; | |
| return node; | |
| } | |
| bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) | |
| { | |
| if ( removeThis->parent != this ) | |
| { | |
| assert( 0 ); | |
| return false; | |
| } | |
| if ( removeThis->next ) | |
| removeThis->next->prev = removeThis->prev; | |
| else | |
| lastChild = removeThis->prev; | |
| if ( removeThis->prev ) | |
| removeThis->prev->next = removeThis->next; | |
| else | |
| firstChild = removeThis->next; | |
| delete removeThis; | |
| return true; | |
| } | |
| const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = firstChild; node; node = node->next ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| TiXmlNode* TiXmlNode::FirstChild( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = firstChild; node; node = node->next ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = lastChild; node; node = node->prev ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| TiXmlNode* TiXmlNode::LastChild( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = lastChild; node; node = node->prev ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const | |
| { | |
| if ( !previous ) | |
| { | |
| return FirstChild(); | |
| } | |
| else | |
| { | |
| assert( previous->parent == this ); | |
| return previous->NextSibling(); | |
| } | |
| } | |
| TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) | |
| { | |
| if ( !previous ) | |
| { | |
| return FirstChild(); | |
| } | |
| else | |
| { | |
| assert( previous->parent == this ); | |
| return previous->NextSibling(); | |
| } | |
| } | |
| const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const | |
| { | |
| if ( !previous ) | |
| { | |
| return FirstChild( val ); | |
| } | |
| else | |
| { | |
| assert( previous->parent == this ); | |
| return previous->NextSibling( val ); | |
| } | |
| } | |
| TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) | |
| { | |
| if ( !previous ) | |
| { | |
| return FirstChild( val ); | |
| } | |
| else | |
| { | |
| assert( previous->parent == this ); | |
| return previous->NextSibling( val ); | |
| } | |
| } | |
| const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = next; node; node = node->next ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| TiXmlNode* TiXmlNode::NextSibling( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = next; node; node = node->next ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = prev; node; node = node->prev ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = prev; node; node = node->prev ) | |
| { | |
| if ( strcmp( node->Value(), _value ) == 0 ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| void TiXmlElement::RemoveAttribute( const char * name ) | |
| { | |
| TiXmlAttribute* node = attributeSet.Find( name ); | |
| if ( node ) | |
| { | |
| attributeSet.Remove( node ); | |
| delete node; | |
| } | |
| } | |
| const TiXmlElement* TiXmlNode::FirstChildElement() const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = FirstChild(); | |
| node; | |
| node = node->NextSibling() ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| TiXmlElement* TiXmlNode::FirstChildElement() | |
| { | |
| TiXmlNode* node; | |
| for ( node = FirstChild(); | |
| node; | |
| node = node->NextSibling() ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = FirstChild( _value ); | |
| node; | |
| node = node->NextSibling( _value ) ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = FirstChild( _value ); | |
| node; | |
| node = node->NextSibling( _value ) ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| const TiXmlElement* TiXmlNode::NextSiblingElement() const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = NextSibling(); | |
| node; | |
| node = node->NextSibling() ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| TiXmlElement* TiXmlNode::NextSiblingElement() | |
| { | |
| TiXmlNode* node; | |
| for ( node = NextSibling(); | |
| node; | |
| node = node->NextSibling() ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node = NextSibling( _value ); | |
| node; | |
| node = node->NextSibling( _value ) ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) | |
| { | |
| TiXmlNode* node; | |
| for ( node = NextSibling( _value ); | |
| node; | |
| node = node->NextSibling( _value ) ) | |
| { | |
| if ( node->ToElement() ) | |
| return node->ToElement(); | |
| } | |
| return 0; | |
| } | |
| const TiXmlDocument* TiXmlNode::GetDocument() const | |
| { | |
| const TiXmlNode* node; | |
| for( node = this; node; node = node->parent ) | |
| { | |
| if ( node->ToDocument() ) | |
| return node->ToDocument(); | |
| } | |
| return 0; | |
| } | |
| TiXmlDocument* TiXmlNode::GetDocument() | |
| { | |
| TiXmlNode* node; | |
| for( node = this; node; node = node->parent ) | |
| { | |
| if ( node->ToDocument() ) | |
| return node->ToDocument(); | |
| } | |
| return 0; | |
| } | |
| TiXmlElement::TiXmlElement (const char * _value) | |
| : TiXmlNode( TiXmlNode::ELEMENT ) | |
| { | |
| firstChild = lastChild = 0; | |
| value = _value; | |
| } | |
| #ifdef TIXML_USE_STL | |
| TiXmlElement::TiXmlElement( const std::string& _value ) | |
| : TiXmlNode( TiXmlNode::ELEMENT ) | |
| { | |
| firstChild = lastChild = 0; | |
| value = _value; | |
| } | |
| #endif | |
| TiXmlElement::TiXmlElement( const TiXmlElement& copy) | |
| : TiXmlNode( TiXmlNode::ELEMENT ) | |
| { | |
| firstChild = lastChild = 0; | |
| copy.CopyTo( this ); | |
| } | |
| void TiXmlElement::operator=( const TiXmlElement& base ) | |
| { | |
| ClearThis(); | |
| base.CopyTo( this ); | |
| } | |
| TiXmlElement::~TiXmlElement() | |
| { | |
| ClearThis(); | |
| } | |
| void TiXmlElement::ClearThis() | |
| { | |
| Clear(); | |
| while( attributeSet.First() ) | |
| { | |
| TiXmlAttribute* node = attributeSet.First(); | |
| attributeSet.Remove( node ); | |
| delete node; | |
| } | |
| } | |
| const char * TiXmlElement::Attribute( const char * name ) const | |
| { | |
| const TiXmlAttribute* node = attributeSet.Find( name ); | |
| if ( node ) | |
| return node->Value(); | |
| return 0; | |
| } | |
| const char * TiXmlElement::Attribute( const char * name, int* i ) const | |
| { | |
| const char * s = Attribute( name ); | |
| if ( i ) | |
| { | |
| if ( s ) | |
| *i = atoi( s ); | |
| else | |
| *i = 0; | |
| } | |
| return s; | |
| } | |
| const char * TiXmlElement::Attribute( const char * name, double* d ) const | |
| { | |
| const char * s = Attribute( name ); | |
| if ( d ) | |
| { | |
| if ( s ) | |
| *d = atof( s ); | |
| else | |
| *d = 0; | |
| } | |
| return s; | |
| } | |
| int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const | |
| { | |
| const TiXmlAttribute* node = attributeSet.Find( name ); | |
| if ( !node ) | |
| return TIXML_NO_ATTRIBUTE; | |
| return node->QueryIntValue( ival ); | |
| } | |
| int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const | |
| { | |
| const TiXmlAttribute* node = attributeSet.Find( name ); | |
| if ( !node ) | |
| return TIXML_NO_ATTRIBUTE; | |
| return node->QueryDoubleValue( dval ); | |
| } | |
| void TiXmlElement::SetAttribute( const char * name, int val ) | |
| { | |
| char buf[64]; | |
| #if defined(TIXML_SNPRINTF) | |
| TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); | |
| #else | |
| sprintf( buf, "%d", val ); | |
| #endif | |
| SetAttribute( name, buf ); | |
| } | |
| void TiXmlElement::SetDoubleAttribute( const char * name, double val ) | |
| { | |
| char buf[256]; | |
| #if defined(TIXML_SNPRINTF) | |
| TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); | |
| #else | |
| sprintf( buf, "%f", val ); | |
| #endif | |
| SetAttribute( name, buf ); | |
| } | |
| void TiXmlElement::SetAttribute( const char * name, const char * _value ) | |
| { | |
| TiXmlAttribute* node = attributeSet.Find( name ); | |
| if ( node ) | |
| { | |
| node->SetValue( _value ); | |
| return; | |
| } | |
| TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); | |
| if ( attrib ) | |
| { | |
| attributeSet.Add( attrib ); | |
| } | |
| else | |
| { | |
| TiXmlDocument* document = GetDocument(); | |
| if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); | |
| } | |
| } | |
| void TiXmlElement::Print( FILE* cfile, int depth ) const | |
| { | |
| int i; | |
| for ( i=0; i<depth; i++ ) | |
| { | |
| fprintf( cfile, " " ); | |
| } | |
| fprintf( cfile, "<%s", value.c_str() ); | |
| const TiXmlAttribute* attrib; | |
| for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) | |
| { | |
| fprintf( cfile, " " ); | |
| attrib->Print( cfile, depth ); | |
| } | |
| // There are 3 different formatting approaches: | |
| // 1) An element without children is printed as a <foo /> node | |
| // 2) An element with only a text child is printed as <foo> text </foo> | |
| // 3) An element with children is printed on multiple lines. | |
| TiXmlNode* node; | |
| if ( !firstChild ) | |
| { | |
| fprintf( cfile, " />" ); | |
| } | |
| else if ( firstChild == lastChild && firstChild->ToText() ) | |
| { | |
| fprintf( cfile, ">" ); | |
| firstChild->Print( cfile, depth + 1 ); | |
| fprintf( cfile, "</%s>", value.c_str() ); | |
| } | |
| else | |
| { | |
| fprintf( cfile, ">" ); | |
| for ( node = firstChild; node; node=node->NextSibling() ) | |
| { | |
| if ( !node->ToText() ) | |
| { | |
| fprintf( cfile, "\n" ); | |
| } | |
| node->Print( cfile, depth+1 ); | |
| } | |
| fprintf( cfile, "\n" ); | |
| for( i=0; i<depth; ++i ) | |
| fprintf( cfile, " " ); | |
| fprintf( cfile, "</%s>", value.c_str() ); | |
| } | |
| } | |
| void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| (*stream) << "<" << value; | |
| const TiXmlAttribute* attrib; | |
| for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) | |
| { | |
| (*stream) << " "; | |
| attrib->StreamOut( stream ); | |
| } | |
| // If this node has children, give it a closing tag. Else | |
| // make it an empty tag. | |
| TiXmlNode* node; | |
| if ( firstChild ) | |
| { | |
| (*stream) << ">"; | |
| for ( node = firstChild; node; node=node->NextSibling() ) | |
| { | |
| node->StreamOut( stream ); | |
| } | |
| (*stream) << "</" << value << ">"; | |
| } | |
| else | |
| { | |
| (*stream) << " />"; | |
| } | |
| } | |
| void TiXmlElement::CopyTo( TiXmlElement* target ) const | |
| { | |
| // superclass: | |
| TiXmlNode::CopyTo( target ); | |
| // Element class: | |
| // Clone the attributes, then clone the children. | |
| const TiXmlAttribute* attribute = 0; | |
| for( attribute = attributeSet.First(); | |
| attribute; | |
| attribute = attribute->Next() ) | |
| { | |
| target->SetAttribute( attribute->Name(), attribute->Value() ); | |
| } | |
| TiXmlNode* node = 0; | |
| for ( node = firstChild; node; node = node->NextSibling() ) | |
| { | |
| target->LinkEndChild( node->Clone() ); | |
| } | |
| } | |
| TiXmlNode* TiXmlElement::Clone() const | |
| { | |
| TiXmlElement* clone = new TiXmlElement( Value() ); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| const char* TiXmlElement::GetText() const | |
| { | |
| const TiXmlNode* child = this->FirstChild(); | |
| if ( child ) { | |
| const TiXmlText* childText = child->ToText(); | |
| if ( childText ) { | |
| return childText->Value(); | |
| } | |
| } | |
| return 0; | |
| } | |
| TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) | |
| { | |
| tabsize = 4; | |
| useMicrosoftBOM = false; | |
| ClearError(); | |
| } | |
| TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) | |
| { | |
| tabsize = 4; | |
| useMicrosoftBOM = false; | |
| value = documentName; | |
| ClearError(); | |
| } | |
| #ifdef TIXML_USE_STL | |
| TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) | |
| { | |
| tabsize = 4; | |
| useMicrosoftBOM = false; | |
| value = documentName; | |
| ClearError(); | |
| } | |
| #endif | |
| TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) | |
| { | |
| copy.CopyTo( this ); | |
| } | |
| void TiXmlDocument::operator=( const TiXmlDocument& copy ) | |
| { | |
| Clear(); | |
| copy.CopyTo( this ); | |
| } | |
| bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) | |
| { | |
| // See STL_STRING_BUG below. | |
| StringToBuffer buf( value ); | |
| if ( buf.buffer && LoadFile( buf.buffer, encoding ) ) | |
| return true; | |
| return false; | |
| } | |
| bool TiXmlDocument::SaveFile() const | |
| { | |
| // See STL_STRING_BUG below. | |
| StringToBuffer buf( value ); | |
| if ( buf.buffer && SaveFile( buf.buffer ) ) | |
| return true; | |
| return false; | |
| } | |
| bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding ) | |
| { | |
| // Delete the existing data: | |
| Clear(); | |
| location.Clear(); | |
| // There was a really terrifying little bug here. The code: | |
| // value = filename | |
| // in the STL case, cause the assignment method of the std::string to | |
| // be called. What is strange, is that the std::string had the same | |
| // address as it's c_str() method, and so bad things happen. Looks | |
| // like a bug in the Microsoft STL implementation. | |
| // See STL_STRING_BUG above. | |
| // Fixed with the StringToBuffer class. | |
| value = filename; | |
| // reading in binary mode so that tinyxml can normalize the EOL | |
| FILE* file = fopen( value.c_str (), "rb" ); | |
| if ( file ) | |
| { | |
| // Get the file size, so we can pre-allocate the string. HUGE speed impact. | |
| long length = 0; | |
| fseek( file, 0, SEEK_END ); | |
| length = ftell( file ); | |
| fseek( file, 0, SEEK_SET ); | |
| // Strange case, but good to handle up front. | |
| if ( length == 0 ) | |
| { | |
| fclose( file ); | |
| return false; | |
| } | |
| // If we have a file, assume it is all one big XML file, and read it in. | |
| // The document parser may decide the document ends sooner than the entire file, however. | |
| TIXML_STRING data; | |
| data.reserve( length ); | |
| // Subtle bug here. TinyXml did use fgets. But from the XML spec: | |
| // 2.11 End-of-Line Handling | |
| // <snip> | |
| // <quote> | |
| // ...the XML processor MUST behave as if it normalized all line breaks in external | |
| // parsed entities (including the document entity) on input, before parsing, by translating | |
| // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to | |
| // a single #xA character. | |
| // </quote> | |
| // | |
| // It is not clear fgets does that, and certainly isn't clear it works cross platform. | |
| // Generally, you expect fgets to translate from the convention of the OS to the c/unix | |
| // convention, and not work generally. | |
| /* | |
| while( fgets( buf, sizeof(buf), file ) ) | |
| { | |
| data += buf; | |
| } | |
| */ | |
| char* buf = new char[ length+1 ]; | |
| buf[0] = 0; | |
| if ( fread( buf, length, 1, file ) != 1 ) { | |
| //if ( fread( buf, 1, length, file ) != (size_t)length ) { | |
| SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); | |
| fclose( file ); | |
| return false; | |
| } | |
| fclose( file ); | |
| const char* lastPos = buf; | |
| const char* p = buf; | |
| buf[length] = 0; | |
| while( *p ) { | |
| assert( p < (buf+length) ); | |
| if ( *p == 0xa ) { | |
| // Newline character. No special rules for this. Append all the characters | |
| // since the last string, and include the newline. | |
| data.append( lastPos, p-lastPos+1 ); // append, include the newline | |
| ++p; // move past the newline | |
| lastPos = p; // and point to the new buffer (may be 0) | |
| assert( p <= (buf+length) ); | |
| } | |
| else if ( *p == 0xd ) { | |
| // Carriage return. Append what we have so far, then | |
| // handle moving forward in the buffer. | |
| if ( (p-lastPos) > 0 ) { | |
| data.append( lastPos, p-lastPos ); // do not add the CR | |
| } | |
| data += (char)0xa; // a proper newline | |
| if ( *(p+1) == 0xa ) { | |
| // Carriage return - new line sequence | |
| p += 2; | |
| lastPos = p; | |
| assert( p <= (buf+length) ); | |
| } | |
| else { | |
| // it was followed by something else...that is presumably characters again. | |
| ++p; | |
| lastPos = p; | |
| assert( p <= (buf+length) ); | |
| } | |
| } | |
| else { | |
| ++p; | |
| } | |
| } | |
| // Handle any left over characters. | |
| if ( p-lastPos ) { | |
| data.append( lastPos, p-lastPos ); | |
| } | |
| delete [] buf; | |
| buf = 0; | |
| Parse( data.c_str(), 0, encoding ); | |
| if ( Error() ) | |
| return false; | |
| else | |
| return true; | |
| } | |
| SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); | |
| return false; | |
| } | |
| bool TiXmlDocument::SaveFile( const char * filename ) const | |
| { | |
| // The old c stuff lives on... | |
| FILE* fp = fopen( filename, "w" ); | |
| if ( fp ) | |
| { | |
| if ( useMicrosoftBOM ) | |
| { | |
| const unsigned char TIXML_UTF_LEAD_0 = 0xefU; | |
| const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; | |
| const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; | |
| fputc( TIXML_UTF_LEAD_0, fp ); | |
| fputc( TIXML_UTF_LEAD_1, fp ); | |
| fputc( TIXML_UTF_LEAD_2, fp ); | |
| } | |
| Print( fp, 0 ); | |
| fclose( fp ); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void TiXmlDocument::CopyTo( TiXmlDocument* target ) const | |
| { | |
| TiXmlNode::CopyTo( target ); | |
| target->error = error; | |
| target->errorDesc = errorDesc.c_str (); | |
| TiXmlNode* node = 0; | |
| for ( node = firstChild; node; node = node->NextSibling() ) | |
| { | |
| target->LinkEndChild( node->Clone() ); | |
| } | |
| } | |
| TiXmlNode* TiXmlDocument::Clone() const | |
| { | |
| TiXmlDocument* clone = new TiXmlDocument(); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| void TiXmlDocument::Print( FILE* cfile, int depth ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node=FirstChild(); node; node=node->NextSibling() ) | |
| { | |
| node->Print( cfile, depth ); | |
| fprintf( cfile, "\n" ); | |
| } | |
| } | |
| void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const | |
| { | |
| const TiXmlNode* node; | |
| for ( node=FirstChild(); node; node=node->NextSibling() ) | |
| { | |
| node->StreamOut( out ); | |
| // Special rule for streams: stop after the root element. | |
| // The stream in code will only read one element, so don't | |
| // write more than one. | |
| if ( node->ToElement() ) | |
| break; | |
| } | |
| } | |
| const TiXmlAttribute* TiXmlAttribute::Next() const | |
| { | |
| // We are using knowledge of the sentinel. The sentinel | |
| // have a value or name. | |
| if ( next->value.empty() && next->name.empty() ) | |
| return 0; | |
| return next; | |
| } | |
| TiXmlAttribute* TiXmlAttribute::Next() | |
| { | |
| // We are using knowledge of the sentinel. The sentinel | |
| // have a value or name. | |
| if ( next->value.empty() && next->name.empty() ) | |
| return 0; | |
| return next; | |
| } | |
| const TiXmlAttribute* TiXmlAttribute::Previous() const | |
| { | |
| // We are using knowledge of the sentinel. The sentinel | |
| // have a value or name. | |
| if ( prev->value.empty() && prev->name.empty() ) | |
| return 0; | |
| return prev; | |
| } | |
| TiXmlAttribute* TiXmlAttribute::Previous() | |
| { | |
| // We are using knowledge of the sentinel. The sentinel | |
| // have a value or name. | |
| if ( prev->value.empty() && prev->name.empty() ) | |
| return 0; | |
| return prev; | |
| } | |
| void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const | |
| { | |
| TIXML_STRING n, v; | |
| PutString( name, &n ); | |
| PutString( value, &v ); | |
| if (value.find ('\"') == TIXML_STRING::npos) | |
| fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); | |
| else | |
| fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); | |
| } | |
| void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| if (value.find( '\"' ) != TIXML_STRING::npos) | |
| { | |
| PutString( name, stream ); | |
| (*stream) << "=" << "'"; | |
| PutString( value, stream ); | |
| (*stream) << "'"; | |
| } | |
| else | |
| { | |
| PutString( name, stream ); | |
| (*stream) << "=" << "\""; | |
| PutString( value, stream ); | |
| (*stream) << "\""; | |
| } | |
| } | |
| int TiXmlAttribute::QueryIntValue( int* ival ) const | |
| { | |
| if ( sscanf( value.c_str(), "%d", ival ) == 1 ) | |
| return TIXML_SUCCESS; | |
| return TIXML_WRONG_TYPE; | |
| } | |
| int TiXmlAttribute::QueryDoubleValue( double* dval ) const | |
| { | |
| if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) | |
| return TIXML_SUCCESS; | |
| return TIXML_WRONG_TYPE; | |
| } | |
| void TiXmlAttribute::SetIntValue( int _value ) | |
| { | |
| char buf [64]; | |
| #if defined(TIXML_SNPRINTF) | |
| TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); | |
| #else | |
| sprintf (buf, "%d", _value); | |
| #endif | |
| SetValue (buf); | |
| } | |
| void TiXmlAttribute::SetDoubleValue( double _value ) | |
| { | |
| char buf [256]; | |
| #if defined(TIXML_SNPRINTF) | |
| TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); | |
| #else | |
| sprintf (buf, "%lf", _value); | |
| #endif | |
| SetValue (buf); | |
| } | |
| int TiXmlAttribute::IntValue() const | |
| { | |
| return atoi (value.c_str ()); | |
| } | |
| double TiXmlAttribute::DoubleValue() const | |
| { | |
| return atof (value.c_str ()); | |
| } | |
| TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) | |
| { | |
| copy.CopyTo( this ); | |
| } | |
| void TiXmlComment::operator=( const TiXmlComment& base ) | |
| { | |
| Clear(); | |
| base.CopyTo( this ); | |
| } | |
| void TiXmlComment::Print( FILE* cfile, int depth ) const | |
| { | |
| for ( int i=0; i<depth; i++ ) | |
| { | |
| fputs( " ", cfile ); | |
| } | |
| fprintf( cfile, "<!--%s-->", value.c_str() ); | |
| } | |
| void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| (*stream) << "<!--"; | |
| //PutString( value, stream ); | |
| (*stream) << value; | |
| (*stream) << "-->"; | |
| } | |
| void TiXmlComment::CopyTo( TiXmlComment* target ) const | |
| { | |
| TiXmlNode::CopyTo( target ); | |
| } | |
| TiXmlNode* TiXmlComment::Clone() const | |
| { | |
| TiXmlComment* clone = new TiXmlComment(); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| void TiXmlText::Print( FILE* cfile, int depth ) const | |
| { | |
| if ( cdata ) | |
| { | |
| int i; | |
| fprintf( cfile, "\n" ); | |
| for ( i=0; i<depth; i++ ) { | |
| fprintf( cfile, " " ); | |
| } | |
| fprintf( cfile, "<![CDATA[\n" ); | |
| fprintf( cfile, "%s", value.c_str() ); // unformatted output | |
| fprintf( cfile, "\n" ); | |
| for ( i=0; i<depth; i++ ) { | |
| fprintf( cfile, " " ); | |
| } | |
| fprintf( cfile, "]]>\n" ); | |
| } | |
| else | |
| { | |
| TIXML_STRING buffer; | |
| PutString( value, &buffer ); | |
| fprintf( cfile, "%s", buffer.c_str() ); | |
| } | |
| } | |
| void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| if ( cdata ) | |
| { | |
| (*stream) << "<![CDATA[" << value << "]]>"; | |
| } | |
| else | |
| { | |
| PutString( value, stream ); | |
| } | |
| } | |
| void TiXmlText::CopyTo( TiXmlText* target ) const | |
| { | |
| TiXmlNode::CopyTo( target ); | |
| target->cdata = cdata; | |
| } | |
| TiXmlNode* TiXmlText::Clone() const | |
| { | |
| TiXmlText* clone = 0; | |
| clone = new TiXmlText( "" ); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| TiXmlDeclaration::TiXmlDeclaration( const char * _version, | |
| const char * _encoding, | |
| const char * _standalone ) | |
| : TiXmlNode( TiXmlNode::DECLARATION ) | |
| { | |
| version = _version; | |
| encoding = _encoding; | |
| standalone = _standalone; | |
| } | |
| #ifdef TIXML_USE_STL | |
| TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, | |
| const std::string& _encoding, | |
| const std::string& _standalone ) | |
| : TiXmlNode( TiXmlNode::DECLARATION ) | |
| { | |
| version = _version; | |
| encoding = _encoding; | |
| standalone = _standalone; | |
| } | |
| #endif | |
| TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) | |
| : TiXmlNode( TiXmlNode::DECLARATION ) | |
| { | |
| copy.CopyTo( this ); | |
| } | |
| void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) | |
| { | |
| Clear(); | |
| copy.CopyTo( this ); | |
| } | |
| void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const | |
| { | |
| fprintf (cfile, "<?xml "); | |
| if ( !version.empty() ) | |
| fprintf (cfile, "version=\"%s\" ", version.c_str ()); | |
| if ( !encoding.empty() ) | |
| fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); | |
| if ( !standalone.empty() ) | |
| fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); | |
| fprintf (cfile, "?>"); | |
| } | |
| void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| (*stream) << "<?xml "; | |
| if ( !version.empty() ) | |
| { | |
| (*stream) << "version=\""; | |
| PutString( version, stream ); | |
| (*stream) << "\" "; | |
| } | |
| if ( !encoding.empty() ) | |
| { | |
| (*stream) << "encoding=\""; | |
| PutString( encoding, stream ); | |
| (*stream ) << "\" "; | |
| } | |
| if ( !standalone.empty() ) | |
| { | |
| (*stream) << "standalone=\""; | |
| PutString( standalone, stream ); | |
| (*stream) << "\" "; | |
| } | |
| (*stream) << "?>"; | |
| } | |
| void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const | |
| { | |
| TiXmlNode::CopyTo( target ); | |
| target->version = version; | |
| target->encoding = encoding; | |
| target->standalone = standalone; | |
| } | |
| TiXmlNode* TiXmlDeclaration::Clone() const | |
| { | |
| TiXmlDeclaration* clone = new TiXmlDeclaration(); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| void TiXmlUnknown::Print( FILE* cfile, int depth ) const | |
| { | |
| for ( int i=0; i<depth; i++ ) | |
| fprintf( cfile, " " ); | |
| fprintf( cfile, "<%s>", value.c_str() ); | |
| } | |
| void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const | |
| { | |
| (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. | |
| } | |
| void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const | |
| { | |
| TiXmlNode::CopyTo( target ); | |
| } | |
| TiXmlNode* TiXmlUnknown::Clone() const | |
| { | |
| TiXmlUnknown* clone = new TiXmlUnknown(); | |
| if ( !clone ) | |
| return 0; | |
| CopyTo( clone ); | |
| return clone; | |
| } | |
| TiXmlAttributeSet::TiXmlAttributeSet() | |
| { | |
| sentinel.next = &sentinel; | |
| sentinel.prev = &sentinel; | |
| } | |
| TiXmlAttributeSet::~TiXmlAttributeSet() | |
| { | |
| assert( sentinel.next == &sentinel ); | |
| assert( sentinel.prev == &sentinel ); | |
| } | |
| void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) | |
| { | |
| assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. | |
| addMe->next = &sentinel; | |
| addMe->prev = sentinel.prev; | |
| sentinel.prev->next = addMe; | |
| sentinel.prev = addMe; | |
| } | |
| void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) | |
| { | |
| TiXmlAttribute* node; | |
| for( node = sentinel.next; node != &sentinel; node = node->next ) | |
| { | |
| if ( node == removeMe ) | |
| { | |
| node->prev->next = node->next; | |
| node->next->prev = node->prev; | |
| node->next = 0; | |
| node->prev = 0; | |
| return; | |
| } | |
| } | |
| assert( 0 ); // we tried to remove a non-linked attribute. | |
| } | |
| const TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const | |
| { | |
| const TiXmlAttribute* node; | |
| for( node = sentinel.next; node != &sentinel; node = node->next ) | |
| { | |
| if ( node->name == name ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) | |
| { | |
| TiXmlAttribute* node; | |
| for( node = sentinel.next; node != &sentinel; node = node->next ) | |
| { | |
| if ( node->name == name ) | |
| return node; | |
| } | |
| return 0; | |
| } | |
| #ifdef TIXML_USE_STL | |
| TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base) | |
| { | |
| TIXML_STRING tag; | |
| tag.reserve( 8 * 1000 ); | |
| base.StreamIn( &in, &tag ); | |
| base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); | |
| return in; | |
| } | |
| #endif | |
| TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base) | |
| { | |
| base.StreamOut (& out); | |
| return out; | |
| } | |
| #ifdef TIXML_USE_STL | |
| std::string & operator<< (std::string& out, const TiXmlNode& base ) | |
| { | |
| std::ostringstream os_stream( std::ostringstream::out ); | |
| base.StreamOut( &os_stream ); | |
| out.append( os_stream.str() ); | |
| return out; | |
| } | |
| #endif | |
| TiXmlHandle TiXmlHandle::FirstChild() const | |
| { | |
| if ( node ) | |
| { | |
| TiXmlNode* child = node->FirstChild(); | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const | |
| { | |
| if ( node ) | |
| { | |
| TiXmlNode* child = node->FirstChild( value ); | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::FirstChildElement() const | |
| { | |
| if ( node ) | |
| { | |
| TiXmlElement* child = node->FirstChildElement(); | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const | |
| { | |
| if ( node ) | |
| { | |
| TiXmlElement* child = node->FirstChildElement( value ); | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::Child( int count ) const | |
| { | |
| if ( node ) | |
| { | |
| int i; | |
| TiXmlNode* child = node->FirstChild(); | |
| for ( i=0; | |
| child && i<count; | |
| child = child->NextSibling(), ++i ) | |
| { | |
| // nothing | |
| } | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const | |
| { | |
| if ( node ) | |
| { | |
| int i; | |
| TiXmlNode* child = node->FirstChild( value ); | |
| for ( i=0; | |
| child && i<count; | |
| child = child->NextSibling( value ), ++i ) | |
| { | |
| // nothing | |
| } | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::ChildElement( int count ) const | |
| { | |
| if ( node ) | |
| { | |
| int i; | |
| TiXmlElement* child = node->FirstChildElement(); | |
| for ( i=0; | |
| child && i<count; | |
| child = child->NextSiblingElement(), ++i ) | |
| { | |
| // nothing | |
| } | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } | |
| TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const | |
| { | |
| if ( node ) | |
| { | |
| int i; | |
| TiXmlElement* child = node->FirstChildElement( value ); | |
| for ( i=0; | |
| child && i<count; | |
| child = child->NextSiblingElement( value ), ++i ) | |
| { | |
| // nothing | |
| } | |
| if ( child ) | |
| return TiXmlHandle( child ); | |
| } | |
| return TiXmlHandle( 0 ); | |
| } |