| /* |
| 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 ); |
| } |