From 4c779c07bdfaf67ab8dcd63e936167d1cc574740 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Fri, 2 Nov 2018 10:56:57 +0300 Subject: [PATCH] xmllite/writer: Implement WriteDocType(). Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/xmllite/reader.c | 8 +-- dlls/xmllite/tests/writer.c | 110 +++++++++++++++++++++++++++++++- dlls/xmllite/writer.c | 113 +++++++++++++++++++++++++++++++-- dlls/xmllite/xmllite_private.h | 3 + 4 files changed, 223 insertions(+), 11 deletions(-) diff --git a/dlls/xmllite/reader.c b/dlls/xmllite/reader.c index 97c269cedae..a6d1f3edcef 100644 --- a/dlls/xmllite/reader.c +++ b/dlls/xmllite/reader.c @@ -95,7 +95,7 @@ static const WCHAR gtW[] = {'>',0}; static const WCHAR commentW[] = {'<','!','-','-',0}; static const WCHAR piW[] = {'<','?',0}; -static BOOL is_namestartchar(WCHAR ch); +BOOL is_namestartchar(WCHAR ch); static const char *debugstr_nodetype(XmlNodeType nodetype) { @@ -1491,7 +1491,7 @@ static inline BOOL is_char(WCHAR ch) } /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */ -static inline BOOL is_pubchar(WCHAR ch) +BOOL is_pubchar(WCHAR ch) { return (ch == ' ') || (ch >= 'a' && ch <= 'z') || @@ -1504,7 +1504,7 @@ static inline BOOL is_pubchar(WCHAR ch) (ch == '_') || (ch == '\r') || (ch == '\n'); } -static inline BOOL is_namestartchar(WCHAR ch) +BOOL is_namestartchar(WCHAR ch) { return (ch == ':') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch >= 'a' && ch <= 'z') || @@ -1548,7 +1548,7 @@ BOOL is_ncnamechar(WCHAR ch) (ch >= 0xfdf0 && ch <= 0xfffd); } -static inline BOOL is_namechar(WCHAR ch) +BOOL is_namechar(WCHAR ch) { return (ch == ':') || is_ncnamechar(ch); } diff --git a/dlls/xmllite/tests/writer.c b/dlls/xmllite/tests/writer.c index 5ddb1ef2a74..99ad168c53d 100644 --- a/dlls/xmllite/tests/writer.c +++ b/dlls/xmllite/tests/writer.c @@ -135,7 +135,8 @@ static void check_writer_state(IXmlWriter *writer, HRESULT exp_hr) hr = IXmlWriter_WriteComment(writer, aW); ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr); - /* FIXME: add WriteDocType */ + hr = IXmlWriter_WriteDocType(writer, aW, NULL, NULL, NULL); + ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr); hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, aW); ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr); @@ -353,7 +354,8 @@ static void test_invalid_output_encoding(IXmlWriter *writer, IUnknown *output) hr = IXmlWriter_WriteComment(writer, aW); ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr); - /* TODO: WriteDocType */ + hr = IXmlWriter_WriteDocType(writer, aW, NULL, NULL, NULL); + ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr); hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, NULL); ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr); @@ -2113,6 +2115,109 @@ static void test_WriteString(void) IStream_Release(stream); } +static HRESULT write_doctype(IXmlWriter *writer, const char *name, const char *pubid, const char *sysid, + const char *subset) +{ + WCHAR *nameW, *pubidW, *sysidW, *subsetW; + HRESULT hr; + + nameW = strdupAtoW(name); + pubidW = strdupAtoW(pubid); + sysidW = strdupAtoW(sysid); + subsetW = strdupAtoW(subset); + + hr = IXmlWriter_WriteDocType(writer, nameW, pubidW, sysidW, subsetW); + + heap_free(nameW); + heap_free(pubidW); + heap_free(sysidW); + heap_free(subsetW); + + return hr; +} + +static void test_WriteDocType(void) +{ + static const struct + { + const char *name; + const char *pubid; + const char *sysid; + const char *subset; + const char *output; + } + doctype_tests[] = + { + { "a", "", NULL, NULL, "" }, + { "a", NULL, NULL, NULL, "" }, + { "a", NULL, "", NULL, "" }, + { "a", "", "", NULL, "" }, + { "a", "pubid", "", NULL, "" }, + { "a", "pubid", NULL, NULL, "" }, + { "a", "", "sysid", NULL, "" }, + { "a", NULL, NULL, "", "" }, + { "a", NULL, NULL, "subset", "" }, + { "a", "", NULL, "subset", "" }, + { "a", NULL, "", "subset", "" }, + { "a", "", "", "subset", "" }, + { "a", "pubid", NULL, "subset", "" }, + { "a", "pubid", "", "subset", "" }, + { "a", NULL, "sysid", "subset", "" }, + { "a", "", "sysid", "subset", "" }, + { "a", "pubid", "sysid", "subset", "" }, + }; + static const WCHAR pubidW[] = {'p',0x100,'i','d',0}; + static const WCHAR nameW[] = {'-','a',0}; + static const WCHAR emptyW[] = { 0 }; + IXmlWriter *writer; + IStream *stream; + unsigned int i; + HRESULT hr; + + hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&writer, NULL); + ok(hr == S_OK, "Failed to create writer instance, hr %#x.\n", hr); + + stream = writer_set_output(writer); + + hr = IXmlWriter_WriteDocType(writer, NULL, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IXmlWriter_WriteDocType(writer, emptyW, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + /* Name validation. */ + hr = IXmlWriter_WriteDocType(writer, nameW, NULL, NULL, NULL); + ok(hr == WC_E_NAMECHARACTER, "Unexpected hr %#x.\n", hr); + + /* Pubid validation. */ + hr = IXmlWriter_WriteDocType(writer, aW, pubidW, NULL, NULL); + ok(hr == WC_E_PUBLICID, "Unexpected hr %#x.\n", hr); + + IStream_Release(stream); + + for (i = 0; i < ARRAY_SIZE(doctype_tests); i++) + { + stream = writer_set_output(writer); + + hr = write_doctype(writer, doctype_tests[i].name, doctype_tests[i].pubid, doctype_tests[i].sysid, + doctype_tests[i].subset); + ok(hr == S_OK, "%u: failed to write doctype, hr %#x.\n", i, hr); + + hr = IXmlWriter_Flush(writer); + ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr); + + CHECK_OUTPUT(stream, doctype_tests[i].output); + + hr = write_doctype(writer, doctype_tests[i].name, doctype_tests[i].pubid, doctype_tests[i].sysid, + doctype_tests[i].subset); + ok(hr == WR_E_INVALIDACTION, "Unexpected hr %#x.\n", hr); + + IStream_Release(stream); + } + + IXmlWriter_Release(writer); +} + START_TEST(writer) { test_writer_create(); @@ -2134,4 +2239,5 @@ START_TEST(writer) test_WriteFullEndElement(); test_WriteCharEntity(); test_WriteString(); + test_WriteDocType(); } diff --git a/dlls/xmllite/writer.c b/dlls/xmllite/writer.c index 8d1fb3a2da3..e1fc232d4fd 100644 --- a/dlls/xmllite/writer.c +++ b/dlls/xmllite/writer.c @@ -366,6 +366,50 @@ static HRESULT is_valid_ncname(const WCHAR *str, int *out) return S_OK; } +static HRESULT is_valid_name(const WCHAR *str, unsigned int *out) +{ + unsigned int len = 1; + + *out = 0; + + if (!str || !*str) + return S_OK; + + if (!is_namestartchar(*str++)) + return WC_E_NAMECHARACTER; + + while (*str++) + { + if (!is_namechar(*str)) + return WC_E_NAMECHARACTER; + len++; + } + + *out = len; + return S_OK; +} + +static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out) +{ + unsigned int len = 0; + + *out = 0; + + if (!str || !*str) + return S_OK; + + while (*str) + { + if (!is_pubchar(*str++)) + return WC_E_PUBLICID; + len++; + } + + *out = len; + + return S_OK; +} + static HRESULT init_output_buffer(xmlwriteroutput *output) { struct output_buffer *buffer = &output->buffer; @@ -457,6 +501,11 @@ static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR * return S_OK; } +static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch) +{ + return write_output_buffer(output, &ch, 1); +} + /* TODO: test if we need to validate char range */ static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len, const WCHAR *local_name, int local_len) @@ -1103,15 +1152,69 @@ static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment) return S_OK; } -static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId, - LPCWSTR pwszSystemId, LPCWSTR pwszSubset) +static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid, + LPCWSTR sysid, LPCWSTR subset) { + static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',' '}; + static const WCHAR publicW[] = {' ','P','U','B','L','I','C',' '}; + static const WCHAR systemW[] = {' ','S','Y','S','T','E','M',' '}; xmlwriter *This = impl_from_IXmlWriter(iface); + unsigned int name_len, pubid_len; + HRESULT hr; - FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId), - wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset)); + TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid), + wine_dbgstr_w(subset)); - return E_NOTIMPL; + switch (This->state) + { + case XmlWriterState_Initial: + return E_UNEXPECTED; + case XmlWriterState_InvalidEncoding: + return MX_E_ENCODING; + case XmlWriterState_Content: + case XmlWriterState_DocClosed: + return WR_E_INVALIDACTION; + default: + ; + } + + if (is_empty_string(name)) + return E_INVALIDARG; + + if (FAILED(hr = is_valid_name(name, &name_len))) + return hr; + + if (FAILED(hr = is_valid_pubid(pubid, &pubid_len))) + return hr; + + write_output_buffer(This->output, doctypeW, ARRAY_SIZE(doctypeW)); + write_output_buffer(This->output, name, name_len); + + if (pubid) + { + write_output_buffer(This->output, publicW, ARRAY_SIZE(publicW)); + write_output_buffer_quoted(This->output, pubid, pubid_len); + write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_quoted(This->output, sysid, -1); + } + else if (sysid) + { + write_output_buffer(This->output, systemW, ARRAY_SIZE(systemW)); + write_output_buffer_quoted(This->output, sysid, -1); + } + + if (subset) + { + write_output_buffer_char(This->output, ' '); + write_output_buffer_char(This->output, '['); + write_output_buffer(This->output, subset, -1); + write_output_buffer_char(This->output, ']'); + } + write_output_buffer_char(This->output, '>'); + + This->state = XmlWriterState_Content; + + return S_OK; } static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix, diff --git a/dlls/xmllite/xmllite_private.h b/dlls/xmllite/xmllite_private.h index 9fb9e82d33d..10f43760b41 100644 --- a/dlls/xmllite/xmllite_private.h +++ b/dlls/xmllite/xmllite_private.h @@ -61,5 +61,8 @@ const WCHAR *get_encoding_name(xml_encoding) DECLSPEC_HIDDEN; xml_encoding get_encoding_from_codepage(UINT) DECLSPEC_HIDDEN; BOOL is_ncnamechar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_pubchar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_namestartchar(WCHAR ch) DECLSPEC_HIDDEN; +BOOL is_namechar(WCHAR ch) DECLSPEC_HIDDEN; #endif /* __XMLLITE_PRIVATE__ */