]> git.saurik.com Git - wxWidgets.git/commitdiff
Fix attributes escaping when writing XML.
authorVáclav Slavík <vslavik@fastmail.fm>
Wed, 4 Aug 2010 14:57:30 +0000 (14:57 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Wed, 4 Aug 2010 14:57:30 +0000 (14:57 +0000)
wxXmlDocument didn't correctly escape some characters that the spec says
must be escaped. Behaves correctly now.

Fixes #12275.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65192 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

src/xml/xml.cpp
tests/xml/xmltest.cpp

index 6c3a9e5d40c13b683aa0d48c2e5f87ff9d25fd76..5507a4fd7746f6c0854542adc4873839105ad6be 100644 (file)
@@ -821,62 +821,69 @@ bool OutputString(wxOutputStream& stream,
     return stream.IsOk();
 }
 
     return stream.IsOk();
 }
 
-// flags for OutputStringEnt()
-enum
+enum EscapingMode
 {
 {
-    XML_ESCAPE_QUOTES = 1
+    Escape_Text,
+    Escape_Attribute
 };
 
 // Same as above, but create entities first.
 };
 
 // Same as above, but create entities first.
-// Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
-bool OutputStringEnt(wxOutputStream& stream,
-                     const wxString& str,
-                     wxMBConv *convMem,
-                     wxMBConv *convFile,
-                     int flags = 0)
-{
-    const size_t len = str.length();
-    size_t i,
-           last = 0;
-    for (i = 0; i < len; i++)
-    {
-        wxChar c = str.GetChar(i);
-        if (c == wxS('<') || c == wxS('>') ||
-            (c == wxS('&') && str.substr(i+1, 4) != wxS("amp;")) ||
-            ((flags & XML_ESCAPE_QUOTES) && c == wxS('"')))
-        {
-            if ( !OutputString(stream, str.substr(last, i - last),
-                               convMem, convFile) )
-                return false;
+// Translates '<' to "&lt;", '>' to "&gt;" and so on, according to the spec:
+// http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
+bool OutputEscapedString(wxOutputStream& stream,
+                         const wxString& str,
+                         wxMBConv *convMem,
+                         wxMBConv *convFile,
+                         EscapingMode mode)
+{
+    wxString escaped;
+    escaped.reserve(str.length());
 
 
-            const char *escaped;
-            switch ( c )
-            {
-                case wxS('<'):
-                    escaped = "&lt;";
-                    break;
-                case wxS('>'):
-                    escaped = "&gt;";
-                    break;
-                case wxS('&'):
-                    escaped = "&amp;";
-                    break;
-                case wxS('"'):
-                    escaped = "&quot;";
-                    break;
-                default:
-                    wxFAIL_MSG( "logic error in the code" );
-                    return false;
-            }
+    for ( wxString::const_iterator i = str.begin(); i != str.end(); ++i )
+    {
+        const wxChar c = *i;
 
 
-            if ( !OutputString(stream, escaped, convMem, convFile) )
-                return false;
+        switch ( c )
+        {
+            case wxS('<'):
+                escaped.append(wxS("&lt;"));
+                break;
+            case wxS('>'):
+                escaped.append(wxS("&gt;"));
+                break;
+            case wxS('&'):
+                escaped.append(wxS("&amp;"));
+                break;
+            case wxS('\r'):
+                escaped.append(wxS("&#xD;"));
+                break;
+            default:
+                if ( mode == Escape_Attribute )
+                {
+                    switch ( c )
+                    {
+                        case wxS('"'):
+                            escaped.append(wxS("&quot;"));
+                            break;
+                        case wxS('\t'):
+                            escaped.append(wxS("&#x9;"));
+                            break;
+                        case wxS('\n'):
+                            escaped.append(wxS("&#xA;"));
+                            break;
+                        default:
+                            escaped.append(c);
+                    }
 
 
-            last = i + 1;
+                }
+                else
+                {
+                    escaped.append(c);
+                }
         }
     }
 
         }
     }
 
-    return OutputString(stream, str.substr(last, i - last), convMem, convFile);
+    return OutputString(stream, escaped, convMem, convFile);
 }
 
 bool OutputIndentation(wxOutputStream& stream,
 }
 
 bool OutputIndentation(wxOutputStream& stream,
@@ -906,7 +913,9 @@ bool OutputNode(wxOutputStream& stream,
             break;
 
         case wxXML_TEXT_NODE:
             break;
 
         case wxXML_TEXT_NODE:
-            rc = OutputStringEnt(stream, node->GetContent(), convMem, convFile);
+            rc = OutputEscapedString(stream, node->GetContent(),
+                                     convMem, convFile,
+                                     Escape_Text);
             break;
 
         case wxXML_ELEMENT_NODE:
             break;
 
         case wxXML_ELEMENT_NODE:
@@ -922,9 +931,9 @@ bool OutputNode(wxOutputStream& stream,
                     rc = OutputString(stream,
                                       wxS(" ") + attr->GetName() +  wxS("=\""),
                                       convMem, convFile) &&
                     rc = OutputString(stream,
                                       wxS(" ") + attr->GetName() +  wxS("=\""),
                                       convMem, convFile) &&
-                         OutputStringEnt(stream, attr->GetValue(),
-                                         convMem, convFile,
-                                         XML_ESCAPE_QUOTES) &&
+                         OutputEscapedString(stream, attr->GetValue(),
+                                             convMem, convFile,
+                                             Escape_Attribute) &&
                          OutputString(stream, wxS("\""), convMem, convFile);
                 }
             }
                          OutputString(stream, wxS("\""), convMem, convFile);
                 }
             }
index 9f3d6056a51c9d1a8afac2493f7801a22142d5f2..79ca9ee3c64a29cbfd237a03ba4e0a96f7da9626 100644 (file)
@@ -76,12 +76,14 @@ private:
         CPPUNIT_TEST( InsertChildAfter );
         CPPUNIT_TEST( LoadSave );
         CPPUNIT_TEST( CDATA );
         CPPUNIT_TEST( InsertChildAfter );
         CPPUNIT_TEST( LoadSave );
         CPPUNIT_TEST( CDATA );
+        CPPUNIT_TEST( Escaping );
     CPPUNIT_TEST_SUITE_END();
 
     void InsertChild();
     void InsertChildAfter();
     void LoadSave();
     void CDATA();
     CPPUNIT_TEST_SUITE_END();
 
     void InsertChild();
     void InsertChildAfter();
     void LoadSave();
     void CDATA();
+    void Escaping();
 
     DECLARE_NO_COPY_CLASS(XmlTestCase)
 };
 
     DECLARE_NO_COPY_CLASS(XmlTestCase)
 };
@@ -215,3 +217,26 @@ void XmlTestCase::CDATA()
     // is not
     CPPUNIT_ASSERT_EQUAL( "Giovanni Mittone", n->GetContent() );
 }
     // is not
     CPPUNIT_ASSERT_EQUAL( "Giovanni Mittone", n->GetContent() );
 }
+
+void XmlTestCase::Escaping()
+{
+    // Verify that attribute values are escaped correctly, see
+    // http://trac.wxwidgets.org/ticket/12275
+
+    const char *xmlText =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<root text=\"hello&#xD;&#xA;this is a new line\">\n"
+"  <x/>\n"
+"</root>\n"
+    ;
+
+    wxStringInputStream sis(xmlText);
+
+    wxXmlDocument doc;
+    CPPUNIT_ASSERT( doc.Load(sis) );
+
+    wxStringOutputStream sos;
+    CPPUNIT_ASSERT( doc.Save(sos) );
+
+    CPPUNIT_ASSERT_EQUAL( xmlText, sos.GetString() );
+}