Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- 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 97c269ceda..a6d1f3edce 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 5ddb1ef2a7..99ad168c53 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, "<!DOCTYPE a PUBLIC \"\" \"\">" }, + { "a", NULL, NULL, NULL, "<!DOCTYPE a>" }, + { "a", NULL, "", NULL, "<!DOCTYPE a SYSTEM \"\">" }, + { "a", "", "", NULL, "<!DOCTYPE a PUBLIC \"\" \"\">" }, + { "a", "pubid", "", NULL, "<!DOCTYPE a PUBLIC \"pubid\" \"\">" }, + { "a", "pubid", NULL, NULL, "<!DOCTYPE a PUBLIC \"pubid\" \"\">" }, + { "a", "", "sysid", NULL, "<!DOCTYPE a PUBLIC \"\" \"sysid\">" }, + { "a", NULL, NULL, "", "<!DOCTYPE a []>" }, + { "a", NULL, NULL, "subset", "<!DOCTYPE a [subset]>" }, + { "a", "", NULL, "subset", "<!DOCTYPE a PUBLIC \"\" \"\" [subset]>" }, + { "a", NULL, "", "subset", "<!DOCTYPE a SYSTEM \"\" [subset]>" }, + { "a", "", "", "subset", "<!DOCTYPE a PUBLIC \"\" \"\" [subset]>" }, + { "a", "pubid", NULL, "subset", "<!DOCTYPE a PUBLIC \"pubid\" \"\" [subset]>" }, + { "a", "pubid", "", "subset", "<!DOCTYPE a PUBLIC \"pubid\" \"\" [subset]>" }, + { "a", NULL, "sysid", "subset", "<!DOCTYPE a SYSTEM \"sysid\" [subset]>" }, + { "a", "", "sysid", "subset", "<!DOCTYPE a PUBLIC \"\" \"sysid\" [subset]>" }, + { "a", "pubid", "sysid", "subset", "<!DOCTYPE a PUBLIC \"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 8d1fb3a2da..e1fc232d4f 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 9fb9e82d33..10f43760b4 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__ */
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/xmllite/writer.c | 53 +++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 30 deletions(-)
diff --git a/dlls/xmllite/writer.c b/dlls/xmllite/writer.c index e1fc232d4f..c1e2c97de3 100644 --- a/dlls/xmllite/writer.c +++ b/dlls/xmllite/writer.c @@ -42,11 +42,6 @@ DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, static const WCHAR closeelementW[] = {'<','/'}; static const WCHAR closetagW[] = {' ','/','>'}; static const WCHAR closepiW[] = {'?','>'}; -static const WCHAR ltW[] = {'<'}; -static const WCHAR gtW[] = {'>'}; -static const WCHAR spaceW[] = {' '}; -static const WCHAR quoteW[] = {'"'}; -static const WCHAR eqW[] = {'='}; static const WCHAR xmlnsW[] = {' ','x','m','l','n','s'}; static const WCHAR xmlnsuriW[] = {'h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','2','0','0','0','/','x','m','l','n','s','/',0};
@@ -492,33 +487,31 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i return S_OK; }
+static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch) +{ + return write_output_buffer(output, &ch, 1); +} + static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len) { - write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW)); + write_output_buffer_char(output, '"'); if (!is_empty_string(data)) write_output_buffer(output, data, len); - write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW)); + write_output_buffer_char(output, '"'); 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) { - static const WCHAR colW[] = {':'}; - assert(prefix_len >= 0 && local_len >= 0);
if (prefix_len) write_output_buffer(output, prefix, prefix_len);
if (prefix_len && local_len) - write_output_buffer(output, colW, ARRAY_SIZE(colW)); + write_output_buffer_char(output, ':');
write_output_buffer(output, local_name, local_len);
@@ -645,7 +638,7 @@ static void writer_output_ns(xmlwriter *writer, struct element *element) continue;
write_output_qname(writer->output, xmlnsW, ARRAY_SIZE(xmlnsW), ns->prefix, ns->prefix_len); - write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW)); + write_output_buffer_char(writer->output, '='); write_output_buffer_quoted(writer->output, ns->uri, -1); } } @@ -657,7 +650,7 @@ static HRESULT writer_close_starttag(xmlwriter *writer) if (!writer->starttagopen) return S_OK;
writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry)); - hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW)); + hr = write_output_buffer_char(writer->output, '>'); writer->starttagopen = 0; return hr; } @@ -868,9 +861,9 @@ static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *p static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *local, int local_len, const WCHAR *value) { - write_output_buffer(writer->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(writer->output, ' '); write_output_qname(writer->output, prefix, prefix_len, local, local_len); - write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW)); + write_output_buffer_char(writer->output, '='); write_output_buffer_quoted(writer->output, value, -1); }
@@ -1138,14 +1131,14 @@ static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment) for (i = 0; i < len; i++) { write_output_buffer(This->output, comment + i, 1); if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-') - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); } } else write_output_buffer(This->output, comment, len);
if (len && comment[len-1] == '-') - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); } write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
@@ -1194,7 +1187,7 @@ static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LP { 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_char(This->output, ' '); write_output_buffer_quoted(This->output, sysid, -1); } else if (sysid) @@ -1269,7 +1262,7 @@ static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR pr write_encoding_bom(This); write_node_indent(This);
- write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW)); + write_output_buffer_char(This->output, '<'); if (ns) write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len); else @@ -1278,17 +1271,17 @@ static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR pr if (!ns && (prefix_len || !is_empty_string(uri))) { write_output_qname(This->output, xmlnsW, ARRAY_SIZE(xmlnsW), prefix, prefix_len); - write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW)); + write_output_buffer_char(This->output, '='); write_output_buffer_quoted(This->output, uri, -1); }
if (value) { - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); write_output_buffer(This->output, value, -1); write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); write_output_qname(This->output, prefix, prefix_len, local_name, local_len); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); } else write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW)); @@ -1365,7 +1358,7 @@ static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface) write_node_indent(This); write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); write_output_buffer(This->output, element->qname, element->len); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>'); } writer_free_element(This, element);
@@ -1435,7 +1428,7 @@ static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface) /* write full end tag */ write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW)); write_output_buffer(This->output, element->qname, element->len); - write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW)); + write_output_buffer_char(This->output, '>');
writer_free_element(This, element);
@@ -1538,7 +1531,7 @@ static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LP write_node_indent(This); write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW)); write_output_buffer(This->output, name, -1); - write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW)); + write_output_buffer_char(This->output, ' '); write_output_buffer(This->output, text, -1); write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
@@ -1707,7 +1700,7 @@ static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR pre if (!ns && uri) writer_push_ns(This, prefix, prefix_len, uri);
- write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW)); + write_output_buffer_char(This->output, '<'); if (ns) write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len); else