>From 6b56d25159de730794460026035b3837bebd81ed Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov@codeweavers.com>
Date: Fri, 27 Jan 2012 16:09:42 +0300
Subject: [PATCH 4/5] Support startDTD()/endDTD() in MXWriter

---
 dlls/msxml3/mxwriter.c        |   80 ++++++++++++++++++++++++++++++++------
 dlls/msxml3/tests/saxreader.c |   86 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+), 13 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index 3e08840..c94faaa 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -41,6 +41,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
 static const WCHAR emptyW[] = {0};
+static const WCHAR spaceW[] = {' '};
+static const WCHAR quotW[]  = {'\"'};
 
 typedef enum
 {
@@ -266,10 +268,10 @@ static void close_output_buffer(mxwriter *This)
 */
 static WCHAR *get_escaped_string(const WCHAR *str, int *len)
 {
-    static const WCHAR ltW[]   = {'&','l','t',';'};
-    static const WCHAR ampW[]  = {'&','a','m','p',';'};
-    static const WCHAR quotW[] = {'&','q','u','o','t',';'};
-    static const WCHAR gtW[]   = {'&','g','t',';'};
+    static const WCHAR ltW[]    = {'&','l','t',';'};
+    static const WCHAR ampW[]   = {'&','a','m','p',';'};
+    static const WCHAR equotW[] = {'&','q','u','o','t',';'};
+    static const WCHAR gtW[]    = {'&','g','t',';'};
 
     const int default_alloc = 100;
     const int grow_thresh = 10;
@@ -301,8 +303,8 @@ static WCHAR *get_escaped_string(const WCHAR *str, int *len)
             ptr += sizeof(ampW)/sizeof(WCHAR);
             break;
         case '"':
-            memcpy(ptr, quotW, sizeof(quotW));
-            ptr += sizeof(quotW)/sizeof(WCHAR);
+            memcpy(ptr, equotW, sizeof(equotW));
+            ptr += sizeof(equotW)/sizeof(WCHAR);
             break;
         case '>':
             memcpy(ptr, gtW, sizeof(gtW));
@@ -330,7 +332,6 @@ static void write_prolog_buffer(const mxwriter *This)
     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
     static const WCHAR noW[] = {'n','o','\"','?','>'};
-    static const WCHAR quotW[] = {'\"'};
     static const WCHAR crlfW[] = {'\r','\n'};
 
     /* version */
@@ -951,9 +952,7 @@ static HRESULT WINAPI SAXContentHandler_startElement(
 
         for (i = 0; i < length; i++)
         {
-            static const WCHAR spaceW[] = {' '};
             static const WCHAR eqqW[] = {'=','\"'};
-            static const WCHAR quotW[] = {'\"'};
             const WCHAR *str;
             WCHAR *escaped;
             INT len = 0;
@@ -1114,17 +1113,72 @@ static HRESULT WINAPI SAXLexicalHandler_startDTD(ISAXLexicalHandler *iface,
     const WCHAR *name, int name_len, const WCHAR *publicId, int publicId_len,
     const WCHAR *systemId, int systemId_len)
 {
+    static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '};
+    static const WCHAR openintW[] = {'[','\r','\n'};
+
     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-    FIXME("(%p)->(%s %s %s): stub\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
+
+    TRACE("(%p)->(%s %s %s)\n", This, debugstr_wn(name, name_len), debugstr_wn(publicId, publicId_len),
         debugstr_wn(systemId, systemId_len));
-    return E_NOTIMPL;
+
+    if (!name) return E_INVALIDARG;
+
+    write_output_buffer(This->buffer, doctypeW, sizeof(doctypeW)/sizeof(WCHAR));
+
+    if (*name)
+    {
+        write_output_buffer(This->buffer, name, name_len);
+        write_output_buffer(This->buffer, spaceW, 1);
+    }
+
+    if (publicId)
+    {
+        static const WCHAR publicW[] = {'P','U','B','L','I','C',' '};
+
+        write_output_buffer(This->buffer, publicW, sizeof(publicW)/sizeof(WCHAR));
+        write_output_buffer(This->buffer, quotW, 1);
+        write_output_buffer(This->buffer, publicId, publicId_len);
+        write_output_buffer(This->buffer, quotW, 1);
+
+        if (!systemId) return E_INVALIDARG;
+
+        if (*publicId)
+            write_output_buffer(This->buffer, spaceW, 1);
+
+        write_output_buffer(This->buffer, quotW, 1);
+        write_output_buffer(This->buffer, systemId, systemId_len);
+        write_output_buffer(This->buffer, quotW, 1);
+
+        if (*systemId)
+            write_output_buffer(This->buffer, spaceW, 1);
+    }
+    else if (systemId)
+    {
+        static const WCHAR systemW[] = {'S','Y','S','T','E','M',' '};
+
+        write_output_buffer(This->buffer, systemW, sizeof(systemW)/sizeof(WCHAR));
+        write_output_buffer(This->buffer, quotW, 1);
+        write_output_buffer(This->buffer, systemId, systemId_len);
+        write_output_buffer(This->buffer, quotW, 1);
+        if (*systemId)
+            write_output_buffer(This->buffer, spaceW, 1);
+    }
+
+    write_output_buffer(This->buffer, openintW, sizeof(openintW)/sizeof(WCHAR));
+
+    return S_OK;
 }
 
 static HRESULT WINAPI SAXLexicalHandler_endDTD(ISAXLexicalHandler *iface)
 {
     mxwriter *This = impl_from_ISAXLexicalHandler( iface );
-    FIXME("(%p): stub\n", This);
-    return E_NOTIMPL;
+    static const WCHAR closedtdW[] = {']','>','\r','\n'};
+
+    TRACE("(%p)\n", This);
+
+    write_output_buffer(This->buffer, closedtdW, sizeof(closedtdW)/sizeof(WCHAR));
+
+    return S_OK;
 }
 
 static HRESULT WINAPI SAXLexicalHandler_startEntity(ISAXLexicalHandler *iface, const WCHAR *name, int len)
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index 30ac57e..1f47062 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -2956,7 +2956,92 @@ static void test_mxwriter_cdata(void)
     ISAXLexicalHandler_Release(lexical);
     IMXWriter_Release(writer);
     free_bstrs();
+}
 
+static void test_mxwriter_dtd(void)
+{
+    static const WCHAR nameW[] = {'n','a','m','e'};
+    static const WCHAR pubW[] = {'p','u','b'};
+    static const WCHAR sysW[] = {'s','y','s'};
+    ISAXContentHandler *content;
+    ISAXLexicalHandler *lexical;
+    IMXWriter *writer;
+    VARIANT dest;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IMXWriter, (void**)&writer);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_startDocument(content);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
+    EXPECT_HR(hr, E_INVALIDARG);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
+    EXPECT_HR(hr, E_INVALIDARG);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, sizeof(sysW)/sizeof(WCHAR));
+    EXPECT_HR(hr, E_INVALIDARG);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
+    EXPECT_HR(hr, E_INVALIDARG);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0, NULL, 0);
+    EXPECT_HR(hr, S_OK);
+
+    V_VT(&dest) = VT_EMPTY;
+    hr = IMXWriter_get_output(writer, &dest);
+    EXPECT_HR(hr, S_OK);
+    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
+    ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
+    VariantClear(&dest);
+
+    /* system id is required if public is present */
+    hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
+    EXPECT_HR(hr, E_INVALIDARG);
+
+    hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR),
+        pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
+    EXPECT_HR(hr, S_OK);
+
+    V_VT(&dest) = VT_EMPTY;
+    hr = IMXWriter_get_output(writer, &dest);
+    EXPECT_HR(hr, S_OK);
+    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
+    ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
+        "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
+    VariantClear(&dest);
+
+    hr = ISAXLexicalHandler_endDTD(lexical);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXLexicalHandler_endDTD(lexical);
+    EXPECT_HR(hr, S_OK);
+
+    V_VT(&dest) = VT_EMPTY;
+    hr = IMXWriter_get_output(writer, &dest);
+    EXPECT_HR(hr, S_OK);
+    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
+    ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
+         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
+        V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
+    VariantClear(&dest);
+
+    ISAXContentHandler_Release(content);
+    ISAXLexicalHandler_Release(lexical);
+    IMXWriter_Release(writer);
+    free_bstrs();
 }
 
 START_TEST(saxreader)
@@ -2996,6 +3081,7 @@ START_TEST(saxreader)
         test_mxwriter_characters();
         test_mxwriter_comment();
         test_mxwriter_cdata();
+        test_mxwriter_dtd();
         test_mxwriter_properties();
         test_mxwriter_flush();
         test_mxwriter_stream();
-- 
1.5.6.5


