diff --git a/tinyxml2.cpp b/tinyxml2.cpp index 8a4fa95..1d68cb0 100644 --- a/tinyxml2.cpp +++ b/tinyxml2.cpp @@ -592,8 +592,11 @@ char* XMLNode::ParseDeep( char* p ) p = document->Identify( p, &node ); if ( p && node ) { p = node->ParseDeep( p ); - // FIXME: is it the correct closing element? + if ( node->IsClosingElement() ) { + if ( !XMLUtil::StringEqual( Value(), node->Value() )) { + document->SetError( ERROR_MISMATCHED_ELEMENT, Value(), 0 ); + } DELETE_NODE( node ); return p; } @@ -953,7 +956,7 @@ char* XMLElement::ParseAttributes( char* p, bool* closedElement ) attrib->memPool = &document->attributePool; p = attrib->ParseDeep( p ); - if ( !p ) { + if ( !p || Attribute( attrib->Name() ) ) { DELETE_ATTRIBUTE( attrib ); document->SetError( ERROR_PARSING_ATTRIBUTE, start, p ); return 0; @@ -1134,7 +1137,8 @@ int XMLDocument::LoadFile( FILE* fp ) p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &writeBOM ); if ( !p || !*p ) { - return 0; // correctly parse an empty string? + SetError( ERROR_EMPTY_DOCUMENT, 0, 0 ); + return errorID; } ParseDeep( charBuffer + (p-charBuffer) ); @@ -1157,12 +1161,14 @@ int XMLDocument::Parse( const char* p ) InitDocument(); if ( !p || !*p ) { - return true; // correctly parse an empty string? + SetError( ERROR_EMPTY_DOCUMENT, 0, 0 ); + return errorID; } p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &writeBOM ); if ( !p || !*p ) { - return true; // correctly parse an empty string? + SetError( ERROR_EMPTY_DOCUMENT, 0, 0 ); + return errorID; } size_t len = strlen( p ); @@ -1180,9 +1186,6 @@ void XMLDocument::Print( XMLStreamer* streamer ) XMLStreamer stdStreamer( stdout ); if ( !streamer ) streamer = &stdStreamer; - //for( XMLNode* node = firstChild; node; node=node->next ) { - // node->Print( streamer ); - //} Accept( streamer ); } @@ -1477,7 +1480,7 @@ bool XMLStreamer::VisitExit( const XMLElement& element ) bool XMLStreamer::Visit( const XMLText& text ) { - PushText( text.Value() ); + PushText( text.Value(), text.CData() ); return true; } diff --git a/tinyxml2.h b/tinyxml2.h index a1f7603..7185472 100644 --- a/tinyxml2.h +++ b/tinyxml2.h @@ -375,8 +375,8 @@ public: static const char* ReadBOM( const char* p, bool* hasBOM ); // p is the starting location, // the UTF-8 value of the entity will be placed in value, and length filled in. - static const char* GetCharacterRef( const char* p, char* value, int* length ); - static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + static const char* GetCharacterRef( const char* p, char* value, int* length ); + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); }; @@ -587,7 +587,9 @@ enum { ERROR_PARSING_CDATA, ERROR_PARSING_COMMENT, ERROR_PARSING_DECLARATION, - ERROR_PARSING_UNKNOWN + ERROR_PARSING_UNKNOWN, + ERROR_EMPTY_DOCUMENT, + ERROR_MISMATCHED_ELEMENT }; @@ -714,6 +716,8 @@ public: void SaveFile( const char* filename ); bool HasBOM() const { return writeBOM; } + XMLElement* RootElement() { return FirstChildElement(); } + const XMLElement* RootElement() const { return FirstChildElement(); } void Print( XMLStreamer* streamer=0 ); virtual bool Accept( XMLVisitor* visitor ) const; diff --git a/xmltest.cpp b/xmltest.cpp index 95f9738..d244efa 100644 --- a/xmltest.cpp +++ b/xmltest.cpp @@ -103,21 +103,7 @@ int main( int argc, const char* argv ) #if defined( WIN32 ) _CrtMemCheckpoint( &startMemState ); #endif -#ifndef DREAM_ONLY -#if 0 - { - static const char* test = ""; - XMLDocument doc; - doc.Parse( test ); - doc.Print(); - } -#endif -#if 0 { static const char* test[] = { "", "", @@ -143,8 +129,19 @@ int main( int argc, const char* argv ) printf( "----------------------------------------------\n" ); } } -#endif -#if 0 + + { + static const char* test = ""; + + XMLDocument doc; + doc.Parse( test ); + doc.Print(); + } + { static const char* test = "Text before."; XMLDocument doc; @@ -160,9 +157,7 @@ int main( int argc, const char* argv ) doc->Parse( test ); delete doc; } -#endif { -#if 0 // Test: Programmatic DOM // Build: // @@ -211,11 +206,8 @@ int main( int argc, const char* argv ) printf( "%s", streamer.CStr() ); delete doc; -#endif } -#endif { -#if 0 // Test: Dream // XML1 : 1,187,569 bytes in 31,209 allocations // XML2 : 469,073 bytes in 323 allocations @@ -246,11 +238,8 @@ int main( int argc, const char* argv ) XMLTest( "Dream-out", "And Robin shall restore amends.", doc2.LastChild()->LastChild()->LastChild()->LastChild()->LastChildElement()->GetText() ); -#endif //gNewTotal = gNew - newStart; } - -#if 0 { const char* error = "\n" "\n" @@ -311,7 +300,6 @@ int main( int argc, const char* argv ) XMLTest( "Attribute round trip. double.", -1, (int)dVal ); } -#endif { XMLDocument doc; doc.LoadFile( "utf8test.xml" ); @@ -370,8 +358,284 @@ int main( int argc, const char* argv ) XMLTest( "UTF-8: Verified multi-language round trip.", 1, okay ); } + // --------GetText()----------- + { + const char* str = "This is text"; + XMLDocument doc; + doc.Parse( str ); + const XMLElement* element = doc.RootElement(); + + XMLTest( "GetText() normal use.", "This is text", element->GetText() ); + + str = "This is text"; + doc.Parse( str ); + element = doc.RootElement(); + + XMLTest( "GetText() contained element.", element->GetText() == 0, true ); + } -#if defined( WIN32 ) + + // ---------- CDATA --------------- + { + const char* str = "" + " the rules!\n" + "...since I make symbolic puns" + "]]>" + ""; + XMLDocument doc; + doc.Parse( str ); + doc.Print(); + + XMLTest( "CDATA parse.", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + false ); + } + + // ----------- CDATA ------------- + { + const char* str = "" + "I am > the rules!\n" + "...since I make symbolic puns" + "]]>" + ""; + XMLDocument doc; + doc.Parse( str ); + doc.Print(); + + XMLTest( "CDATA parse. [ tixml1:1480107 ]", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + false ); + } + + // InsertAfterChild causes crash. + { + // InsertBeforeChild and InsertAfterChild causes crash. + XMLDocument doc; + XMLElement* parent = doc.NewElement( "Parent" ); + doc.InsertFirstChild( parent ); + + XMLElement* childText0 = doc.NewElement( "childText0" ); + XMLElement* childText1 = doc.NewElement( "childText1" ); + + XMLNode* childNode0 = parent->InsertEndChild( childText0 ); + XMLNode* childNode1 = parent->InsertAfterChild( childNode0, childText1 ); + + XMLTest( "Test InsertAfterChild on empty node. ", ( childNode1 == parent->LastChild() ), true ); + } + + { + // Entities not being written correctly. + // From Lynn Allen + + const char* passages = + "" + "" + " " + ""; + + XMLDocument doc; + doc.Parse( passages ); + XMLElement* psg = doc.RootElement()->FirstChildElement(); + const char* context = psg->Attribute( "context" ); + const char* expected = "Line 5 has \"quotation marks\" and 'apostrophe marks'. It also has <, >, and &, as well as a fake copyright \xC2\xA9."; + + XMLTest( "Entity transformation: read. ", expected, context, true ); + + FILE* textfile = fopen( "textfile.txt", "w" ); + if ( textfile ) + { + XMLStreamer streamer( textfile ); + psg->Accept( &streamer ); + fclose( textfile ); + } + textfile = fopen( "textfile.txt", "r" ); + TIXMLASSERT( textfile ); + if ( textfile ) + { + char buf[ 1024 ]; + fgets( buf, 1024, textfile ); + XMLTest( "Entity transformation: write. ", + "\n", + buf, false ); + } + fclose( textfile ); + } + + { + const char* test = ""; + + XMLDocument doc; + doc.Parse( test ); + XMLTest( "dot in names", doc.Error(), 0); + XMLTest( "dot in names", doc.FirstChildElement()->Name(), "a.elem" ); + XMLTest( "dot in names", doc.FirstChildElement()->Attribute( "xmi.version" ), "2.0" ); + } + + { + const char* test = "1.1 Start easy ignore fin thickness "; + + XMLDocument doc; + doc.Parse( test ); + + XMLText* text = doc.FirstChildElement()->FirstChildElement()->FirstChild()->ToText(); + XMLTest( "Entity with one digit.", + text->Value(), "1.1 Start easy ignore fin thickness\n", + false ); + } + + { + // DOCTYPE not preserved (950171) + // + const char* doctype = + "" + "" + "" + "" + ""; + + XMLDocument doc; + doc.Parse( doctype ); + doc.SaveFile( "test7.xml" ); + doc.DeleteChild( doc.RootElement() ); + doc.LoadFile( "test7.xml" ); + doc.Print(); + + const XMLUnknown* decl = doc.FirstChild()->NextSibling()->ToUnknown(); + XMLTest( "Correct value of unknown.", "DOCTYPE PLAY SYSTEM 'play.dtd'", decl->Value() ); + + } + + { + // Comments do not stream out correctly. + const char* doctype = + ""; + XMLDocument doc; + doc.Parse( doctype ); + + XMLComment* comment = doc.FirstChild()->ToComment(); + + XMLTest( "Comment formatting.", " Somewhat ", comment->Value() ); + } + { + // Double attributes + const char* doctype = ""; + + XMLDocument doc; + doc.Parse( doctype ); + + XMLTest( "Parsing repeated attributes.", ERROR_PARSING_ATTRIBUTE, doc.ErrorID() ); // is an error to tinyxml (didn't use to be, but caused issues) + } + + { + // Embedded null in stream. + const char* doctype = ""; + + XMLDocument doc; + doc.Parse( doctype ); + XMLTest( "Embedded null throws error.", true, doc.Error() ); + } + + { + // Empty documents should return TIXML_ERROR_PARSING_EMPTY, bug 1070717 + const char* str = " "; + XMLDocument doc; + doc.Parse( str ); + XMLTest( "Empty document error", ERROR_EMPTY_DOCUMENT, doc.ErrorID() ); + } + + { + // Low entities + XMLDocument doc; + doc.Parse( "" ); + const char result[] = { 0x0e, 0 }; + XMLTest( "Low entities.", doc.FirstChildElement()->GetText(), result ); + doc.Print(); + } + + { + // Attribute values with trailing quotes not handled correctly + XMLDocument doc; + doc.Parse( "" ); + XMLTest( "Throw error with bad end quotes.", doc.Error(), true ); + } + + { + // [ 1663758 ] Failure to report error on bad XML + XMLDocument xml; + xml.Parse(""); + XMLTest("Missing end tag at end of input", xml.Error(), true); + xml.Parse(" "); + XMLTest("Missing end tag with trailing whitespace", xml.Error(), true); + xml.Parse(""); + XMLTest("Mismatched tags", xml.ErrorID(), ERROR_MISMATCHED_ELEMENT); + } + + + { + // [ 1475201 ] TinyXML parses entities in comments + XMLDocument xml; + xml.Parse("" + "" ); + + XMLNode* e0 = xml.FirstChild(); + XMLNode* e1 = e0->NextSibling(); + XMLComment* c0 = e0->ToComment(); + XMLComment* c1 = e1->ToComment(); + + XMLTest( "Comments ignore entities.", " declarations for & ", c0->Value(), true ); + XMLTest( "Comments ignore entities.", " far & away ", c1->Value(), true ); + } + + { + XMLDocument xml; + xml.Parse( "" + "" + "" + "" + "" ); + int count = 0; + + for( XMLNode* ele = xml.FirstChildElement( "Parent" )->FirstChild(); + ele; + ele = ele->NextSibling() ) + { + ++count; + } + + XMLTest( "Comments iterate correctly.", 3, count ); + } + + { + // trying to repro ]1874301]. If it doesn't go into an infinite loop, all is well. + unsigned char buf[] = " " ); + XMLTest( "Handle end tag whitespace", false, xml.Error() ); + } + + { + // This one must not result in an infinite loop + XMLDocument xml; + xml.Parse( "loop" ); + XMLTest( "Infinite loop test.", true, true ); + } + + #if defined( WIN32 ) _CrtMemCheckpoint( &endMemState ); //_CrtMemDumpStatistics( &endMemState );