[PATCH 0/7] MR10172: msxml/tests: Separate reader tests in a number of helpers.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/saxreader.c | 90 ++++++++++++++++++++- dlls/msxml6/tests/saxreader.c | 147 +++++++++++++++++++++++----------- 2 files changed, 187 insertions(+), 50 deletions(-) diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index f5f998fbfec..0d529ba9a70 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -2328,7 +2328,6 @@ static void test_saxreader(void) HANDLE file; static const CHAR testXmlA[] = "test.xml"; IXMLDOMDocument *doc; - char seqname[50]; VARIANT_BOOL v; while (table->clsid) @@ -2665,17 +2664,58 @@ static void test_saxreader(void) hr = ISAXXMLReader_putEntityResolver(reader, NULL); ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); + ISAXXMLReader_Release(reader); + table++; + } + + free_bstrs(); +} + +static void test_saxreader_cdata(void) +{ + const struct msxmlsupported_data_t *table = reader_support_data; + ISAXXMLReader *reader = NULL; + char seqname[50]; + IStream *stream; + VARIANT var; + HRESULT hr; + + while (table->clsid) + { + struct call_entry *test_seq; + + if (!is_clsid_supported(table->clsid, reader_support_data)) + { + table++; + continue; + } + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + msxml_version = 4; + else + msxml_version = 0; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* CDATA sections */ init_saxlexicalhandler(&lexicalhandler, S_OK); V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface; + V_UNKNOWN(&var) = (IUnknown *)&lexicalhandler.ISAXLexicalHandler_iface; hr = ISAXXMLReader_putProperty(reader, L"http://xml.org/sax/properties/lexical-handler", var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); stream = create_test_stream(test_cdata_xml, -1); V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; + V_UNKNOWN(&var) = (IUnknown *)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = cdata_test_alt; @@ -2711,7 +2751,7 @@ static void test_saxreader(void) /* 3. CDATA sections */ stream = create_test_stream(test3_cdata_xml, -1); V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; + V_UNKNOWN(&var) = (IUnknown *)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = cdata_test3_alt; @@ -2726,6 +2766,46 @@ static void test_saxreader(void) IStream_Release(stream); + ISAXXMLReader_Release(reader); + table++; + } + + free_bstrs(); +} + +static void test_saxreader_pi(void) +{ + const struct msxmlsupported_data_t *table = reader_support_data; + ISAXXMLReader *reader; + char seqname[50]; + VARIANT var; + HRESULT hr; + + while (table->clsid) + { + struct call_entry *test_seq; + + if (!is_clsid_supported(table->clsid, reader_support_data)) + { + table++; + continue; + } + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + msxml_version = 4; + else + msxml_version = 0; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* PI */ V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_pi_xml, -1); @@ -6348,6 +6428,8 @@ START_TEST(saxreader) test_saxreader_encoding(); test_saxreader_dispex(); test_saxreader_vb_content_handler(); + test_saxreader_cdata(); + test_saxreader_pi(); test_saxreader_dtd(); /* MXXMLWriter tests */ diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 39498f553c3..0a64f6b1eb8 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -4803,52 +4803,6 @@ static void test_saxreader(void) hr = ISAXXMLReader_putEntityResolver(reader, NULL); ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); - /* CDATA sections */ - init_saxlexicalhandler(&lexicalhandler, S_OK); - - V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface; - hr = ISAXXMLReader_putProperty(reader, L"http://xml.org/sax/properties/lexical-handler", var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - stream = create_test_stream(test_cdata_xml, -1); - V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; - - test_seq = cdata_test_alt; - set_expected_seq(test_seq); - hr = ISAXXMLReader_parse(reader, var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test", TRUE); - - IStream_Release(stream); - - /* 2. CDATA sections */ - stream = create_test_stream(test2_cdata_xml, -1); - V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; - - test_seq = cdata_test2_alt; - set_expected_seq(test_seq); - hr = ISAXXMLReader_parse(reader, var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 2", TRUE); - - IStream_Release(stream); - - /* 3. CDATA sections */ - stream = create_test_stream(test3_cdata_xml, -1); - V_VT(&var) = VT_UNKNOWN; - V_UNKNOWN(&var) = (IUnknown*)stream; - - test_seq = cdata_test3_alt; - set_expected_seq(test_seq); - hr = ISAXXMLReader_parse(reader, var); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 3", TRUE); - - IStream_Release(stream); - /* PI */ V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_pi_xml, -1); @@ -5042,6 +4996,105 @@ static void test_saxreader_vb_content_handler(void) free_bstrs(); } +static void test_saxreader_cdata(void) +{ + struct call_entry *test_seq; + ISAXXMLReader *reader; + IStream *stream; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* CDATA sections */ + init_saxlexicalhandler(&lexicalhandler, S_OK); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface; + hr = ISAXXMLReader_putProperty(reader, L"http://xml.org/sax/properties/lexical-handler", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream = create_test_stream(test_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + + test_seq = cdata_test_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test", TRUE); + + IStream_Release(stream); + + /* 2. CDATA sections */ + stream = create_test_stream(test2_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + + test_seq = cdata_test2_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 2", TRUE); + + IStream_Release(stream); + + /* 3. CDATA sections */ + stream = create_test_stream(test3_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + + test_seq = cdata_test3_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 3", TRUE); + + IStream_Release(stream); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_saxreader_pi(void) +{ + ISAXXMLReader *reader; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* PI */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_pi_xml, -1); + set_expected_seq(pi_test); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, pi_test, "pi test 1", TRUE); + VariantClear(&var); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + START_TEST(saxreader) { HRESULT hr; @@ -5061,6 +5114,8 @@ START_TEST(saxreader) test_saxreader_encoding(); test_saxreader_dispex(); test_saxreader_vb_content_handler(); + test_saxreader_cdata(); + test_saxreader_pi(); } if (is_class_supported(&CLSID_MXXMLWriter60)) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/saxreader.c | 144 ++++++++++++++ dlls/msxml6/tests/saxreader.c | 358 +++++++++++++++++++++------------- 2 files changed, 362 insertions(+), 140 deletions(-) diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index 0d529ba9a70..56164f66e2f 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -761,6 +761,12 @@ static const char test3_cdata_xml[] = static const char test_pi_xml[] = "<?xml version=\"1.0\" ?><a><?t some text ?></a>"; +static const char test_chardata_xml[] = +"<?xml version=\"1.0\" ?><a>\nabc<b>de\nf</b>gh\n</a>"; + +static const char test_chardata_xml2[] = +"<?xml version=\"1.0\" ?><a>\rabc<b>de\rf</b>gh\r</a>"; + static struct call_entry content_handler_test1[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, @@ -1120,6 +1126,72 @@ static struct call_entry pi_test_v4[] = { CH_ENDTEST } }; +static struct call_entry chardata_test[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 26, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 26, S_OK, L"\nabc" }, + { CH_STARTELEMENT, 2, 7, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 2, 7, S_OK, L"de\nf" }, + { CH_ENDELEMENT, 3, 4, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 6, S_OK, L"gh\n" }, + { CH_ENDELEMENT, 4, 3, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test_v4[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 4, S_OK, L"\nabc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 2, S_OK, L"de\nf" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 4, 1, S_OK, L"gh\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test2[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 26, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 26, S_OK, L"\n" }, + { CH_CHARACTERS, 2, 1, S_OK, L"abc" }, + { CH_STARTELEMENT, 2, 7, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 2, 7, S_OK, L"de\n" }, + { CH_CHARACTERS, 3, 1, S_OK, L"f" }, + { CH_ENDELEMENT, 3, 4, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 6, S_OK, L"gh\n" }, + { CH_ENDELEMENT, 4, 3, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test2_v4[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 2, 4, S_OK, L"abc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 2, 9, S_OK, L"de" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 2, S_OK, L"f" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 8, S_OK, L"gh" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + /* this is what MSXML6 does */ static struct call_entry cdata_test_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, @@ -2829,6 +2901,77 @@ static void test_saxreader_pi(void) free_bstrs(); } +static void test_saxreader_characters(void) +{ + const struct msxmlsupported_data_t *table = reader_support_data; + ISAXXMLReader *reader; + char seqname[50]; + VARIANT var; + HRESULT hr; + + while (table->clsid) + { + struct call_entry *test_seq; + + if (!is_clsid_supported(table->clsid, reader_support_data)) + { + table++; + continue; + } + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + msxml_version = 4; + else + msxml_version = 0; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Character data. */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml, -1); + + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + test_seq = chardata_test_v4; + else + test_seq = chardata_test; + + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + sprintf(seqname, "%s: char data test", table->name); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + VariantClear(&var); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml2, -1); + + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + test_seq = chardata_test2_v4; + else + test_seq = chardata_test2; + + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + sprintf(seqname, "%s: char data test", table->name); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + VariantClear(&var); + + ISAXXMLReader_Release(reader); + table++; + } + + free_bstrs(); +} + static void test_saxreader_properties(void) { static const struct saxreader_props_test_t @@ -6430,6 +6573,7 @@ START_TEST(saxreader) test_saxreader_vb_content_handler(); test_saxreader_cdata(); test_saxreader_pi(); + test_saxreader_characters(); test_saxreader_dtd(); /* MXXMLWriter tests */ diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 0a64f6b1eb8..3f3657fa900 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -246,11 +246,12 @@ static const char *get_event_name(CH event) return event_names[event]; } -struct attribute_entry { - const char *uri; - const char *local; - const char *qname; - const char *value; +struct attribute_entry +{ + const WCHAR *uri; + const WCHAR *local; + const WCHAR *qname; + const WCHAR *value; /* used for actual call data only, null for expected call data */ BSTR uriW; @@ -265,9 +266,9 @@ struct call_entry int line; int column; HRESULT ret; - const char *arg1; - const char *arg2; - const char *arg3; + const WCHAR *arg1; + const WCHAR *arg2; + const WCHAR *arg3; /* allocated once at startElement callback */ struct attribute_entry *attributes; @@ -290,10 +291,9 @@ struct call_sequence #define NUM_CALL_SEQUENCES 1 static struct call_sequence *sequences[NUM_CALL_SEQUENCES]; -static void test_saxstr(const char *file, unsigned line, BSTR str, const char *expected, BOOL todo, int *failcount) +static void test_saxstr(const char *file, unsigned line, BSTR str, const WCHAR *expected, BOOL todo, int *failcount) { int len, lenexp, cmp; - WCHAR buf[1024]; len = SysStringLen(str); @@ -318,32 +318,30 @@ static void test_saxstr(const char *file, unsigned line, BSTR str, const char *e return; } - lenexp = strlen(expected); + lenexp = lstrlenW(expected); if (lenexp != len && todo) { (*failcount)++; todo_wine - ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected); + ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, wine_dbgstr_w(expected)); } else - ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected); + ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, wine_dbgstr_w(expected)); /* exit earlier on length mismatch */ if (lenexp != len) return; - MultiByteToWideChar(CP_ACP, 0, expected, -1, buf, ARRAY_SIZE(buf)); - - cmp = memcmp(str, buf, lenexp*sizeof(WCHAR)); + cmp = lstrcmpW(str, expected); if (cmp && todo) { (*failcount)++; todo_wine ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", - wine_dbgstr_wn(str, len), expected); + wine_dbgstr_wn(str, len), wine_dbgstr_w(expected)); } else ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", - wine_dbgstr_wn(str, len), expected); + wine_dbgstr_wn(str, len), wine_dbgstr_w(expected)); } static void init_call_entry(ISAXLocator *locator, struct call_entry *call) @@ -701,6 +699,12 @@ static const char test3_cdata_xml[] = static const char test_pi_xml[] = "<?xml version=\"1.0\" ?><a><?t some text ?></a>"; +static const char test_chardata_xml[] = +"<?xml version=\"1.0\" ?><a>\nabc<b>de\nf</b>gh\n</a>"; + +static const char test_chardata_xml2[] = +"<?xml version=\"1.0\" ?><a>\rabc<b>de\rf</b>gh\r</a>"; + struct class_support { const GUID *clsid; @@ -3729,39 +3733,38 @@ static void test_saxreader_dispex(void) } static struct saxlexicalhandler lexicalhandler; -//static struct saxdeclhandler declhandler; static struct call_entry content_handler_test1_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" }, - { CH_CHARACTERS, 3, 4, S_OK, "\n " }, - { CH_STARTELEMENT, 3, 11, S_OK, "", "Number", "Number" }, - { CH_CHARACTERS, 3, 16, S_OK, "1234" }, - { CH_ENDELEMENT, 3, 24, S_OK, "", "Number", "Number" }, - { CH_CHARACTERS, 4, 4, S_OK, "\n " }, - { CH_STARTELEMENT, 4, 9, S_OK, "", "Name", "Name" }, - { CH_CHARACTERS, 4, 22, S_OK, "Captain Ahab" }, - { CH_ENDELEMENT, 4, 28, S_OK, "", "Name", "Name" }, - { CH_CHARACTERS, 5, 1, S_OK, "\n" }, - { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" }, + { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 4, S_OK, L"\n " }, + { CH_STARTELEMENT, 3, 11, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 16, S_OK, L"1234" }, + { CH_ENDELEMENT, 3, 24, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 4, S_OK, L"\n " }, + { CH_STARTELEMENT, 4, 9, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 22, S_OK, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 28, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 1, S_OK, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_OK }, { CH_ENDTEST } }; static struct attribute_entry ch_attributes_alt_6[] = { - { "prefix_test", "arg1", "test:arg1", "arg1" }, - { "", "arg2", "arg2", "arg2" }, - { "prefix_test", "ar3", "test:ar3", "arg3" }, - { "http://www.w3.org/2000/xmlns/", "", "xmlns:test", "prefix_test" }, - { "http://www.w3.org/2000/xmlns/", "", "xmlns", "prefix" }, + { L"prefix_test", L"arg1", L"test:arg1", L"arg1" }, + { L"", L"arg2", L"arg2", L"arg2" }, + { L"prefix_test", L"ar3", L"test:ar3", L"arg3" }, + { L"http://www.w3.org/2000/xmlns/", L"", L"xmlns:test", L"prefix_test" }, + { L"http://www.w3.org/2000/xmlns/", L"", L"xmlns", L"prefix" }, { NULL } }; static struct attribute_entry ch_attributes2_6[] = { - { "http://www.w3.org/2000/xmlns/", "", "xmlns:p", "test" }, + { L"http://www.w3.org/2000/xmlns/", L"", L"xmlns:p", L"test" }, { NULL } }; @@ -3769,24 +3772,24 @@ static struct call_entry content_handler_test_attributes_alternate_6[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" }, - { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_6 }, - { CH_CHARACTERS, 3, 1, S_OK, "\n" }, - { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" }, - { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2_6 }, - { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" }, - { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" }, - { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, + { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_alt_6 }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", ch_attributes2_6 }, + { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, + { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; static struct attribute_entry read_test_attrs[] = { - { "", "attr", "attr", "val" }, + { L"", L"attr", L"attr", L"val" }, { NULL } }; @@ -3794,20 +3797,20 @@ static struct call_entry read_test_seq[] = { { CH_PUTDOCUMENTLOCATOR, -1, 0, S_OK }, { CH_STARTDOCUMENT, -1, -1, S_OK }, - { CH_STARTELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" }, - { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, - { CH_CHARACTERS, -1, -1, S_OK, "text" }, - { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, - { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, - { CH_CHARACTERS, -1, -1, S_OK, "text" }, - { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, - { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, - { CH_CHARACTERS, -1, -1, S_OK, "text" }, - { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, - { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, - { CH_CHARACTERS, -1, -1, S_OK, "text" }, - { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, - { CH_ENDELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"rootelem", L"rootelem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"rootelem", L"rootelem" }, { CH_ENDDOCUMENT, -1, -1, S_OK}, { CH_ENDTEST } }; @@ -3816,19 +3819,19 @@ static struct call_entry content_handler_test2_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 21, S_OK }, - { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" }, - { CH_CHARACTERS, 3, 0, S_OK, "\n" }, - { CH_CHARACTERS, 3, 2, S_OK, "\t" }, - { CH_STARTELEMENT, 3, 9, S_OK, "", "Number", "Number" }, - { CH_CHARACTERS, 3, 14, S_OK, "1234" }, - { CH_ENDELEMENT, 3, 22, S_OK, "", "Number", "Number" }, - { CH_CHARACTERS, 4, 0, S_OK, "\n" }, - { CH_CHARACTERS, 4, 2, S_OK, "\t" }, - { CH_STARTELEMENT, 4, 7, S_OK, "", "Name", "Name" }, - { CH_CHARACTERS, 4, 20, S_OK, "Captain Ahab" }, - { CH_ENDELEMENT, 4, 26, S_OK, "", "Name", "Name" }, - { CH_CHARACTERS, 5, 0, S_OK, "\n" }, - { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" }, + { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 2, S_OK, L"\t" }, + { CH_STARTELEMENT, 3, 9, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 14, S_OK, L"1234" }, + { CH_ENDELEMENT, 3, 22, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 4, 2, S_OK, L"\t" }, + { CH_STARTELEMENT, 4, 7, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 20, S_OK, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 26, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 0, S_OK, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_OK }, { CH_ENDTEST } }; @@ -3845,24 +3848,24 @@ static struct call_entry content_handler_test_callback_rets_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE }, { CH_STARTDOCUMENT, 1, 22, S_FALSE }, - { CH_STARTELEMENT, 2, 13, S_FALSE, "", "BankAccount", "BankAccount" }, - { CH_CHARACTERS, 3, 4, S_FALSE, "\n " }, - { CH_STARTELEMENT, 3, 11, S_FALSE, "", "Number", "Number" }, - { CH_CHARACTERS, 3, 16, S_FALSE, "1234" }, - { CH_ENDELEMENT, 3, 24, S_FALSE, "", "Number", "Number" }, - { CH_CHARACTERS, 4, 4, S_FALSE, "\n " }, - { CH_STARTELEMENT, 4, 9, S_FALSE, "", "Name", "Name" }, - { CH_CHARACTERS, 4, 22, S_FALSE, "Captain Ahab" }, - { CH_ENDELEMENT, 4, 28, S_FALSE, "", "Name", "Name" }, - { CH_CHARACTERS, 5, 1, S_FALSE, "\n" }, - { CH_ENDELEMENT, 5, 14, S_FALSE, "", "BankAccount", "BankAccount" }, + { CH_STARTELEMENT, 2, 13, S_FALSE, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 4, S_FALSE, L"\n " }, + { CH_STARTELEMENT, 3, 11, S_FALSE, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 16, S_FALSE, L"1234" }, + { CH_ENDELEMENT, 3, 24, S_FALSE, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 4, S_FALSE, L"\n " }, + { CH_STARTELEMENT, 4, 9, S_FALSE, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 22, S_FALSE, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 28, S_FALSE, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 1, S_FALSE, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_FALSE, L"", L"BankAccount", L"BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_FALSE }, { CH_ENDTEST } }; static struct attribute_entry xmlspace_attrs[] = { - { "http://www.w3.org/XML/1998/namespace", "space", "xml:space", "preserve" }, + { L"http://www.w3.org/XML/1998/namespace", L"space", L"xml:space", L"preserve" }, { NULL } }; @@ -3870,25 +3873,25 @@ static struct call_entry xmlspaceattr_test_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 39, S_OK }, - { CH_STARTELEMENT, 1, 63, S_OK, "", "a", "a", xmlspace_attrs }, - { CH_CHARACTERS, 1, 80, S_OK, " Some text data " }, - { CH_ENDELEMENT, 1, 83, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 1, 63, S_OK, L"", L"a", L"a", xmlspace_attrs }, + { CH_CHARACTERS, 1, 80, S_OK, L" Some text data " }, + { CH_ENDELEMENT, 1, 83, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 1, 83, S_OK }, { CH_ENDTEST } }; static struct attribute_entry ch_attributes2[] = { - { "", "", "xmlns:p", "test" }, + { L"", L"", L"xmlns:p", L"test" }, { NULL } }; /* 'namespace' feature switched off */ static struct attribute_entry ch_attributes_alt_no_ns[] = { - { "", "", "xmlns:test", "prefix_test" }, - { "", "", "xmlns", "prefix" }, - { "", "", "test:arg1", "arg1" }, - { "", "", "arg2", "arg2" }, - { "", "", "test:ar3", "arg3" }, + { L"", L"", L"xmlns:test", L"prefix_test" }, + { L"", L"", L"xmlns", L"prefix" }, + { L"", L"", L"test:arg1", L"arg1" }, + { L"", L"", L"arg2", L"arg2" }, + { L"", L"", L"test:ar3", L"arg3" }, { NULL } }; @@ -3896,11 +3899,11 @@ static struct call_entry content_handler_test_attributes_alt_no_ns[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 2, 95, S_OK, "", "", "document", ch_attributes_alt_no_ns }, - { CH_CHARACTERS, 3, 1, S_OK, "\n" }, - { CH_STARTELEMENT, 3, 24, S_OK, "", "", "node1", ch_attributes2 }, - { CH_ENDELEMENT, 3, 24, S_OK, "", "", "node1" }, - { CH_ENDELEMENT, 3, 35, S_OK, "", "", "document" }, + { CH_STARTELEMENT, 2, 95, S_OK, L"", L"", L"document", ch_attributes_alt_no_ns }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"", L"", L"node1", ch_attributes2 }, + { CH_ENDELEMENT, 3, 24, S_OK, L"", L"", L"node1" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"", L"", L"document" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; @@ -3908,9 +3911,9 @@ static struct call_entry content_handler_test_attributes_alt_no_ns[] = /* 'namespaces' is on, 'namespace-prefixes' if off */ static struct attribute_entry ch_attributes_no_prefix[] = { - { "prefix_test", "arg1", "test:arg1", "arg1" }, - { "", "arg2", "arg2", "arg2" }, - { "prefix_test", "ar3", "test:ar3", "arg3" }, + { L"prefix_test", L"arg1", L"test:arg1", L"arg1" }, + { L"", L"arg2", L"arg2", L"arg2" }, + { L"prefix_test", L"ar3", L"test:ar3", L"arg3" }, { NULL } }; @@ -3918,24 +3921,24 @@ static struct call_entry content_handler_test_attributes_alt_no_prefix[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" }, - { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_no_prefix }, - { CH_CHARACTERS, 3, 1, S_OK, "\n" }, - { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" }, - { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", NULL }, - { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" }, - { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" }, - { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, + { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_no_prefix }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", NULL }, + { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, + { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; static struct attribute_entry attribute_norm_attrs[] = { - { "", "attr1", "attr1", " attr_value A & & " }, + { L"", L"attr1", L"attr1", L" attr_value A & & " }, { NULL } }; @@ -3943,8 +3946,8 @@ static struct call_entry attribute_norm_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 8, 3, S_OK, "", "a", "a", attribute_norm_attrs }, - { CH_ENDELEMENT, 8, 3, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 8, 3, S_OK, L"", L"a", L"a", attribute_norm_attrs }, + { CH_ENDELEMENT, 8, 3, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 9, 0, S_OK }, { CH_ENDTEST } }; @@ -3953,15 +3956,15 @@ static struct call_entry cdata_test_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 1, 40, S_OK, "Some " }, - { CH_CHARACTERS, 2, 0, S_OK, "\n" }, - { CH_CHARACTERS, 3, 1, S_OK, "text\n" }, - { CH_CHARACTERS, 4, 0, S_OK, "\n" }, - { CH_CHARACTERS, 6, 3, S_OK, "data\n\n" }, + { CH_CHARACTERS, 1, 40, S_OK, L"Some " }, + { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 1, S_OK, L"text\n" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 6, 3, S_OK, L"data\n\n" }, { LH_ENDCDATA, 6, 3, S_OK }, - { CH_ENDELEMENT, 6, 7, S_OK, "", "a", "a" }, + { CH_ENDELEMENT, 6, 7, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 6, 7, S_OK }, { CH_ENDTEST } }; @@ -3970,17 +3973,17 @@ static struct call_entry cdata_test2_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 2, 1, S_OK, "\n" }, - { CH_CHARACTERS, 3, 0, S_OK, "\n" }, - { CH_CHARACTERS, 3, 6, S_OK, "Some " }, - { CH_CHARACTERS, 4, 0, S_OK, "\n" }, - { CH_CHARACTERS, 5, 1, S_OK, "text\n" }, - { CH_CHARACTERS, 6, 0, S_OK, "\n" }, - { CH_CHARACTERS, 8, 3, S_OK, "data\n\n" }, + { CH_CHARACTERS, 2, 1, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 6, S_OK, L"Some " }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 5, 1, S_OK, L"text\n" }, + { CH_CHARACTERS, 6, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 8, 3, S_OK, L"data\n\n" }, { LH_ENDCDATA, 8, 3, S_OK }, - { CH_ENDELEMENT, 8, 7, S_OK, "", "a", "a" }, + { CH_ENDELEMENT, 8, 7, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 8, 7, S_OK }, { CH_ENDTEST } }; @@ -3989,11 +3992,11 @@ static struct call_entry cdata_test3_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 1, 51, S_OK, "Some text data" }, + { CH_CHARACTERS, 1, 51, S_OK, L"Some text data" }, { LH_ENDCDATA, 1, 51, S_OK }, - { CH_ENDELEMENT, 1, 55, S_OK, "", "a", "a" }, + { CH_ENDELEMENT, 1, 55, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 1, 55, S_OK }, { CH_ENDTEST } }; @@ -4002,9 +4005,9 @@ static struct call_entry pi_test[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, - { CH_PROCESSINGINSTRUCTION, 1, 41, S_OK, "t", "some text " }, - { CH_ENDELEMENT, 1, 45, S_OK, "", "a", "a" }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_PROCESSINGINSTRUCTION, 1, 41, S_OK, L"t", L"some text " }, + { CH_ENDELEMENT, 1, 45, S_OK, L"", L"a", L"a" }, { CH_ENDDOCUMENT, 1, 45, S_OK }, { CH_ENDTEST } }; @@ -5065,6 +5068,80 @@ static void test_saxreader_cdata(void) free_bstrs(); } +static struct call_entry chardata_test[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 4, S_OK, L"\nabc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 2, S_OK, L"de\nf" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 4, 1, S_OK, L"gh\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test2[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 2, 4, S_OK, L"abc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 2, 9, S_OK, L"de" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 2, S_OK, L"f" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 8, S_OK, L"gh" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + +static void test_saxreader_characters(void) +{ + ISAXXMLReader *reader; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Character data. */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml, -1); + + set_expected_seq(chardata_test); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, chardata_test, "char data test", TRUE); + VariantClear(&var); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml2, -1); + + set_expected_seq(chardata_test); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, chardata_test2, "char data test 2", TRUE); + VariantClear(&var); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + static void test_saxreader_pi(void) { ISAXXMLReader *reader; @@ -5115,6 +5192,7 @@ START_TEST(saxreader) test_saxreader_dispex(); test_saxreader_vb_content_handler(); test_saxreader_cdata(); + test_saxreader_characters(); test_saxreader_pi(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/saxreader.c | 6783 +++++++++++++++++++++++---------- dlls/msxml3/tests/saxreader.c | 54 +- dlls/msxml3/tests/xmldoc.c | 1 - dlls/msxml6/tests/saxreader.c | 12 +- 4 files changed, 4748 insertions(+), 2102 deletions(-) diff --git a/dlls/msxml3/saxreader.c b/dlls/msxml3/saxreader.c index 75e44ca68a2..7846b512910 100644 --- a/dlls/msxml3/saxreader.c +++ b/dlls/msxml3/saxreader.c @@ -3,6 +3,7 @@ * * Copyright 2008 Alistair Leslie-Hughes * Copyright 2008 Piotr Caban + * Copyright 2025-2026 Nikolay Sivov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,10 +22,6 @@ #define COBJMACROS #include <stdarg.h> -#include <libxml/parser.h> -#include <libxml/xmlerror.h> -#include <libxml/SAX2.h> -#include <libxml/parserInternals.h> #include "windef.h" #include "winbase.h" @@ -36,6 +33,7 @@ #include "urlmon.h" #include "winreg.h" #include "shlwapi.h" +#include "winternl.h" #include "wine/debug.h" @@ -43,29 +41,284 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml); -typedef enum -{ - FeatureUnknown = 0, - ExhaustiveErrors = 1 << 1, - ExternalGeneralEntities = 1 << 2, - ExternalParameterEntities = 1 << 3, - ForcedResync = 1 << 4, - NamespacePrefixes = 1 << 5, - Namespaces = 1 << 6, - ParameterEntities = 1 << 7, - PreserveSystemIndentifiers = 1 << 8, - ProhibitDTD = 1 << 9, - SchemaValidation = 1 << 10, - ServerHttpRequest = 1 << 11, - SuppressValidationfatalError = 1 << 12, - UseInlineSchema = 1 << 13, - UseSchemaLocation = 1 << 14, - LexicalHandlerParEntities = 1 << 15 -} saxreader_feature; +struct stream_wrapper +{ + ISequentialStream ISequentialStream_iface; + LONG refcount; + + const BYTE *buffer; + DWORD size; + DWORD position; +}; + +static struct stream_wrapper *impl_from_ISequentialStream(ISequentialStream *iface) +{ + return CONTAINING_RECORD(iface, struct stream_wrapper, ISequentialStream_iface); +} + +static HRESULT WINAPI stream_wrapper_QueryInterface(ISequentialStream *iface, REFIID riid, void **out) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISequentialStream)) + { + *out = iface; + ISequentialStream_AddRef(iface); + return S_OK; + } + + *out = NULL; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI stream_wrapper_AddRef(ISequentialStream *iface) +{ + struct stream_wrapper *stream = impl_from_ISequentialStream(iface); + ULONG refcount = InterlockedIncrement(&stream->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI stream_wrapper_Release(ISequentialStream *iface) +{ + struct stream_wrapper *stream = impl_from_ISequentialStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + free(stream); + + return refcount; +} + +static HRESULT WINAPI stream_wrapper_Read(ISequentialStream *iface, void *buff, ULONG buff_size, ULONG *read_len) +{ + struct stream_wrapper *stream = impl_from_ISequentialStream(iface); + DWORD size; + + TRACE("%p, %p, %lu, %p.\n", iface, buff, buff_size, read_len); + + if (stream->position >= stream->size) + { + if (read_len) + *read_len = 0; + return S_FALSE; + } + + size = stream->size - stream->position; + if (buff_size < size) + size = buff_size; + + memmove(buff, stream->buffer + stream->position, size); + stream->position += size; + + if (read_len) + *read_len = size; + + return S_OK; +} + +static HRESULT WINAPI stream_wrapper_Write(ISequentialStream *iface, const void *buff, ULONG buff_size, ULONG *written) +{ + TRACE("%p, %p, %lu, %p.\n", iface, buff, buff_size, written); + + return E_NOTIMPL; +} + +static const ISequentialStreamVtbl stream_wrapper_vtbl = +{ + stream_wrapper_QueryInterface, + stream_wrapper_AddRef, + stream_wrapper_Release, + stream_wrapper_Read, + stream_wrapper_Write, +}; + +static HRESULT stream_wrapper_create(const void *buffer, DWORD size, ISequentialStream **ret) +{ + struct stream_wrapper *object; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->ISequentialStream_iface.lpVtbl = &stream_wrapper_vtbl; + object->refcount = 1; + object->buffer = buffer; + object->size = size; + + *ret = &object->ISequentialStream_iface; + + return S_OK; +} + +enum error_codes +{ + E_SAX_UNDEFINEDREF = 0xc00ce002, + E_SAX_INFINITEREFLOOP = 0xc00ce003, + E_SAX_UNPARSEDENTITYREF = 0xc00ce006, + E_SAX_CONTAINSCOLON = 0xc00ce00c, + E_SAX_UNDECLAREDPREFIX = 0xc00ce01d, + + E_SAX_MISSINGEQUALS = 0xc00ce501, + E_SAX_MISSINGQUOTE = 0xc00ce502, + E_SAX_COMMENTSYNTAX = 0xc00ce503, + E_SAX_BADSTARTNAMECHAR = 0xc00ce504, + E_SAX_BADNAMECHAR = 0xc00ce505, + E_SAX_BADCHARINSTRING = 0xc00ce506, + E_SAX_MISSINGWHITESPACE = 0xc00ce509, + E_SAX_EXPECTINGTAGEND = 0xc00ce50a, + E_SAX_BADCHARINDTD = 0xc00ce50b, + E_SAX_MISSINGSEMICOLON = 0xc00ce50d, + E_SAX_BADCHARINENTREF = 0xc00ce50e, + E_SAX_BADCHARINMIXEDMODEL = 0xc00ce515, + E_SAX_MISSING_STAR = 0xc00ce516, + E_SAX_MISSING_PAREN = 0xc00ce518, + E_SAX_BADCHARINENUMERATION = 0xc00ce519, + E_SAX_PIDECLSYNTAX = 0xc00ce51a, + E_SAX_EXPECTINGCLOSEQUOTE = 0xc00ce51b, + E_SAX_MULTIPLE_COLONS = 0xc00ce51c, + E_SAX_INVALID_UNICODE = 0xc00ce51f, + E_SAX_WHITESPACEORQUESTIONMARK = 0xc00ce520, + E_SAX_UNEXPECTEDENDTAG = 0xc00ce552, + E_SAX_DUPLICATEATTRIBUTE = 0xc00ce554, + E_SAX_INVALIDATROOTLEVEL = 0xc00ce556, + E_SAX_BAD_XMLDECL = 0xc00ce557, + E_SAX_MISSINGROOT = 0xc00ce558, + E_SAX_UNEXPECTED_EOF = 0xc00ce559, + E_SAX_INVALID_CDATACLOSINGTAG = 0xc00ce55c, + E_SAX_UNCLOSEDPI = 0xc00ce55d, + E_SAX_UNCLOSEDENDTAG = 0xc00ce55f, + E_SAX_UNCLOSEDCDATA = 0xc00ce564, + E_SAX_BADDECLNAME = 0xc00ce565, + E_SAX_RESERVEDNAMESPACE = 0xc00ce568, + E_SAX_ENDTAGMISMATCH = 0xc00ce56d, + E_SAX_INVALIDENCODING = 0xc00ce56e, + E_SAX_INVALIDSWITCH = 0xc00ce56f, + E_SAX_INVALID_MODEL = 0xc00ce571, + E_SAX_INVALID_TYPE = 0xc00ce572, + E_SAX_BADXMLCASE = 0xc00ce576, + E_SAX_INVALID_STANDALONE = 0xc00ce579, + E_SAX_INVALID_VERSION = 0xc00ce57f, +}; + +enum attdef_type +{ + ATTDEF_TYPE_INVALID = 0, + ATTDEF_TYPE_CDATA, + ATTDEF_TYPE_ID, + ATTDEF_TYPE_IDREF, + ATTDEF_TYPE_IDREFS, + ATTDEF_TYPE_ENTITY, + ATTDEF_TYPE_ENTITIES, + ATTDEF_TYPE_NMTOKEN, + ATTDEF_TYPE_NMTOKENS, + ATTDEF_TYPE_NOTATION, + ATTDEF_TYPE_ENUMERATION, +}; + +enum attdefault_value_type +{ + ATTDEFDECL_NONE = 0, + ATTDEFDECL_REQUIRED, + ATTDEFDECL_IMPLIED, + ATTDEFDECL_FIXED, +}; + +struct attribute +{ + BSTR uri; + BSTR local; + BSTR qname; + BSTR value; + BSTR prefix; + bool nsdef; +}; + +struct name +{ + BSTR prefix; + BSTR local; + BSTR qname; +}; + +struct attlist_attr +{ + struct name name; + BSTR type; + BSTR valuetype; + BSTR value; +}; + +struct attlist_decl +{ + struct list entry; + BSTR name; + struct attlist_attr *attributes; + size_t count; + size_t capacity; +}; + +struct entity_part +{ + BSTR value; + bool reference; +}; + +struct text_position +{ + int line; + int column; +}; + +struct entity_decl +{ + struct list entry; + BSTR name; + BSTR sysid; + BSTR value; + struct + { + struct entity_part *parts; + size_t count; + size_t capacity; + } content; + bool unparsed; + + struct list input_entry; + bool visited; + size_t remaining; + struct text_position position; +}; + +enum saxreader_feature +{ + FeatureUnknown = 0x00000000, + ExhaustiveErrors = 0x00000001, + ExternalGeneralEntities = 0x00000002, + ExternalParameterEntities = 0x00000004, + ForcedResync = 0x00000008, + NamespacePrefixes = 0x00000010, + Namespaces = 0x00000020, + ParameterEntities = 0x00000040, + PreserveSystemIndentifiers = 0x00000080, + ProhibitDTD = 0x00000100, + SchemaValidation = 0x00000200, + ServerHttpRequest = 0x00000400, + SuppressValidationfatalError = 0x00000800, + UseInlineSchema = 0x00001000, + UseSchemaLocation = 0x00002000, + LexicalHandlerParEntities = 0x00004000, + FeatureForceDWord = 0xffffffff, +}; struct saxreader_feature_pair { - saxreader_feature feature; + enum saxreader_feature feature; const WCHAR *name; }; @@ -80,7 +333,7 @@ static const struct saxreader_feature_pair saxreader_feature_map[] = { { SchemaValidation, L"schema-validation" }, }; -static saxreader_feature get_saxreader_feature(const WCHAR *name) +static enum saxreader_feature get_saxreader_feature(const WCHAR *name) { int min, max, n, c; @@ -106,13 +359,6 @@ static saxreader_feature get_saxreader_feature(const WCHAR *name) static const WCHAR empty_str; -struct bstrpool -{ - BSTR *pool; - unsigned int index; - unsigned int len; -}; - typedef struct { BSTR prefix; @@ -164,6 +410,18 @@ struct saxlexicalhandler_iface IVBSAXLexicalHandler *vbhandler; }; +struct saxdtdhandler_iface +{ + ISAXDTDHandler *handler; + IVBSAXDTDHandler *vbhandler; +}; + +struct saxdeclhandler_iface +{ + ISAXDeclHandler *handler; + IVBSAXDeclHandler *vbhandler; +}; + struct saxentityresolver_iface { ISAXEntityResolver *handler; @@ -172,36 +430,39 @@ struct saxentityresolver_iface struct saxhandler_iface { - union { + union + { struct saxcontenthandler_iface content; struct saxentityresolver_iface entityresolver; struct saxerrorhandler_iface error; struct saxlexicalhandler_iface lexical; + struct saxdtdhandler_iface dtd; + struct saxdeclhandler_iface decl; struct saxanyhandler_iface anyhandler; } u; }; -typedef struct +struct saxreader { DispatchEx dispex; IVBSAXXMLReader IVBSAXXMLReader_iface; ISAXXMLReader ISAXXMLReader_iface; - LONG ref; + LONG refcount; struct saxhandler_iface saxhandlers[SAXHandler_Last]; - xmlSAXHandler sax; BOOL isParsing; - struct bstrpool pool; - saxreader_feature features; + enum saxreader_feature features; BSTR xmldecl_version; + BSTR xmldecl_standalone; + BSTR xmldecl_encoding; BSTR empty_bstr; MSXML_VERSION version; -} saxreader; +}; -static HRESULT saxreader_put_handler(saxreader *reader, enum saxhandler_type type, void *ptr, BOOL vb) +static HRESULT saxreader_put_handler(struct saxreader *reader, enum saxhandler_type type, void *ptr, bool vb) { struct saxanyhandler_iface *iface = &reader->saxhandlers[type].u.anyhandler; - IUnknown *unk = (IUnknown*)ptr; + IUnknown *unk = (IUnknown *)ptr; if (unk) IUnknown_AddRef(unk); @@ -217,7 +478,7 @@ static HRESULT saxreader_put_handler(saxreader *reader, enum saxhandler_type typ return S_OK; } -static HRESULT saxreader_get_handler(const saxreader *reader, enum saxhandler_type type, BOOL vb, void **ret) +static HRESULT saxreader_get_handler(const struct saxreader *reader, enum saxhandler_type type, bool vb, void **ret) { const struct saxanyhandler_iface *iface = &reader->saxhandlers[type].u.anyhandler; @@ -236,452 +497,522 @@ static HRESULT saxreader_get_handler(const saxreader *reader, enum saxhandler_ty return S_OK; } -static struct saxcontenthandler_iface *saxreader_get_contenthandler(saxreader *reader) +static struct saxcontenthandler_iface *saxreader_get_contenthandler(struct saxreader *reader) { return &reader->saxhandlers[SAXContentHandler].u.content; } -static struct saxerrorhandler_iface *saxreader_get_errorhandler(saxreader *reader) +static struct saxerrorhandler_iface *saxreader_get_errorhandler(struct saxreader *reader) { return &reader->saxhandlers[SAXErrorHandler].u.error; } -static struct saxlexicalhandler_iface *saxreader_get_lexicalhandler(saxreader *reader) +static struct saxlexicalhandler_iface *saxreader_get_lexicalhandler(struct saxreader *reader) { return &reader->saxhandlers[SAXLexicalHandler].u.lexical; } -typedef struct -{ - IVBSAXLocator IVBSAXLocator_iface; - ISAXLocator ISAXLocator_iface; - IVBSAXAttributes IVBSAXAttributes_iface; - ISAXAttributes ISAXAttributes_iface; - LONG ref; - saxreader *saxreader; - HRESULT ret; - xmlParserCtxtPtr pParserCtxt; - BSTR publicId; - BSTR systemId; - int line; - int column; - BOOL vbInterface; - struct list elements; - - BSTR namespaceUri; - int attr_alloc_count; - int attr_count; - struct _attributes - { - BSTR szLocalname; - BSTR szURI; - BSTR szValue; - BSTR szQName; - } *attributes; -} saxlocator; - -static inline saxreader *impl_from_IVBSAXXMLReader( IVBSAXXMLReader *iface ) +static struct saxdtdhandler_iface *saxreader_get_dtdhandler(struct saxreader *reader) { - return CONTAINING_RECORD(iface, saxreader, IVBSAXXMLReader_iface); + return &reader->saxhandlers[SAXDTDHandler].u.dtd; } -static inline saxreader *impl_from_ISAXXMLReader( ISAXXMLReader *iface ) +static struct saxdeclhandler_iface *saxreader_get_declhandler(struct saxreader *reader) { - return CONTAINING_RECORD(iface, saxreader, ISAXXMLReader_iface); + return &reader->saxhandlers[SAXDeclHandler].u.decl; } -static inline saxlocator *impl_from_IVBSAXLocator( IVBSAXLocator *iface ) +struct encoded_buffer { - return CONTAINING_RECORD(iface, saxlocator, IVBSAXLocator_iface); -} + char *data; + size_t cur; + size_t allocated; + size_t written; +}; -static inline saxlocator *impl_from_ISAXLocator( ISAXLocator *iface ) +static void encoded_buffer_cleanup(struct encoded_buffer *buffer) { - return CONTAINING_RECORD(iface, saxlocator, ISAXLocator_iface); + free(buffer->data); + memset(buffer, 0, sizeof(*buffer)); } -static inline saxlocator *impl_from_IVBSAXAttributes( IVBSAXAttributes *iface ) +enum xmlencoding { - return CONTAINING_RECORD(iface, saxlocator, IVBSAXAttributes_iface); -} + XML_ENCODING_UNKNOWN = 0, + XML_ENCODING_UTF16LE, + XML_ENCODING_UTF16BE, + XML_ENCODING_UCS4BE, + XML_ENCODING_UCS4LE, + XML_ENCODING_UTF8, + XML_ENCODING_LAST = XML_ENCODING_UTF8, + XML_ENCODING_FORCE_DWORD = 0xffffffff, +}; -static inline saxlocator *impl_from_ISAXAttributes( ISAXAttributes *iface ) +static const char *saxreader_get_encoding_name(enum xmlencoding encoding) { - return CONTAINING_RECORD(iface, saxlocator, ISAXAttributes_iface); + static const char *names[] = + { + [XML_ENCODING_UNKNOWN] = "unknown", + [XML_ENCODING_UTF16LE] = "utf16le", + [XML_ENCODING_UTF16BE] = "utf16be", + [XML_ENCODING_UCS4BE] = "ucs4be", + [XML_ENCODING_UCS4LE] = "ucs4le", + [XML_ENCODING_UTF8] = "utf8", + }; + if (encoding > XML_ENCODING_LAST) return "<out-of-bounds>"; + return names[encoding]; } -static inline BOOL saxreader_has_handler(const saxlocator *locator, enum saxhandler_type type) +static bool saxreader_get_encoding_codepage(const WCHAR *name, UINT *codepage) { - struct saxanyhandler_iface *iface = &locator->saxreader->saxhandlers[type].u.anyhandler; - return (locator->vbInterface && iface->vbhandler) || (!locator->vbInterface && iface->handler); + static const struct + { + const WCHAR *name; + UINT codepage; + } + encodings[] = + { + { L"gbk", 936 }, + { L"gb2312", 936 }, + { L"us-ascii", 20127 }, + { L"iso8859-1", 28591 }, + { L"iso-8859-1", 28591 }, + { L"iso-8859-2", 28592 }, + { L"iso-8859-3", 28593 }, + { L"iso-8859-4", 28594 }, + { L"iso-8859-5", 28595 }, + { L"iso-8859-6", 28596 }, + { L"iso-8859-7", 28597 }, + { L"iso-8859-8", 28598 }, + { L"iso-8859-9", 28599 }, + { L"windows-1250", 1250 }, + { L"windows-1251", 1251 }, + { L"windows-1252", 1252 }, + { L"windows-1253", 1253 }, + { L"windows-1254", 1254 }, + { L"windows-1255", 1255 }, + { L"windows-1256", 1256 }, + { L"windows-1257", 1257 }, + { L"windows-1258", 1258 }, + }; + + for (int i = 0; i < ARRAYSIZE(encodings); ++i) + { + if (!wcsicmp(name, encodings[i].name)) + { + *codepage = encodings[i].codepage; + return true; + } + } + + return false; } -static HRESULT saxreader_saxcharacters(saxlocator *locator, BSTR chars) +static bool saxreader_can_ignore_encoding(const WCHAR *encoding) { - struct saxcontenthandler_iface *content = saxreader_get_contenthandler(locator->saxreader); - HRESULT hr; - - if (!saxreader_has_handler(locator, SAXContentHandler)) return S_OK; + static const WCHAR *encodings[] = + { + L"utf-16", L"utf-8" + }; - if (locator->vbInterface) - hr = IVBSAXContentHandler_characters(content->vbhandler, &chars); - else - hr = ISAXContentHandler_characters(content->handler, chars, SysStringLen(chars)); + for (int i = 0; i < ARRAYSIZE(encodings); ++i) + { + if (!wcsicmp(encoding, encodings[i])) + return true; + } - return hr; + return false; } -/* property names */ -static const WCHAR PropertyDeclHandlerW[] = L"http://xml.org/sax/properties/declaration-handler"; -static const WCHAR PropertyDomNodeW[] = L"http://xml.org/sax/properties/dom-node"; -static const WCHAR PropertyLexicalHandlerW[] = L"http://xml.org/sax/properties/lexical-handler"; +typedef int (*p_converter)(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length); -static inline HRESULT set_feature_value(saxreader *reader, saxreader_feature feature, VARIANT_BOOL value) +static int convert_mbtowc(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) { - /* handling of non-VARIANT_* values is version dependent */ - if ((reader->version < MSXML4) && (value != VARIANT_TRUE)) - value = VARIANT_FALSE; - if ((reader->version >= MSXML4) && (value != VARIANT_FALSE)) - value = VARIANT_TRUE; - - if (value == VARIANT_TRUE) - reader->features |= feature; - else - reader->features &= ~feature; - - return S_OK; + return MultiByteToWideChar(cp, 0, src, src_size, buffer, buffer_length); } -static inline HRESULT get_feature_value(const saxreader *reader, saxreader_feature feature, VARIANT_BOOL *value) +static int convert_utf16be(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) { - *value = reader->features & feature ? VARIANT_TRUE : VARIANT_FALSE; - return S_OK; -} + size_t size = min(src_size / 2, buffer_length); -static BOOL is_namespaces_enabled(const saxreader *reader) -{ - return (reader->version < MSXML4) || (reader->features & Namespaces); + if (!buffer) return src_size / 2; + + for (int i = 0; i < size; ++i) + buffer[i] = src[i + 1] | src[i]; + + return size; } -static BSTR build_qname(BSTR prefix, BSTR local) +static unsigned int saxreader_convert_u32_to_u16(UINT32 ch, WCHAR *out) { - if (prefix && *prefix) + if (ch < 0x10000) { - BSTR qname = SysAllocStringLen(NULL, SysStringLen(prefix) + SysStringLen(local) + 1); - WCHAR *ptr; - - ptr = qname; - lstrcpyW(ptr, prefix); - ptr += SysStringLen(prefix); - *ptr++ = ':'; - lstrcpyW(ptr, local); - return qname; + out[0] = ch; + out[1] = 0; + out[2] = 0; + return 1; } else - return SysAllocString(local); + { + out[0] = ((ch - 0x10000) >> 10) + 0xd800; + out[1] = ((ch - 0x10000) & 0x3ff) + 0xdc00; + out[2] = 0; + return 2; + } } -static element_entry* alloc_element_entry(const xmlChar *local, const xmlChar *prefix, int nb_ns, - const xmlChar **namespaces) +static int convert_ucs4le(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) { - element_entry *ret; - int i; + const UINT32 *u = (const UINT32 *)src; + int size = 0, len; + WCHAR *p = buffer; - ret = malloc(sizeof(*ret)); - if (!ret) return ret; + if (src_size % 4) return 0; - ret->local = bstr_from_xmlChar(local); - ret->prefix = bstr_from_xmlChar(prefix); - ret->qname = build_qname(ret->prefix, ret->local); - ret->ns = nb_ns ? malloc(nb_ns * sizeof(ns)) : NULL; - ret->ns_count = nb_ns; - - for (i=0; i < nb_ns; i++) + if (!buffer) { - ret->ns[i].prefix = bstr_from_xmlChar(namespaces[2*i]); - ret->ns[i].uri = bstr_from_xmlChar(namespaces[2*i+1]); - } - - return ret; -} + for (int i = 0; i < src_size / 4; ++i) + { + ++size; + if (u[i] > 0xffff) ++size; + } -static void free_element_entry(element_entry *element) -{ - int i; + return size; + } - for (i=0; i<element->ns_count;i++) + while (buffer_length && src_size) { - SysFreeString(element->ns[i].prefix); - SysFreeString(element->ns[i].uri); + if (*u > 0xffff && buffer_length < 2) break; + len = saxreader_convert_u32_to_u16(*u, buffer); + buffer_length -= len; + buffer += len; + ++u; } - SysFreeString(element->prefix); - SysFreeString(element->local); - SysFreeString(element->qname); - - free(element->ns); - free(element); -} - -static void push_element_ns(saxlocator *locator, element_entry *element) -{ - list_add_head(&locator->elements, &element->entry); + return buffer - p; } -static element_entry * pop_element_ns(saxlocator *locator) +static int convert_ucs4be(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) { - element_entry *element = LIST_ENTRY(list_head(&locator->elements), element_entry, entry); + const UINT32 *u = (const UINT32 *)src; + int size = 0, len; + WCHAR *p = buffer; + UINT32 _u; - if (element) - list_remove(&element->entry); + if (src_size % 4) return 0; - return element; -} + if (!buffer) + { + for (int i = 0; i < src_size / 4; ++i) + { + ++size; + if (u[i] & 0xffff) ++size; + } -static BSTR find_element_uri(saxlocator *locator, const xmlChar *uri) -{ - element_entry *element; - BSTR uriW; - int i; + return size; + } - if (!uri) return NULL; + while (buffer_length && src_size) + { + _u = RtlUlongByteSwap(*u); + if (_u > 0xffff && buffer_length < 2) break; + len = saxreader_convert_u32_to_u16(_u, buffer); + buffer_length -= len; + buffer += len; + ++u; + } - uriW = bstr_from_xmlChar(uri); + return buffer - p; +} - LIST_FOR_EACH_ENTRY(element, &locator->elements, element_entry, entry) +static p_converter convert_get_converter(enum xmlencoding encoding) +{ + switch (encoding) { - for (i=0; i < element->ns_count; i++) - if (!wcscmp(uriW, element->ns[i].uri)) - { - SysFreeString(uriW); - return element->ns[i].uri; - } + case XML_ENCODING_UCS4BE: + return convert_ucs4be; + case XML_ENCODING_UCS4LE: + return convert_ucs4le; + case XML_ENCODING_UTF16BE: + return convert_utf16be; + case XML_ENCODING_UTF8: + return convert_mbtowc; + case XML_ENCODING_UTF16LE: + default: + ; } - SysFreeString(uriW); - ERR("namespace uri not found, %s\n", debugstr_a((char*)uri)); return NULL; } -/* used to localize version dependent error check behaviour */ -static inline BOOL sax_callback_failed(saxlocator *This, HRESULT hr) +static UINT convert_get_codepage(enum xmlencoding encoding) { - return This->saxreader->version >= MSXML4 ? FAILED(hr) : hr != S_OK; + switch (encoding) + { + case XML_ENCODING_UTF16LE: + return 1200; + case XML_ENCODING_UTF8: + return CP_UTF8; + default: + ; + } + + return 0; } -/* index value -1 means it tries to loop for a first time */ -static inline BOOL iterate_endprefix_index(saxlocator *This, const element_entry *element, int *i) +/* Get a rough estimate on raw byte size, to produce output of 'size' WCHARs. + Estimate could off in either direction, it should never be an issue if requested + chunk size is always larger than reasonable lookahead window used to detect next markup pattern. */ +static ULONG convert_estimate_raw_size(enum xmlencoding encoding, ULONG size) { - if (This->saxreader->version >= MSXML4) - { - if (*i == -1) *i = 0; else ++*i; - return *i < element->ns_count; - } - else + switch (encoding) { - if (*i == -1) *i = element->ns_count-1; else --*i; - return *i >= 0; + case XML_ENCODING_UTF16LE: + case XML_ENCODING_UTF16BE: + return size * 2; + case XML_ENCODING_UTF8: + return size * 3; + case XML_ENCODING_UCS4BE: + case XML_ENCODING_UCS4LE: + return size * 4; + default: + return 0; } } -static BOOL bstr_pool_insert(struct bstrpool *pool, BSTR pool_entry) +struct input_buffer { - if (!pool->pool) - { - pool->pool = malloc(16 * sizeof(*pool->pool)); - if (!pool->pool) - return FALSE; + struct encoded_buffer utf16; + struct encoded_buffer raw; + enum xmlencoding encoding; + bool switched_encoding; + p_converter converter; + UINT code_page; + struct text_position position; + size_t consumed; + bool last_cr; - pool->index = 0; - pool->len = 16; - } - else if (pool->index == pool->len) - { - BSTR *new_pool = realloc(pool->pool, pool->len * 2 * sizeof(*new_pool)); + unsigned int chunk_size; + struct list entities; +}; - if (!new_pool) - return FALSE; +static size_t convert_get_raw_length(struct input_buffer *buffer) +{ + const struct encoded_buffer *raw = &buffer->raw; + size_t size = raw->written; - pool->pool = new_pool; - pool->len *= 2; + if (buffer->encoding == XML_ENCODING_UTF8 && buffer->code_page == CP_UTF8) + { + /* Incomplete single byte char, look for a start byte of multibyte char. */ + if (raw->data[size-1] & 0x80) + { + while (--size && !(raw->data[size] & 0xc0)) + ; + } } - pool->pool[pool->index++] = pool_entry; - return TRUE; + return size - raw->cur; } -static void free_bstr_pool(struct bstrpool *pool) +enum saxreader_state { - unsigned int i; - - for (i = 0; i < pool->index; i++) - SysFreeString(pool->pool[i]); + SAX_PARSER_START = 0, + SAX_PARSER_MISC, + SAX_PARSER_PI, + SAX_PARSER_COMMENT, + SAX_PARSER_START_TAG, + SAX_PARSER_CONTENT, + SAX_PARSER_CDATA, + SAX_PARSER_END_TAG, + SAX_PARSER_EPILOG, + SAX_PARSER_XML_DECL, + SAX_PARSER_EOF, +}; - free(pool->pool); +struct saxlocator +{ + IVBSAXLocator IVBSAXLocator_iface; + ISAXLocator ISAXLocator_iface; + IVBSAXAttributes IVBSAXAttributes_iface; + ISAXAttributes ISAXAttributes_iface; + LONG refcount; + struct saxreader *saxreader; + HRESULT ret; + int line; + int column; + bool vbInterface; + struct list elements; + enum saxreader_state state; - pool->pool = NULL; - pool->index = pool->len = 0; -} + ISequentialStream *stream; + bool eos; -static BSTR bstr_from_xmlCharN(const xmlChar *buf, int len) -{ - DWORD dLen; - BSTR bstr; + BSTR xmlns_uri; + BSTR xml_uri; + BSTR null_uri; + struct + { + struct attribute *entries; + size_t count; + size_t capacity; + size_t *map; + } attributes; - if (!buf) - return NULL; + struct + { + struct list attr_decls; + struct list entities; + } dtd; - dLen = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, NULL, 0); - if(len != -1) dLen++; - bstr = SysAllocStringLen(NULL, dLen-1); - if (!bstr) - return NULL; - MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, bstr, dLen); - if(len != -1) bstr[dLen-1] = '\0'; + struct input_buffer buffer; + struct text_position start_document_position; + HRESULT status; +}; - return bstr; +static inline struct saxreader *impl_from_IVBSAXXMLReader(IVBSAXXMLReader *iface) +{ + return CONTAINING_RECORD(iface, struct saxreader, IVBSAXXMLReader_iface); } -static BSTR QName_from_xmlChar(const xmlChar *prefix, const xmlChar *name) +static inline struct saxreader *impl_from_ISAXXMLReader(ISAXXMLReader *iface) { - xmlChar *qname; - BSTR bstr; - - if(!name) return NULL; - - if(!prefix || !*prefix) - return bstr_from_xmlChar(name); + return CONTAINING_RECORD(iface, struct saxreader, ISAXXMLReader_iface); +} - qname = xmlBuildQName(name, prefix, NULL, 0); - bstr = bstr_from_xmlChar(qname); - xmlFree(qname); +static inline struct saxlocator *impl_from_IVBSAXLocator(IVBSAXLocator *iface) +{ + return CONTAINING_RECORD(iface, struct saxlocator, IVBSAXLocator_iface); +} - return bstr; +static inline struct saxlocator *impl_from_ISAXLocator(ISAXLocator *iface) +{ + return CONTAINING_RECORD(iface, struct saxlocator, ISAXLocator_iface); } -static BSTR pooled_bstr_from_xmlChar(struct bstrpool *pool, const xmlChar *buf) +static inline struct saxlocator *impl_from_IVBSAXAttributes(IVBSAXAttributes *iface) { - BSTR pool_entry = bstr_from_xmlChar(buf); + return CONTAINING_RECORD(iface, struct saxlocator, IVBSAXAttributes_iface); +} - if (pool_entry && !bstr_pool_insert(pool, pool_entry)) - { - SysFreeString(pool_entry); - return NULL; - } +static inline struct saxlocator *impl_from_ISAXAttributes(ISAXAttributes *iface) +{ + return CONTAINING_RECORD(iface, struct saxlocator, ISAXAttributes_iface); +} - return pool_entry; +static inline bool saxreader_has_handler(const struct saxlocator *locator, enum saxhandler_type type) +{ + struct saxanyhandler_iface *iface = &locator->saxreader->saxhandlers[type].u.anyhandler; + return (locator->vbInterface && iface->vbhandler) || (!locator->vbInterface && iface->handler); } -static BSTR pooled_bstr_from_xmlCharN(struct bstrpool *pool, const xmlChar *buf, int len) +static inline HRESULT set_feature_value(struct saxreader *reader, enum saxreader_feature feature, VARIANT_BOOL value) { - BSTR pool_entry = bstr_from_xmlCharN(buf, len); + /* handling of non-VARIANT_* values is version dependent */ + if ((reader->version < MSXML4) && (value != VARIANT_TRUE)) + value = VARIANT_FALSE; + if ((reader->version >= MSXML4) && (value != VARIANT_FALSE)) + value = VARIANT_TRUE; - if (pool_entry && !bstr_pool_insert(pool, pool_entry)) - { - SysFreeString(pool_entry); - return NULL; - } + if (value == VARIANT_TRUE) + reader->features |= feature; + else + reader->features &= ~feature; - return pool_entry; + return S_OK; } -static void format_error_message_from_id(saxlocator *This, HRESULT hr) +static inline HRESULT get_feature_value(const struct saxreader *reader, enum saxreader_feature feature, VARIANT_BOOL *value) { - struct saxerrorhandler_iface *handler = saxreader_get_errorhandler(This->saxreader); - xmlStopParser(This->pParserCtxt); - This->ret = hr; + *value = reader->features & feature ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} - if (saxreader_has_handler(This, SAXErrorHandler)) - { - WCHAR msg[1024]; - if(!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, hr, 0, msg, ARRAY_SIZE(msg), NULL)) - { - FIXME("MSXML errors not yet supported.\n"); - msg[0] = '\0'; - } +static void saxreader_set_error(struct saxlocator *locator, HRESULT status) +{ + if (locator->status == S_OK) + locator->status = status; +} - if(This->vbInterface) - { - BSTR bstrMsg = SysAllocString(msg); - IVBSAXErrorHandler_fatalError(handler->vbhandler, - &This->IVBSAXLocator_iface, &bstrMsg, hr); - SysFreeString(bstrMsg); - } - else - ISAXErrorHandler_fatalError(handler->handler, - &This->ISAXLocator_iface, msg, hr); - } +static BSTR saxreader_alloc_string(struct saxlocator *locator, const WCHAR *str) +{ + BSTR ret; + + if (!(ret = SysAllocString(str))) + saxreader_set_error(locator, E_OUTOFMEMORY); + + return ret; } -static void update_position(saxlocator *This, BOOL fix_column) +static BSTR saxreader_alloc_stringlen(struct saxlocator *locator, const WCHAR *str, UINT len) { - const xmlChar *p = This->pParserCtxt->input->cur-1; - const xmlChar *baseP = This->pParserCtxt->input->base; + BSTR ret; - This->line = xmlSAX2GetLineNumber(This->pParserCtxt); - if(fix_column) - { - This->column = 1; - for(;p>=baseP && *p!='\n' && *p!='\r'; p--) - This->column++; - } - else + if (!(ret = SysAllocStringLen(str, len))) + saxreader_set_error(locator, E_OUTOFMEMORY); + + return ret; +} + +static bool is_namespaces_enabled(const struct saxreader *reader) +{ + return (reader->version < MSXML4) || (reader->features & Namespaces); +} + +static void free_element_entry(element_entry *element) +{ + int i; + + for (i=0; i<element->ns_count;i++) { - This->column = xmlSAX2GetColumnNumber(This->pParserCtxt); + SysFreeString(element->ns[i].prefix); + SysFreeString(element->ns[i].uri); } + + SysFreeString(element->prefix); + SysFreeString(element->local); + SysFreeString(element->qname); + + free(element->ns); + free(element); } -/*** IVBSAXAttributes interface ***/ -static HRESULT WINAPI ivbsaxattributes_QueryInterface( - IVBSAXAttributes* iface, - REFIID riid, - void **ppvObject) +static HRESULT WINAPI ivbsaxattributes_QueryInterface(IVBSAXAttributes *iface, REFIID riid, void **obj) { - saxlocator *This = impl_from_IVBSAXAttributes(iface); - TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); - return IVBSAXLocator_QueryInterface(&This->IVBSAXLocator_iface, riid, ppvObject); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + return IVBSAXLocator_QueryInterface(&locator->IVBSAXLocator_iface, riid, obj); } static ULONG WINAPI ivbsaxattributes_AddRef(IVBSAXAttributes* iface) { - saxlocator *This = impl_from_IVBSAXAttributes(iface); - return IVBSAXLocator_AddRef(&This->IVBSAXLocator_iface); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + return IVBSAXLocator_AddRef(&locator->IVBSAXLocator_iface); } static ULONG WINAPI ivbsaxattributes_Release(IVBSAXAttributes* iface) { - saxlocator *This = impl_from_IVBSAXAttributes(iface); - return IVBSAXLocator_Release(&This->IVBSAXLocator_iface); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + return IVBSAXLocator_Release(&locator->IVBSAXLocator_iface); } -static HRESULT WINAPI ivbsaxattributes_GetTypeInfoCount( IVBSAXAttributes *iface, UINT* pctinfo ) +static HRESULT WINAPI ivbsaxattributes_GetTypeInfoCount(IVBSAXAttributes *iface, UINT *count) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - - TRACE("(%p)->(%p)\n", This, pctinfo); + TRACE("%p, %p.\n", iface, count); - *pctinfo = 1; + *count = 1; return S_OK; } -static HRESULT WINAPI ivbsaxattributes_GetTypeInfo( - IVBSAXAttributes *iface, - UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo ) +static HRESULT WINAPI ivbsaxattributes_GetTypeInfo(IVBSAXAttributes *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) { TRACE("%p, %u, %lx, %p.\n", iface, iTInfo, lcid, ppTInfo); return get_typeinfo(IVBSAXAttributes_tid, ppTInfo); } -static HRESULT WINAPI ivbsaxattributes_GetIDsOfNames( - IVBSAXAttributes *iface, - REFIID riid, - LPOLESTR* rgszNames, - UINT cNames, - LCID lcid, - DISPID* rgDispId) +static HRESULT WINAPI ivbsaxattributes_GetIDsOfNames(IVBSAXAttributes *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { ITypeInfo *typeinfo; HRESULT hr; @@ -702,274 +1033,252 @@ static HRESULT WINAPI ivbsaxattributes_GetIDsOfNames( return hr; } -static HRESULT WINAPI ivbsaxattributes_Invoke( - IVBSAXAttributes *iface, - DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS* pDispParams, - VARIANT* pVarResult, - EXCEPINFO* pExcepInfo, - UINT* puArgErr) +static HRESULT WINAPI ivbsaxattributes_Invoke(IVBSAXAttributes *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD flags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *ei, UINT *arg_err) { ITypeInfo *typeinfo; HRESULT hr; TRACE("%p, %ld, %s, %lx, %d, %p, %p, %p, %p.\n", iface, dispIdMember, debugstr_guid(riid), - lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + lcid, flags, pDispParams, pVarResult, ei, arg_err); hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo); - if(SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { - hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, flags, pDispParams, pVarResult, ei, arg_err); ITypeInfo_Release(typeinfo); } return hr; } -/*** IVBSAXAttributes methods ***/ -static HRESULT WINAPI ivbsaxattributes_get_length( - IVBSAXAttributes* iface, - int *nLength) +static HRESULT WINAPI ivbsaxattributes_get_length(IVBSAXAttributes *iface, int *length) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - return ISAXAttributes_getLength(&This->ISAXAttributes_iface, nLength); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + + TRACE("%p, %p.\n", iface, length); + + *length = locator->attributes.count; + + return S_OK; } -static HRESULT WINAPI ivbsaxattributes_getURI( - IVBSAXAttributes* iface, - int nIndex, - BSTR *uri) +static const struct attribute *saxlocator_get_attribute(const struct saxlocator *locator, int index) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *uriW; - HRESULT hr; - int len; + if (index >= locator->attributes.count || index < 0) return NULL; + if (locator->attributes.map) + return &locator->attributes.entries[locator->attributes.map[index]]; + return &locator->attributes.entries[index]; +} + +static const struct attribute *saxlocator_get_attribute_by_name(const struct saxlocator *locator, + const WCHAR *uri, int uri_length, const WCHAR *local, int local_length, int *index) +{ + const struct attribute *attr; + + for (int i = 0; i < locator->attributes.count; ++i) + { + attr = saxlocator_get_attribute(locator, i); + + if (uri_length != SysStringLen(attr->uri) || local_length != SysStringLen(attr->local)) + continue; + + if (uri_length && memcmp(uri, attr->uri, sizeof(WCHAR) * uri_length)) + continue; + + if (local_length && memcmp(local, attr->local, sizeof(WCHAR) * local_length)) + continue; + + *index = i; + return attr; + } + + return NULL; +} + +static const struct attribute *saxlocator_get_attribute_by_qname(const struct saxlocator *locator, const WCHAR *name, + int length, int *index) +{ + const struct attribute *attr; + + for (int i = 0; i < locator->attributes.count; ++i) + { + attr = saxlocator_get_attribute(locator, i); + + if (length != SysStringLen(attr->qname)) continue; + if (memcmp(name, attr->qname, sizeof(WCHAR) * length)) continue; + + *index = i; + return attr; + } + + return NULL; +} - TRACE("(%p)->(%d %p)\n", This, nIndex, uri); + +static HRESULT WINAPI ivbsaxattributes_getURI(IVBSAXAttributes *iface, int index, BSTR *uri) +{ + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; + + TRACE("%p, %d, %p.\n", iface, index, uri); if (!uri) return E_POINTER; *uri = NULL; - hr = ISAXAttributes_getURI(&This->ISAXAttributes_iface, nIndex, &uriW, &len); - if (FAILED(hr)) - return hr; - return return_bstrn(uriW, len, uri); + if (!(attr = saxlocator_get_attribute(locator, index))) + return E_INVALIDARG; + + return return_bstrn(attr->uri, SysStringLen(attr->uri), uri); } -static HRESULT WINAPI ivbsaxattributes_getLocalName( - IVBSAXAttributes* iface, - int nIndex, - BSTR *name) +static HRESULT WINAPI ivbsaxattributes_getLocalName(IVBSAXAttributes *iface, int index, BSTR *name) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *nameW; - HRESULT hr; - int len; + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; - TRACE("(%p)->(%d %p)\n", This, nIndex, name); + TRACE("%p, %d, %p.\n", iface, index, name); if (!name) return E_POINTER; *name = NULL; - hr = ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, nIndex, &nameW, &len); - if (FAILED(hr)) - return hr; - return return_bstrn(nameW, len, name); + if (!(attr = saxlocator_get_attribute(locator, index))) + return E_INVALIDARG; + + return return_bstrn(attr->local, SysStringLen(attr->local), name); } -static HRESULT WINAPI ivbsaxattributes_getQName( - IVBSAXAttributes* iface, - int nIndex, - BSTR *QName) +static HRESULT WINAPI ivbsaxattributes_getQName(IVBSAXAttributes *iface, int index, BSTR *name) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *nameW; - HRESULT hr; - int len; + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; - TRACE("(%p)->(%d %p)\n", This, nIndex, QName); + TRACE("%p, %d, %p.\n", iface, index, name); - if (!QName) + if (!name) return E_POINTER; - *QName = NULL; - hr = ISAXAttributes_getQName(&This->ISAXAttributes_iface, nIndex, &nameW, &len); - if (FAILED(hr)) - return hr; + *name = NULL; + + if (!(attr = saxlocator_get_attribute(locator, index))) + return E_INVALIDARG; - return return_bstrn(nameW, len, QName); + return return_bstrn(attr->qname, SysStringLen(attr->qname), name); } -static HRESULT WINAPI ivbsaxattributes_getIndexFromName( - IVBSAXAttributes* iface, - BSTR uri, - BSTR localName, - int *index) +static HRESULT WINAPI ivbsaxattributes_getIndexFromName(IVBSAXAttributes *iface, BSTR uri, + BSTR name, int *index) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - return ISAXAttributes_getIndexFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), - localName, SysStringLen(localName), index); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; + + attr = saxlocator_get_attribute_by_name(locator, uri, SysStringLen(uri), name, SysStringLen(name), index); + + return attr ? S_OK : E_INVALIDARG; } -static HRESULT WINAPI ivbsaxattributes_getIndexFromQName( - IVBSAXAttributes* iface, - BSTR QName, - int *index) +static HRESULT WINAPI ivbsaxattributes_getIndexFromQName(IVBSAXAttributes *iface, BSTR name, int *index) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - return ISAXAttributes_getIndexFromQName(&This->ISAXAttributes_iface, QName, - SysStringLen(QName), index); + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; + + if (!name || !index) return E_POINTER; + + if (!(attr = saxlocator_get_attribute_by_qname(locator, name, SysStringLen(name), index))) + return E_INVALIDARG; + + return S_OK; } -static HRESULT WINAPI ivbsaxattributes_getType( - IVBSAXAttributes* iface, - int nIndex, - BSTR *type) +static HRESULT WINAPI ivbsaxattributes_getType(IVBSAXAttributes *iface, int index, BSTR *type) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *typeW; - HRESULT hr; - int len; - - TRACE("(%p)->(%d %p)\n", This, nIndex, type); + FIXME("%p, %d, %p stub\n", iface, index, type); if (!type) return E_POINTER; - *type = NULL; - hr = ISAXAttributes_getType(&This->ISAXAttributes_iface, nIndex, &typeW, &len); - if (FAILED(hr)) - return hr; - - return return_bstrn(typeW, len, type); + return E_NOTIMPL; } -static HRESULT WINAPI ivbsaxattributes_getTypeFromName( - IVBSAXAttributes* iface, - BSTR uri, - BSTR localName, - BSTR *type) +static HRESULT WINAPI ivbsaxattributes_getTypeFromName(IVBSAXAttributes *iface, BSTR uri, BSTR name, BSTR *type) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *typeW; - HRESULT hr; - int len; - - TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(localName), type); + FIXME("%p, %s, %s, %p stub\n", iface, debugstr_w(uri), debugstr_w(name), type); if (!type) return E_POINTER; - *type = NULL; - hr = ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), - localName, SysStringLen(localName), &typeW, &len); - if (FAILED(hr)) - return hr; - - return return_bstrn(typeW, len, type); + return E_NOTIMPL; } -static HRESULT WINAPI ivbsaxattributes_getTypeFromQName( - IVBSAXAttributes* iface, - BSTR QName, - BSTR *type) +static HRESULT WINAPI ivbsaxattributes_getTypeFromQName(IVBSAXAttributes *iface, BSTR name, BSTR *type) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *typeW; - HRESULT hr; - int len; - - TRACE("(%p)->(%s %p)\n", This, debugstr_w(QName), type); + FIXME("%p, %s, %p stub\n", iface, debugstr_w(name), type); if (!type) return E_POINTER; - *type = NULL; - hr = ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, QName, SysStringLen(QName), - &typeW, &len); - if (FAILED(hr)) - return hr; - - return return_bstrn(typeW, len, type); + return E_NOTIMPL; } -static HRESULT WINAPI ivbsaxattributes_getValue( - IVBSAXAttributes* iface, - int nIndex, - BSTR *value) +static HRESULT WINAPI ivbsaxattributes_getValue(IVBSAXAttributes *iface, int index, BSTR *value) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *valueW; - HRESULT hr; - int len; + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; - TRACE("(%p)->(%d %p)\n", This, nIndex, value); + TRACE("%p, %d, %p.\n", iface, index, value); if (!value) return E_POINTER; *value = NULL; - hr = ISAXAttributes_getValue(&This->ISAXAttributes_iface, nIndex, &valueW, &len); - if (FAILED(hr)) - return hr; - return return_bstrn(valueW, len, value); + if (!(attr = saxlocator_get_attribute(locator, index))) + return E_INVALIDARG; + + return return_bstrn(attr->value, SysStringLen(attr->value), value); } -static HRESULT WINAPI ivbsaxattributes_getValueFromName( - IVBSAXAttributes* iface, - BSTR uri, - BSTR localName, - BSTR *value) +static HRESULT WINAPI ivbsaxattributes_getValueFromName(IVBSAXAttributes *iface, BSTR uri, BSTR name, BSTR *value) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *valueW; - HRESULT hr; - int len; + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; + int index; - TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(uri), debugstr_w(localName), value); + TRACE("%p, %s, %s, %p.\n", iface, debugstr_w(uri), debugstr_w(name), value); if (!value) return E_POINTER; *value = NULL; - hr = ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), - localName, SysStringLen(localName), &valueW, &len); - if (FAILED(hr)) - return hr; - return return_bstrn(valueW, len, value); + if (!(attr = saxlocator_get_attribute_by_name(locator, uri, SysStringLen(uri), name, SysStringLen(name), &index))) + return E_INVALIDARG; + + return return_bstrn(attr->value, SysStringLen(attr->value), value); } -static HRESULT WINAPI ivbsaxattributes_getValueFromQName( - IVBSAXAttributes* iface, - BSTR QName, - BSTR *value) +static HRESULT WINAPI ivbsaxattributes_getValueFromQName(IVBSAXAttributes *iface, BSTR name, BSTR *value) { - saxlocator *This = impl_from_IVBSAXAttributes( iface ); - const WCHAR *valueW; - HRESULT hr; - int len; + struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); + const struct attribute *attr; + int index; - TRACE("(%p)->(%s %p)\n", This, debugstr_w(QName), value); + TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); if (!value) return E_POINTER; *value = NULL; - hr = ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, QName, - SysStringLen(QName), &valueW, &len); - if (FAILED(hr)) - return hr; - return return_bstrn(valueW, len, value); + if (!(attr = saxlocator_get_attribute_by_qname(locator, name, SysStringLen(name), &index))) + return E_INVALIDARG; + + return return_bstrn(attr->value, SysStringLen(attr->value), value); } static const struct IVBSAXAttributesVtbl ivbsaxattributes_vtbl = @@ -995,293 +1304,210 @@ static const struct IVBSAXAttributesVtbl ivbsaxattributes_vtbl = ivbsaxattributes_getValueFromQName }; -/*** ISAXAttributes interface ***/ -/*** IUnknown methods ***/ -static HRESULT WINAPI isaxattributes_QueryInterface( - ISAXAttributes* iface, - REFIID riid, - void **ppvObject) +static HRESULT WINAPI isaxattributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **obj) { - saxlocator *This = impl_from_ISAXAttributes(iface); - TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); - return ISAXLocator_QueryInterface(&This->ISAXLocator_iface, riid, ppvObject); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + return ISAXLocator_QueryInterface(&locator->ISAXLocator_iface, riid, obj); } -static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface) +static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes *iface) { - saxlocator *This = impl_from_ISAXAttributes(iface); - TRACE("%p\n", This); - return ISAXLocator_AddRef(&This->ISAXLocator_iface); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + return ISAXLocator_AddRef(&locator->ISAXLocator_iface); } -static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface) +static ULONG WINAPI isaxattributes_Release(ISAXAttributes *iface) { - saxlocator *This = impl_from_ISAXAttributes(iface); - - TRACE("%p\n", This); - return ISAXLocator_Release(&This->ISAXLocator_iface); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + return ISAXLocator_Release(&locator->ISAXLocator_iface); } /*** ISAXAttributes methods ***/ -static HRESULT WINAPI isaxattributes_getLength( - ISAXAttributes* iface, - int *length) +static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes *iface, int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); - *length = This->attr_count; - TRACE("Length set to %d\n", *length); - return S_OK; -} + TRACE("%p, %p.\n", iface, length); -static inline BOOL is_valid_attr_index(const saxlocator *locator, int index) -{ - return index < locator->attr_count && index >= 0; + *length = locator->attributes.count; + + return S_OK; } -static HRESULT WINAPI isaxattributes_getURI( - ISAXAttributes* iface, - int index, - const WCHAR **url, - int *size) +static HRESULT saxlocator_return_string(BSTR str, const WCHAR **p, int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%d)\n", This, index); - - if(!is_valid_attr_index(This, index)) return E_INVALIDARG; - if(!url || !size) return E_POINTER; + if (!p || !length) + return E_POINTER; - *size = SysStringLen(This->attributes[index].szURI); - *url = This->attributes[index].szURI; + *length = SysStringLen(str); + *p = str; - TRACE("(%s:%d)\n", debugstr_w(This->attributes[index].szURI), *size); + TRACE("%s:%d\n", debugstr_w(str), *length); return S_OK; } -static HRESULT WINAPI isaxattributes_getLocalName( - ISAXAttributes* iface, - int index, - const WCHAR **pLocalName, - int *pLocalNameLength) +static HRESULT WINAPI isaxattributes_getURI(ISAXAttributes *iface, int index, + const WCHAR **url, int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%d)\n", This, index); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - if(!is_valid_attr_index(This, index)) return E_INVALIDARG; - if(!pLocalName || !pLocalNameLength) return E_POINTER; + TRACE("%p, %d, %p, %p.\n", iface, index, url, length); - *pLocalNameLength = SysStringLen(This->attributes[index].szLocalname); - *pLocalName = This->attributes[index].szLocalname; - - return S_OK; + if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; + return saxlocator_return_string(attr->uri, url, length); } -static HRESULT WINAPI isaxattributes_getQName( - ISAXAttributes* iface, - int index, - const WCHAR **pQName, - int *pQNameLength) +static HRESULT WINAPI isaxattributes_getLocalName(ISAXAttributes *iface, int index, + const WCHAR **name, int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%d)\n", This, index); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - if(!is_valid_attr_index(This, index)) return E_INVALIDARG; - if(!pQName || !pQNameLength) return E_POINTER; + TRACE("%p, %d, %p, %p.\n", iface, index, name, length); - *pQNameLength = SysStringLen(This->attributes[index].szQName); - *pQName = This->attributes[index].szQName; + if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; + return saxlocator_return_string(attr->local, name, length); +} - return S_OK; +static HRESULT WINAPI isaxattributes_getQName(ISAXAttributes *iface, int index, + const WCHAR **name, int *length) +{ + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; + + TRACE("%p, %d, %p, %p.\n", iface, index, name, length); + + if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; + return saxlocator_return_string(attr->qname, name, length); } -static HRESULT WINAPI isaxattributes_getName( - ISAXAttributes* iface, - int index, - const WCHAR **uri, - int *pUriLength, - const WCHAR **localName, - int *pLocalNameSize, - const WCHAR **QName, - int *pQNameLength) +static HRESULT WINAPI isaxattributes_getName(ISAXAttributes *iface, int index, + const WCHAR **uri, int *uri_length, const WCHAR **local, int *local_length, + const WCHAR **qname, int *qname_length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%d)\n", This, index); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - if(!is_valid_attr_index(This, index)) return E_INVALIDARG; - if(!uri || !pUriLength || !localName || !pLocalNameSize - || !QName || !pQNameLength) return E_POINTER; + TRACE("%p, %d, %p, %p, %p, %p, %p, %p.\n", iface, index, uri, uri_length, local, local_length, + qname, qname_length); - *pUriLength = SysStringLen(This->attributes[index].szURI); - *uri = This->attributes[index].szURI; - *pLocalNameSize = SysStringLen(This->attributes[index].szLocalname); - *localName = This->attributes[index].szLocalname; - *pQNameLength = SysStringLen(This->attributes[index].szQName); - *QName = This->attributes[index].szQName; + if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; + if (!uri || !uri_length || !local || !local_length || !qname || !qname_length) return E_POINTER; - TRACE("(%s, %s, %s)\n", debugstr_w(*uri), debugstr_w(*localName), debugstr_w(*QName)); + saxlocator_return_string(attr->uri, uri, uri_length); + saxlocator_return_string(attr->local, local, local_length); + saxlocator_return_string(attr->qname, qname, qname_length); return S_OK; } -static HRESULT WINAPI isaxattributes_getIndexFromName( - ISAXAttributes* iface, - const WCHAR *pUri, - int cUriLength, - const WCHAR *pLocalName, - int cocalNameLength, - int *index) +static HRESULT WINAPI isaxattributes_getIndexFromName(ISAXAttributes *iface, const WCHAR *uri, + int uri_length, const WCHAR *local, int local_length, int *index) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - int i; - TRACE("(%p)->(%s, %d, %s, %d)\n", This, debugstr_w(pUri), cUriLength, - debugstr_w(pLocalName), cocalNameLength); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - if(!pUri || !pLocalName || !index) return E_POINTER; + TRACE("%p, %s, %d, %s, %d, %p.\n", iface, debugstr_w(uri), uri_length, debugstr_w(local), local_length, index); - for(i=0; i<This->attr_count; i++) - { - if(cUriLength!=SysStringLen(This->attributes[i].szURI) - || cocalNameLength!=SysStringLen(This->attributes[i].szLocalname)) - continue; - if(cUriLength && memcmp(pUri, This->attributes[i].szURI, - sizeof(WCHAR)*cUriLength)) - continue; - if(cocalNameLength && memcmp(pLocalName, This->attributes[i].szLocalname, - sizeof(WCHAR)*cocalNameLength)) - continue; + if (!uri || !local || !index) + return E_POINTER; - *index = i; - return S_OK; - } + attr = saxlocator_get_attribute_by_name(locator, uri, uri_length, local, local_length, index); - return E_INVALIDARG; + return attr ? S_OK : E_INVALIDARG; } -static HRESULT WINAPI isaxattributes_getIndexFromQName( - ISAXAttributes* iface, - const WCHAR *pQName, - int nQNameLength, - int *index) +static HRESULT WINAPI isaxattributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR *name, int length, int *index) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - int i; - TRACE("(%p)->(%s, %d)\n", This, debugstr_w(pQName), nQNameLength); + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - if(!pQName || !index) return E_POINTER; - if(!nQNameLength) return E_INVALIDARG; + TRACE("%p, %s, %d, %p.\n", iface, debugstr_w(name), length, index); - for(i=0; i<This->attr_count; i++) - { - if(nQNameLength!=SysStringLen(This->attributes[i].szQName)) continue; - if(memcmp(pQName, This->attributes[i].szQName, sizeof(WCHAR)*nQNameLength)) continue; + if (!name || !index) return E_POINTER; + if (!length) return E_INVALIDARG; - *index = i; - return S_OK; - } + attr = saxlocator_get_attribute_by_qname(locator, name, length, index); - return E_INVALIDARG; + return attr ? S_OK : E_INVALIDARG; } -static HRESULT WINAPI isaxattributes_getType( - ISAXAttributes* iface, - int nIndex, - const WCHAR **pType, - int *pTypeLength) +static HRESULT WINAPI isaxattributes_getType(ISAXAttributes *iface, int index, const WCHAR **type, + int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); + FIXME("%p, %d, %p, %p stub\n", iface, index, type, length); - FIXME("(%p)->(%d) stub\n", This, nIndex); return E_NOTIMPL; } -static HRESULT WINAPI isaxattributes_getTypeFromName( - ISAXAttributes* iface, - const WCHAR *pUri, - int nUri, - const WCHAR *pLocalName, - int nLocalName, - const WCHAR **pType, - int *nType) +static HRESULT WINAPI isaxattributes_getTypeFromName(ISAXAttributes *iface, const WCHAR *uri, + int uri_length, const WCHAR *name, int name_length, const WCHAR **type, int *type_length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); + FIXME("%p, %s, %d, %s, %d, %p, %p stub\n", iface, debugstr_w(uri), uri_length, debugstr_w(name), + name_length, type, type_length); - FIXME("(%p)->(%s, %d, %s, %d) stub\n", This, debugstr_w(pUri), nUri, - debugstr_w(pLocalName), nLocalName); return E_NOTIMPL; } -static HRESULT WINAPI isaxattributes_getTypeFromQName( - ISAXAttributes* iface, - const WCHAR *pQName, - int nQName, - const WCHAR **pType, - int *nType) +static HRESULT WINAPI isaxattributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR *name, + int name_length, const WCHAR **type, int *type_length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); + FIXME("%p, %s, %d, %p, %p stub\n", iface, debugstr_w(name), name_length, type, type_length); - FIXME("(%p)->(%s, %d) stub\n", This, debugstr_w(pQName), nQName); return E_NOTIMPL; } -static HRESULT WINAPI isaxattributes_getValue( - ISAXAttributes* iface, - int index, - const WCHAR **value, - int *nValue) +static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes *iface, int index, const WCHAR **value, + int *length) { - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%d)\n", This, index); - - if(!is_valid_attr_index(This, index)) return E_INVALIDARG; - if(!value || !nValue) return E_POINTER; + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; - *nValue = SysStringLen(This->attributes[index].szValue); - *value = This->attributes[index].szValue; + TRACE("%p, %d, %p, %p.\n", iface, index, value, length); - TRACE("(%s:%d)\n", debugstr_w(*value), *nValue); - - return S_OK; + if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; + return saxlocator_return_string(attr->value, value, length); } -static HRESULT WINAPI isaxattributes_getValueFromName( - ISAXAttributes* iface, - const WCHAR *pUri, - int nUri, - const WCHAR *pLocalName, - int nLocalName, - const WCHAR **pValue, - int *nValue) +static HRESULT WINAPI isaxattributes_getValueFromName(ISAXAttributes *iface, const WCHAR *uri, + int uri_length, const WCHAR *name, int name_length, const WCHAR **value, int *length) { - HRESULT hr; + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; int index; - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%s, %d, %s, %d)\n", This, debugstr_w(pUri), nUri, - debugstr_w(pLocalName), nLocalName); - hr = ISAXAttributes_getIndexFromName(iface, - pUri, nUri, pLocalName, nLocalName, &index); - if(hr==S_OK) hr = ISAXAttributes_getValue(iface, index, pValue, nValue); + TRACE("%p, %s, %d, %s, %d, %p, %p.\n", iface, debugstr_w(uri), uri_length, + debugstr_w(name), name_length, value, length); - return hr; + if (!uri || !name) + return E_POINTER; + + attr = saxlocator_get_attribute_by_name(locator, uri, uri_length, name, name_length, &index); + if (attr) + return saxlocator_return_string(attr->value, value, length); + + return E_INVALIDARG; } -static HRESULT WINAPI isaxattributes_getValueFromQName( - ISAXAttributes* iface, - const WCHAR *pQName, - int nQName, - const WCHAR **pValue, - int *nValue) +static HRESULT WINAPI isaxattributes_getValueFromQName(ISAXAttributes *iface, + const WCHAR *name, int name_length, const WCHAR **value, int *length) { - HRESULT hr; + struct saxlocator *locator = impl_from_ISAXAttributes(iface); + const struct attribute *attr; int index; - saxlocator *This = impl_from_ISAXAttributes( iface ); - TRACE("(%p)->(%s, %d)\n", This, debugstr_w(pQName), nQName); - hr = ISAXAttributes_getIndexFromQName(iface, pQName, nQName, &index); - if(hr==S_OK) hr = ISAXAttributes_getValue(iface, index, pValue, nValue); + TRACE("%p, %s, %d, %p, %p.\n", iface, debugstr_w(name), name_length, value, length); - return hr; + if (!name) + return E_POINTER; + + attr = saxlocator_get_attribute_by_qname(locator, name, name_length, &index); + if (attr) + return saxlocator_return_string(attr->value, value, length); + + return E_INVALIDARG; } static const struct ISAXAttributesVtbl isaxattributes_vtbl = @@ -1304,1373 +1530,3921 @@ static const struct ISAXAttributesVtbl isaxattributes_vtbl = isaxattributes_getValueFromQName }; -/* Libxml2 escapes '&' back to char reference '&' in attribute value, - so when document has escaped value with '&' it's parsed to '&' and then - escaped to '&'. This function takes care of ampersands only. */ -static BSTR saxreader_get_unescaped_value(const xmlChar *buf, int len) +static void saxreader_free_bstr(BSTR *str) { - static const WCHAR ampescW[] = L"&"; - WCHAR *dest, *ptrW, *str; - DWORD str_len; - BSTR bstr; - - if (!buf) - return NULL; - - str_len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, NULL, 0); - if (len != -1) str_len++; - - str = malloc(str_len * sizeof(WCHAR)); - if (!str) return NULL; - - MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, str, str_len); - if (len != -1) str[str_len-1] = 0; - - ptrW = str; - while ((dest = wcsstr(ptrW, ampescW))) - { - WCHAR *src; - - /* leave first '&' from a reference as a value */ - src = dest + ARRAY_SIZE(ampescW) - 1; - dest++; - - /* move together with null terminator */ - memmove(dest, src, (lstrlenW(src) + 1)*sizeof(WCHAR)); - - ptrW++; - } - - bstr = SysAllocString(str); - free(str); - - return bstr; + SysFreeString(*str); + *str = NULL; } -static void free_attribute_values(saxlocator *locator) +static void saxreader_clear_attributes(struct saxlocator *locator) { - int i; - - for (i = 0; i < locator->attr_count; i++) + for (size_t i = 0; i < locator->attributes.count; ++i) { - SysFreeString(locator->attributes[i].szLocalname); - locator->attributes[i].szLocalname = NULL; + struct attribute *attr = &locator->attributes.entries[i]; - SysFreeString(locator->attributes[i].szValue); - locator->attributes[i].szValue = NULL; - - SysFreeString(locator->attributes[i].szQName); - locator->attributes[i].szQName = NULL; + saxreader_free_bstr(&attr->local); + saxreader_free_bstr(&attr->value); + saxreader_free_bstr(&attr->qname); + saxreader_free_bstr(&attr->prefix); } + + free(locator->attributes.map); + locator->attributes.map = NULL; + locator->attributes.count = 0; } -static HRESULT SAXAttributes_populate(saxlocator *locator, - int nb_namespaces, const xmlChar **xmlNamespaces, - int nb_attributes, const xmlChar **xmlAttributes) +static HRESULT WINAPI ivbsaxlocator_QueryInterface(IVBSAXLocator *iface, REFIID riid, void **obj) { - static const xmlChar xmlns[] = "xmlns"; + struct saxlocator *locator = impl_from_IVBSAXLocator(iface); - struct _attributes *attrs; - int i; + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - /* skip namespace definitions */ - if ((locator->saxreader->features & NamespacePrefixes) == 0) - nb_namespaces = 0; + *obj = NULL; - locator->attr_count = nb_namespaces + nb_attributes; - if(locator->attr_count > locator->attr_alloc_count) + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IDispatch) || + IsEqualGUID(riid, &IID_IVBSAXLocator)) { - int new_size = locator->attr_count * 2; - attrs = realloc(locator->attributes, new_size * sizeof(*locator->attributes)); - if(!attrs) - { - free_attribute_values(locator); - locator->attr_count = 0; - return E_OUTOFMEMORY; - } - memset(attrs + locator->attr_alloc_count, 0, - (new_size - locator->attr_alloc_count) * sizeof(*locator->attributes)); - locator->attributes = attrs; - locator->attr_alloc_count = new_size; + *obj = iface; } - else + else if (IsEqualGUID(riid, &IID_IVBSAXAttributes)) { - attrs = locator->attributes; + *obj = &locator->IVBSAXAttributes_iface; } - - for (i = 0; i < nb_namespaces; i++) + else { - SysFreeString(attrs[nb_attributes+i].szLocalname); - attrs[nb_attributes+i].szLocalname = SysAllocStringLen(NULL, 0); + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } - attrs[nb_attributes+i].szURI = locator->namespaceUri; + IVBSAXLocator_AddRef(iface); - SysFreeString(attrs[nb_attributes+i].szValue); - attrs[nb_attributes+i].szValue = bstr_from_xmlChar(xmlNamespaces[2*i+1]); + return S_OK; +} - SysFreeString(attrs[nb_attributes+i].szQName); - if(!xmlNamespaces[2*i]) - attrs[nb_attributes+i].szQName = SysAllocString(L"xmlns"); - else - attrs[nb_attributes+i].szQName = QName_from_xmlChar(xmlns, xmlNamespaces[2*i]); - } +static ULONG WINAPI ivbsaxlocator_AddRef(IVBSAXLocator *iface) +{ + struct saxlocator *locator = impl_from_IVBSAXLocator(iface); + return ISAXLocator_AddRef(&locator->ISAXLocator_iface); +} - for (i = 0; i < nb_attributes; i++) - { - static const xmlChar xmlA[] = "xml"; +static ULONG WINAPI ivbsaxlocator_Release(IVBSAXLocator *iface) +{ + struct saxlocator *locator = impl_from_IVBSAXLocator(iface); + return ISAXLocator_Release(&locator->ISAXLocator_iface); +} - if (xmlStrEqual(xmlAttributes[i*5+1], xmlA)) - attrs[i].szURI = bstr_from_xmlChar(xmlAttributes[i*5+2]); - else - /* that's an important feature to keep same uri pointer for every reported attribute */ - attrs[i].szURI = find_element_uri(locator, xmlAttributes[i*5+2]); +static HRESULT WINAPI ivbsaxlocator_GetTypeInfoCount(IVBSAXLocator *iface, UINT *count) +{ + TRACE("%p, %p.\n", iface, count); - SysFreeString(attrs[i].szLocalname); - attrs[i].szLocalname = bstr_from_xmlChar(xmlAttributes[i*5]); + *count = 1; - SysFreeString(attrs[i].szValue); - attrs[i].szValue = saxreader_get_unescaped_value(xmlAttributes[i*5+3], xmlAttributes[i*5+4]-xmlAttributes[i*5+3]); + return S_OK; +} - SysFreeString(attrs[i].szQName); - attrs[i].szQName = QName_from_xmlChar(xmlAttributes[i*5+1], xmlAttributes[i*5]); - } +static HRESULT WINAPI ivbsaxlocator_GetTypeInfo(IVBSAXLocator *iface, UINT index, LCID lcid, ITypeInfo **ti) +{ + TRACE("%p, %u, %lx, %p.\n", iface, index, lcid, ti); - return S_OK; + return get_typeinfo(IVBSAXLocator_tid, ti); } -/*** LibXML callbacks ***/ -static void libxmlStartDocument(void *ctx) +static HRESULT WINAPI ivbsaxlocator_GetIDsOfNames(IVBSAXLocator *iface, REFIID riid, LPOLESTR *names, + UINT count, LCID lcid, DISPID *dispid) { - saxlocator *This = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); + ITypeInfo *typeinfo; HRESULT hr; - if (This->saxreader->version >= MSXML4) - { - const xmlChar *p = This->pParserCtxt->input->cur-1; - update_position(This, FALSE); - while(p>This->pParserCtxt->input->base && *p!='>') - { - if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) - This->line--; - p--; + TRACE("%p, %s, %p, %u, %lx, %p.\n", iface, debugstr_guid(riid), names, count, lcid, dispid); + + if (!names || !count || !dispid) + return E_INVALIDARG; + + hr = get_typeinfo(IVBSAXLocator_tid, &typeinfo); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, dispid); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ivbsaxlocator_Invoke(IVBSAXLocator *iface, DISPID dispid, REFIID riid, + LCID lcid, WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *ei, UINT *argerr) +{ + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p, %ld, %s, %lx, %d, %p, %p, %p, %p.\n", iface, dispid, debugstr_guid(riid), + lcid, flags, params, result, ei, argerr); + + hr = get_typeinfo(IVBSAXLocator_tid, &typeinfo); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke(typeinfo, iface, dispid, flags, params, result, ei, argerr); + ITypeInfo_Release(typeinfo); + } + + return hr; +} + +static HRESULT WINAPI ivbsaxlocator_get_columnNumber(IVBSAXLocator *iface, int *column) +{ + struct saxlocator *locator = impl_from_IVBSAXLocator(iface); + return ISAXLocator_getColumnNumber(&locator->ISAXLocator_iface, column); +} + +static HRESULT WINAPI ivbsaxlocator_get_lineNumber(IVBSAXLocator *iface, int *line) +{ + struct saxlocator *locator = impl_from_IVBSAXLocator(iface); + return ISAXLocator_getLineNumber(&locator->ISAXLocator_iface, line); +} + +static HRESULT WINAPI ivbsaxlocator_get_publicId(IVBSAXLocator *iface, BSTR *ret) +{ + FIXME("%p, %p stub.\n", iface, ret); + + if (!ret) + return E_POINTER; + + *ret = NULL; + + return S_OK; +} + +static HRESULT WINAPI ivbsaxlocator_get_systemId(IVBSAXLocator *iface, BSTR *ret) +{ + FIXME("%p, %p.\n", iface, ret); + + if (!ret) + return E_POINTER; + + *ret = NULL; + + return S_OK; +} + +static const struct IVBSAXLocatorVtbl VBSAXLocatorVtbl = +{ + ivbsaxlocator_QueryInterface, + ivbsaxlocator_AddRef, + ivbsaxlocator_Release, + ivbsaxlocator_GetTypeInfoCount, + ivbsaxlocator_GetTypeInfo, + ivbsaxlocator_GetIDsOfNames, + ivbsaxlocator_Invoke, + ivbsaxlocator_get_columnNumber, + ivbsaxlocator_get_lineNumber, + ivbsaxlocator_get_publicId, + ivbsaxlocator_get_systemId +}; + +static HRESULT WINAPI isaxlocator_QueryInterface(ISAXLocator *iface, REFIID riid, void **obj) +{ + struct saxlocator *locator = impl_from_ISAXLocator(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + *obj = NULL; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_ISAXLocator)) + { + *obj = iface; + } + else if (IsEqualGUID(riid, &IID_ISAXAttributes)) + { + *obj = &locator->ISAXAttributes_iface; + } + else + { + WARN("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + ISAXLocator_AddRef(iface); + + return S_OK; +} + +static ULONG WINAPI isaxlocator_AddRef(ISAXLocator* iface) +{ + struct saxlocator *locator = impl_from_ISAXLocator(iface); + ULONG refcount = InterlockedIncrement(&locator->refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI isaxlocator_Release(ISAXLocator *iface) +{ + struct saxlocator *locator = impl_from_ISAXLocator(iface); + ULONG refcount = InterlockedDecrement(&locator->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + if (!refcount) + { + element_entry *element, *element2; + + SysFreeString(locator->xmlns_uri); + SysFreeString(locator->xml_uri); + SysFreeString(locator->null_uri); + + saxreader_clear_attributes(locator); + free(locator->attributes.entries); + + /* element stack */ + LIST_FOR_EACH_ENTRY_SAFE(element, element2, &locator->elements, element_entry, entry) + { + list_remove(&element->entry); + free_element_entry(element); + } + + encoded_buffer_cleanup(&locator->buffer.utf16); + encoded_buffer_cleanup(&locator->buffer.raw); + + if (locator->stream) + ISequentialStream_Release(locator->stream); + ISAXXMLReader_Release(&locator->saxreader->ISAXXMLReader_iface); + free(locator); + } + + return refcount; +} + +/*** ISAXLocator methods ***/ +static HRESULT WINAPI isaxlocator_getColumnNumber(ISAXLocator *iface, int *column) +{ + struct saxlocator *locator = impl_from_ISAXLocator(iface); + + *column = locator->column; + return S_OK; +} + +static HRESULT WINAPI isaxlocator_getLineNumber(ISAXLocator *iface, int *line) +{ + struct saxlocator *locator = impl_from_ISAXLocator(iface); + + *line = locator->line; + return S_OK; +} + +static HRESULT WINAPI isaxlocator_getPublicId(ISAXLocator *iface, const WCHAR **value) +{ + FIXME("%p, %p stub\n", iface, value); + + *value = NULL; + return S_OK; +} + +static HRESULT WINAPI isaxlocator_getSystemId(ISAXLocator *iface, const WCHAR **value) +{ + FIXME("%p, %p stub\n", iface, value); + + *value = NULL; + return S_OK; +} + +static const struct ISAXLocatorVtbl SAXLocatorVtbl = +{ + isaxlocator_QueryInterface, + isaxlocator_AddRef, + isaxlocator_Release, + isaxlocator_getColumnNumber, + isaxlocator_getLineNumber, + isaxlocator_getPublicId, + isaxlocator_getSystemId +}; + +static HRESULT saxlocator_create(struct saxreader *reader, ISequentialStream *stream, + bool vbInterface, struct saxlocator **obj) +{ + struct saxlocator *locator; + HRESULT hr; + + *obj = NULL; + + if (!(locator = calloc(1, sizeof(*locator)))) + return E_OUTOFMEMORY; + + locator->IVBSAXLocator_iface.lpVtbl = &VBSAXLocatorVtbl; + locator->ISAXLocator_iface.lpVtbl = &SAXLocatorVtbl; + locator->IVBSAXAttributes_iface.lpVtbl = &ivbsaxattributes_vtbl; + locator->ISAXAttributes_iface.lpVtbl = &isaxattributes_vtbl; + locator->refcount = 1; + locator->vbInterface = vbInterface; + + locator->saxreader = reader; + ISAXXMLReader_AddRef(&reader->ISAXXMLReader_iface); + + locator->stream = stream; + ISequentialStream_AddRef(locator->stream); + + locator->line = reader->version < MSXML4 ? 0 : 1; + locator->column = 0; + locator->buffer.position.line = 1; + locator->buffer.position.column = 1; + locator->buffer.chunk_size = 4096; + list_init(&locator->buffer.entities); + + locator->ret = S_OK; + + locator->xml_uri = saxreader_alloc_string(locator, L"http://www.w3.org/XML/1998/namespace"); + locator->null_uri = saxreader_alloc_stringlen(locator, NULL, 0); + if (locator->saxreader->version >= MSXML6) + locator->xmlns_uri = saxreader_alloc_string(locator, L"http://www.w3.org/2000/xmlns/"); + else + locator->xmlns_uri = saxreader_alloc_stringlen(locator, NULL, 0); + + list_init(&locator->elements); + list_init(&locator->dtd.attr_decls); + list_init(&locator->dtd.entities); + + if (FAILED(locator->status)) + { + hr = locator->status; + ISAXLocator_Release(&locator->ISAXLocator_iface); + return hr; + } + + *obj = locator; + + TRACE("returning %p\n", *obj); + + return S_OK; +} + +static bool saxreader_array_reserve(struct saxlocator *locator, void **elements, size_t *capacity, size_t count, size_t size) +{ + bool ret; + + if (!(ret = array_reserve(elements, capacity, count, size))) + saxreader_set_error(locator, E_OUTOFMEMORY); + + return ret; +} + +static bool saxreader_reserve_buffer(struct saxlocator *locator, struct encoded_buffer *buffer, size_t size) +{ + return saxreader_array_reserve(locator, (void **)&buffer->data, &buffer->allocated, + buffer->written + size, sizeof(*buffer->data)); +} + +static bool saxreader_stream_read(struct saxlocator *locator, void *buffer, ULONG size, ULONG *read) +{ + HRESULT hr; + + if (locator->eos) + return false; + + if (FAILED(hr = ISequentialStream_Read(locator->stream, buffer, size, read))) + { + saxreader_set_error(locator, hr); + locator->eos = true; + return false; + } + + locator->eos = *read == 0; + return true; +} + +static void saxreader_convert_input(struct saxlocator *locator, bool switch_encoding) +{ + struct encoded_buffer *utf16 = &locator->buffer.utf16; + struct encoded_buffer *raw = &locator->buffer.raw; + ULONG read = 0, size; + + /* First conversion attempt with a new codepage. */ + + if (switch_encoding) + { + size = raw->cur; + raw->cur = 0; + } + else + { + size = convert_get_raw_length(&locator->buffer); + } + + read = locator->buffer.converter(locator->buffer.code_page, raw->data + raw->cur, size, NULL, 0); + + if (saxreader_reserve_buffer(locator, utf16, (read + 1) * sizeof(WCHAR))) + { + locator->buffer.converter(locator->buffer.code_page, raw->data + raw->cur, size, + (WCHAR *)(utf16->data + utf16->written), read); + + utf16->written += read * sizeof(WCHAR); + *(WCHAR *)&utf16->data[utf16->written] = 0; + + /* Discard processed raw data */ + if (locator->buffer.switched_encoding) + { + if (size < raw->written) + memmove(raw->data, raw->data + size, raw->written - size); + raw->written -= size; + } + else + { + raw->cur += size; + } + } +} + +static void saxreader_more(struct saxlocator *locator) +{ + struct encoded_buffer *utf16 = &locator->buffer.utf16; + struct encoded_buffer *raw = &locator->buffer.raw; + ULONG read = 0, size; + + if (locator->buffer.encoding == XML_ENCODING_UTF16LE) + { + ULONG needed = locator->buffer.chunk_size * sizeof(WCHAR); + + if (!saxreader_reserve_buffer(locator, utf16, needed + sizeof(WCHAR))) + return; + + if (!saxreader_stream_read(locator, utf16->data + utf16->written, needed, &read)) + return; + utf16->written += read; + *(WCHAR *)&utf16->data[utf16->written] = 0; + + return; + } + + size = convert_estimate_raw_size(locator->buffer.encoding, locator->buffer.chunk_size); + if (!saxreader_reserve_buffer(locator, raw, size)) + return; + + if (!saxreader_stream_read(locator, raw->data + raw->written, size, &read)) + return; + raw->written += read; + + /* Convert complete part of the input */ + saxreader_convert_input(locator, false); +} + +static void saxreader_shrink(struct saxlocator *locator) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + size_t size; + + if (buffer->cur > 3 * locator->buffer.chunk_size / 8) + { + size = buffer->written - buffer->cur * sizeof(WCHAR); + if (size) + memmove(buffer->data, (WCHAR *)buffer->data + buffer->cur, size); + + buffer->written = size; + buffer->cur = 0; + } +} + +static WCHAR *saxreader_get_ptr(struct saxlocator *locator) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + WCHAR *ptr = (WCHAR *)buffer->data + buffer->cur; + if (!*ptr) saxreader_more(locator); + return (WCHAR *)buffer->data + buffer->cur; +} + +static WCHAR saxreader_get_char(struct saxlocator *locator, int pos) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + if (buffer->written - buffer->cur < pos * sizeof(WCHAR)) return 0; + return *(WCHAR *)(buffer->data + buffer->cur + pos * sizeof(WCHAR)); +} + +static WCHAR *saxreader_get_ptr_noread(struct saxlocator *locator) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + return (WCHAR *)buffer->data + buffer->cur; +} + +static void saxreader_update_position(struct saxlocator *locator, WCHAR ch) +{ + struct input_buffer *input = &locator->buffer; + + /* Treat \r\n -> \n */ + if (ch == '\r' || (!input->last_cr && ch == '\n')) + { + ++input->position.line; + input->position.column = 1; + } + else if (!(input->last_cr && ch == '\n')) + { + ++input->position.column; + } + input->last_cr = ch == '\r'; +} + +static void saxlocator_end_entity(struct saxlocator *locator, const struct text_position *position, BSTR name); + +static bool saxreader_pop_entity(struct saxlocator *locator) +{ + struct input_buffer *input = &locator->buffer; + struct entity_decl *entity; + + if (list_empty(&input->entities)) return true; + + entity = LIST_ENTRY(list_head(&input->entities), struct entity_decl, input_entry); + + if (entity->remaining) + { + --entity->remaining; + return false; + } + + entity->visited = false; + list_remove(&entity->input_entry); + + saxlocator_end_entity(locator, &entity->position, entity->name); + + return list_empty(&input->entities); +} + +static void saxreader_skip(struct saxlocator *locator, int n) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + struct input_buffer *input = &locator->buffer; + WCHAR ch; + + while (locator->status == S_OK && (ch = *saxreader_get_ptr(locator)) && n--) + { + if (saxreader_pop_entity(locator)) + saxreader_update_position(locator, ch); + + ++buffer->cur; + ++input->consumed; + } +} + +static bool saxreader_peek(struct saxlocator *locator, const WCHAR *str, size_t count) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + WCHAR *ptr = (WCHAR *)buffer->data + buffer->cur; + + if (FAILED(locator->status)) + return false; + + if ((buffer->written - buffer->cur) < count) + return false; + + return !memcmp(ptr, str, count * sizeof(WCHAR)); +} + +static bool saxreader_cmp(struct saxlocator *locator, const WCHAR *str) +{ + const WCHAR *ptr; + size_t i = 0; + + if (FAILED(locator->status)) + return false; + + ptr = saxreader_get_ptr(locator); + while (str[i]) + { + if (!ptr[i]) + { + saxreader_more(locator); + ptr = saxreader_get_ptr(locator); + } + if (str[i] != ptr[i]) + return false; + i++; + } + saxreader_skip(locator, i); + return true; +} + +static void saxreader_fatal_error(struct saxlocator *locator) +{ + struct saxerrorhandler_iface *handler = saxreader_get_errorhandler(locator->saxreader); + + if (saxreader_has_handler(locator, SAXErrorHandler)) + { + WCHAR msg[1024]; + + if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, locator->status, 0, msg, ARRAY_SIZE(msg), NULL)) + { + FIXME("MSXML errors not yet supported.\n"); + msg[0] = '\0'; + } + + if (locator->vbInterface) + { + BSTR s = SysAllocString(msg); + IVBSAXErrorHandler_fatalError(handler->vbhandler, &locator->IVBSAXLocator_iface, &s, locator->status); + SysFreeString(s); + } + else + { + ISAXErrorHandler_fatalError(handler->handler, &locator->ISAXLocator_iface, msg, locator->status); + } + } +} + +struct ns +{ + BSTR prefix; + BSTR uri; +}; + +struct element +{ + struct list entry; + struct name name; + BSTR uri; + + struct + { + struct ns *entries; + size_t count; + size_t capacity; + } ns; +}; + +static HRESULT saxlocator_callback_result(struct saxlocator *locator, HRESULT hr) +{ + if (locator->saxreader->version >= MSXML4) return FAILED(hr) ? hr : S_OK; + return hr; +} + +static void saxlocator_put_document_locator(struct saxlocator *locator) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr = S_OK; + + if (locator->status != S_OK) + return; + + locator->start_document_position = locator->buffer.position; + if (locator->start_document_position.column) + --locator->start_document_position.column; + + if (saxreader_has_handler(locator, SAXContentHandler)) + { + if (locator->vbInterface) + hr = IVBSAXContentHandler_putref_documentLocator(handler->vbhandler, &locator->IVBSAXLocator_iface); + else + hr = ISAXContentHandler_putDocumentLocator(handler->handler, &locator->ISAXLocator_iface); + } + + if (FAILED(hr)) + locator->status = hr; +} + +static void saxlocator_start_document(struct saxlocator *locator) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr = S_OK; + + if (locator->status != S_OK) + return; + + if (locator->saxreader->version >= MSXML4) + { + locator->line = locator->start_document_position.line; + locator->column = locator->start_document_position.column; + } + else + { + locator->line = 0; + locator->column = 0; + } + + if (saxreader_has_handler(locator, SAXContentHandler)) + { + if (locator->vbInterface) + hr = IVBSAXContentHandler_startDocument(handler->vbhandler); + else + hr = ISAXContentHandler_startDocument(handler->handler); + } + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_end_document(struct saxlocator *locator) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr = S_OK; + + if (locator->status != S_OK) + return; + + if (locator->saxreader->version >= MSXML4) + { + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column - 1; + } + else + { + locator->line = 0; + locator->column = 0; + } + + if (saxreader_has_handler(locator, SAXContentHandler)) + { + if (locator->vbInterface) + hr = IVBSAXContentHandler_endDocument(handler->vbhandler); + else + hr = ISAXContentHandler_endDocument(handler->handler); + } + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_start_element(struct saxlocator *locator, const struct text_position *position, + struct element *element) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + BSTR uri = NULL, local = NULL; + HRESULT hr; + + if (locator->status != S_OK) + return; + + if (!saxreader_has_handler(locator, SAXContentHandler)) + return; + + locator->line = position->line; + locator->column = position->column; + /* Point to the closing '>' */ + if (locator->saxreader->version >= MSXML4) + --locator->column; + + if (is_namespaces_enabled(locator->saxreader)) + { + for (size_t i = 0; i < element->ns.count && saxreader_has_handler(locator, SAXContentHandler); ++i) + { + struct ns *ns = &element->ns.entries[i]; + + if (locator->vbInterface) + hr = IVBSAXContentHandler_startPrefixMapping( + handler->vbhandler, &ns->prefix, &ns->uri); + else + hr = ISAXContentHandler_startPrefixMapping( + handler->handler, + ns->prefix, + SysStringLen(ns->prefix), + ns->uri, + SysStringLen(ns->uri)); + + if (FAILED((hr = saxlocator_callback_result(locator, hr)))) + { + locator->status = hr; + return; + } + } + + uri = element->uri; + local = element->name.local; + } + + if (locator->vbInterface) + { + if (!uri) uri = locator->saxreader->empty_bstr; + hr = IVBSAXContentHandler_startElement(handler->vbhandler, &uri, &local, &element->name.qname, + &locator->IVBSAXAttributes_iface); + } + else + { + hr = ISAXContentHandler_startElement(handler->handler, uri ? uri : &empty_str, SysStringLen(uri), + local ? local : &empty_str, SysStringLen(local), element->name.qname, SysStringLen(element->name.qname), + &locator->ISAXAttributes_iface); + } + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_end_element(struct saxlocator *locator, const struct text_position *position, struct element *element) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + BSTR uri = NULL, local = NULL; + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + /* Point to the closing '>' */ + if (locator->saxreader->version >= MSXML4) + locator->column = locator->buffer.position.column - 1; + else + locator->column = position->column; + + if (saxreader_has_handler(locator, SAXContentHandler)) + { + if (is_namespaces_enabled(locator->saxreader)) + { + uri = element->uri; + local = element->name.local; + } + + if (locator->vbInterface) + { + if (!uri) uri = locator->saxreader->empty_bstr; + hr = IVBSAXContentHandler_endElement(handler->vbhandler, &uri, &local, &element->name.qname); + } + else + { + hr = ISAXContentHandler_endElement(handler->handler, uri ? uri : &empty_str, SysStringLen(uri), + local ? local : &empty_str, SysStringLen(local), element->name.qname, SysStringLen(element->name.qname)); + } + + locator->status = saxlocator_callback_result(locator, hr); + } + + if (locator->status != S_OK) + return; + + if (is_namespaces_enabled(locator->saxreader)) + { + for (size_t i = 0; i < element->ns.count && saxreader_has_handler(locator, SAXContentHandler); ++i) + { + size_t index = locator->saxreader->version >= MSXML4 ? i : element->ns.count - 1 - i; + struct ns *ns = &element->ns.entries[index]; + + if (locator->vbInterface) + hr = IVBSAXContentHandler_endPrefixMapping(handler->vbhandler, &ns->prefix); + else + hr = ISAXContentHandler_endPrefixMapping(handler->handler, ns->prefix, SysStringLen(ns->prefix)); + + locator->status = saxlocator_callback_result(locator, hr); + if (locator->status != S_OK) + break; + } + } +} + +static void saxlocator_start_cdata(struct saxlocator *locator, const struct text_position *position) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr = S_OK; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + /* Point to the closing '[' */ + if (locator->saxreader->version >= MSXML4) + --locator->column; + + if (saxreader_has_handler(locator, SAXLexicalHandler)) + { + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_startCDATA(lexical->vbhandler); + else + hr = ISAXLexicalHandler_startCDATA(lexical->handler); + } + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_end_cdata(struct saxlocator *locator, const struct text_position *position) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr = S_OK; + + if (locator->status != S_OK) + return; + + if (locator->saxreader->version >= MSXML4) + { + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column - 1; + } + else + { + locator->line = position->line; + locator->column = position->column; + } + + if (saxreader_has_handler(locator, SAXLexicalHandler)) + { + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_endCDATA(lexical->vbhandler); + else + hr = ISAXLexicalHandler_endCDATA(lexical->handler); + } + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_characters(struct saxlocator *locator, const struct text_position *position, BSTR chars) +{ + struct saxcontenthandler_iface *content = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK || !*chars) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXContentHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXContentHandler_characters(content->vbhandler, &chars); + else + hr = ISAXContentHandler_characters(content->handler, chars, SysStringLen(chars)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_comment(struct saxlocator *locator, const struct text_position *position, BSTR chars) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXLexicalHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_comment(lexical->vbhandler, &chars); + else + hr = ISAXLexicalHandler_comment(lexical->handler, chars, SysStringLen(chars)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_pi(struct saxlocator *locator, const struct text_position *position, BSTR target, BSTR data) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + /* Point to the closing '>' */ + if (locator->saxreader->version >= MSXML4) + { + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column - 1; + } + else + { + locator->line = position->line; + locator->column = position->column; + } + + if (!saxreader_has_handler(locator, SAXContentHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXContentHandler_processingInstruction(handler->vbhandler, &target, &data); + else + hr = ISAXContentHandler_processingInstruction(handler->handler, target, SysStringLen(target), + data, SysStringLen(data)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_skipped_entity(struct saxlocator *locator, const struct text_position *position, BSTR name) +{ + struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXContentHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXContentHandler_skippedEntity(handler->vbhandler, &name); + else + hr = ISAXContentHandler_skippedEntity(handler->handler, name, SysStringLen(name)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_notationdecl(struct saxlocator *locator, const struct text_position *position, + BSTR name, BSTR pubid, BSTR sysid) +{ + struct saxdtdhandler_iface *handler = saxreader_get_dtdhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXDTDHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXDTDHandler_notationDecl(handler->vbhandler, &name, &pubid, &sysid); + else + hr = ISAXDTDHandler_notationDecl(handler->handler, name, SysStringLen(name), + pubid, SysStringLen(pubid), sysid, SysStringLen(sysid)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_unparsed_entitydecl(struct saxlocator *locator, BSTR name, BSTR pubid, BSTR sysid, BSTR notation) +{ + struct saxdtdhandler_iface *handler = saxreader_get_dtdhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXDTDHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXDTDHandler_unparsedEntityDecl(handler->vbhandler, &name, &pubid, &sysid, ¬ation); + else + hr = ISAXDTDHandler_unparsedEntityDecl(handler->handler, name, SysStringLen(name), + pubid, SysStringLen(pubid), sysid, SysStringLen(sysid), notation, SysStringLen(notation)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_external_entitydecl(struct saxlocator *locator, BSTR name, BSTR pubid, BSTR sysid) +{ + struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXDeclHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXDeclHandler_externalEntityDecl(handler->vbhandler, &name, &pubid, &sysid); + else + hr = ISAXDeclHandler_externalEntityDecl(handler->handler, name, SysStringLen(name), + pubid, SysStringLen(pubid), sysid, SysStringLen(sysid)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_elementdecl(struct saxlocator *locator, BSTR name, BSTR model) +{ + struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXDeclHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXDeclHandler_elementDecl(handler->vbhandler, &name, &model); + else + hr = ISAXDeclHandler_elementDecl(handler->handler, name, SysStringLen(name), model, SysStringLen(model)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_internal_entitydecl(struct saxlocator *locator, BSTR name, BSTR value) +{ + struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXDeclHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXDeclHandler_internalEntityDecl(handler->vbhandler, &name, &value); + else + hr = ISAXDeclHandler_internalEntityDecl(handler->handler, name, SysStringLen(name), + value, SysStringLen(value)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_attribute_decl(struct saxlocator *locator, struct attlist_decl *decl) +{ + struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXDeclHandler)) + return; + + for (size_t i = 0; i < decl->count && locator->status == S_OK; ++i) + { + if (locator->vbInterface) + hr = IVBSAXDeclHandler_attributeDecl(handler->vbhandler, &decl->name, + &decl->attributes[i].name.qname, &decl->attributes[i].type, + &decl->attributes[i].valuetype, &decl->attributes[i].value); + else + hr = ISAXDeclHandler_attributeDecl(handler->handler, decl->name, SysStringLen(decl->name), + decl->attributes[i].name.qname, SysStringLen(decl->attributes[i].name.qname), + decl->attributes[i].type, SysStringLen(decl->attributes[i].type), + decl->attributes[i].valuetype, SysStringLen(decl->attributes[i].valuetype), + decl->attributes[i].value, SysStringLen(decl->attributes[i].value)); + + locator->status = saxlocator_callback_result(locator, hr); + } +} + +static void saxlocator_startdtd(struct saxlocator *locator, BSTR name, BSTR pubid, BSTR sysid) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXLexicalHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_startDTD(lexical->vbhandler, &name, &pubid, &sysid); + else + hr = ISAXLexicalHandler_startDTD(lexical->handler, name, SysStringLen(name), + pubid, SysStringLen(pubid), sysid, SysStringLen(sysid)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_enddtd(struct saxlocator *locator) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = locator->buffer.position.line; + locator->column = locator->buffer.position.column; + + if (!saxreader_has_handler(locator, SAXLexicalHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_endDTD(lexical->vbhandler); + else + hr = ISAXLexicalHandler_endDTD(lexical->handler); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_start_entity(struct saxlocator *locator, const struct text_position *position, BSTR name) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXLexicalHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_startEntity(lexical->vbhandler, &name); + else + hr = ISAXLexicalHandler_startEntity(lexical->handler, name, SysStringLen(name)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +static void saxlocator_end_entity(struct saxlocator *locator, const struct text_position *position, BSTR name) +{ + struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); + HRESULT hr; + + if (locator->status != S_OK) + return; + + locator->line = position->line; + locator->column = position->column; + + if (!saxreader_has_handler(locator, SAXLexicalHandler)) + return; + + if (locator->vbInterface) + hr = IVBSAXLexicalHandler_endEntity(lexical->vbhandler, &name); + else + hr = ISAXLexicalHandler_endEntity(lexical->handler, name, SysStringLen(name)); + + locator->status = saxlocator_callback_result(locator, hr); +} + +struct string_buffer +{ + WCHAR *data; + size_t count; + size_t capacity; +}; + +static void saxreader_string_append(struct saxlocator *locator, struct string_buffer *buffer, + const WCHAR *str, UINT len) +{ + if (!saxreader_array_reserve(locator, (void **)&buffer->data, &buffer->capacity, buffer->count + len, sizeof(*str))) + { + saxreader_set_error(locator, E_OUTOFMEMORY); + return; + } + + memcpy(buffer->data + buffer->count, str, len * sizeof(WCHAR)); + buffer->count += len; +} + +static void saxreader_string_append_bstr(struct saxlocator *locator, struct string_buffer *buffer, BSTR *str) +{ + saxreader_string_append(locator, buffer, *str, SysStringLen(*str)); + SysFreeString(*str); + *str = NULL; +} + +static BSTR saxreader_string_to_bstr(struct saxlocator *locator, struct string_buffer *buffer) +{ + BSTR str = saxreader_alloc_stringlen(locator, buffer->data, buffer->count); + free(buffer->data); + memset(buffer, 0, sizeof(*buffer)); + return str; +} + +/* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ +static bool saxreader_is_space(WCHAR ch) +{ + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +/* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ +static bool saxreader_skipspaces(struct saxlocator *locator) +{ + size_t length = 0; + const WCHAR *ptr; + + if (locator->status != S_OK) + return false; + + ptr = saxreader_get_ptr(locator); + while (saxreader_is_space(*ptr)) + { + saxreader_skip(locator, 1); + ptr = saxreader_get_ptr(locator); + ++length; + } + + return length; +} + +static bool saxreader_skip_required_spaces(struct saxlocator *locator) +{ + bool ret; + + if (!(ret = saxreader_skipspaces(locator))) + saxreader_set_error(locator, E_SAX_MISSINGWHITESPACE); + + return ret; +} + +/* [25] Eq ::= S? '=' S? */ +static void saxreader_parse_eq(struct saxlocator *locator) +{ + saxreader_skipspaces(locator); + if (!saxreader_cmp(locator, L"=")) + return saxreader_set_error(locator, E_SAX_MISSINGEQUALS); + saxreader_skipspaces(locator); +} + +/* [26] VersionNum ::= '1.' [0-9]+ */ +static void saxreader_parse_versionnum(struct saxlocator *locator) +{ + if (saxreader_cmp(locator, L"1.0")) + { + SysFreeString(locator->saxreader->xmldecl_version); + locator->saxreader->xmldecl_version = saxreader_alloc_string(locator, L"1.0"); + } + else + { + saxreader_set_error(locator, E_SAX_INVALID_VERSION); + } +} + +static WCHAR saxreader_is_quote(struct saxlocator *locator) +{ + const WCHAR *ptr; + + if (locator->status != S_OK) + return 0; + + ptr = saxreader_get_ptr(locator); + if (*ptr == '\'' || *ptr == '"') return *ptr; + return 0; +} + +/* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */ +static void saxreader_parse_versioninfo(struct saxlocator *locator) +{ + WCHAR q[2] = { 0 }; + + if (!saxreader_cmp(locator, L"version")) + return saxreader_set_error(locator, E_SAX_BAD_XMLDECL); + + saxreader_parse_eq(locator); + if (!(q[0] = saxreader_is_quote(locator))) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + saxreader_skip(locator, 1); + + saxreader_parse_versionnum(locator); + + if (!saxreader_cmp(locator, q)) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); +} + +static void saxreader_switch_encoding(struct saxlocator *locator, UINT codepage) +{ + /* We'll keep mbtowc converter. */ + locator->buffer.code_page = codepage; + + /* Convert entire buffer again, nothing should've been discarded yet. + The utf16 buffer cursor is kept intact. */ + locator->buffer.utf16.written = 0; + locator->buffer.switched_encoding = true; + saxreader_convert_input(locator, true); +} + +/* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */ +static void saxreader_parse_encdecl(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + WCHAR q[2] = { 0 }, ch; + UINT codepage; + + saxreader_skip_required_spaces(locator); + if (!saxreader_cmp(locator, L"encoding")) + return; + + saxreader_parse_eq(locator); + + if (!(q[0] = saxreader_is_quote(locator))) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + saxreader_skip(locator, 1); + + ch = *saxreader_get_ptr(locator); + while (ch != *q) + { + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr(locator); + } + + if (!saxreader_cmp(locator, q)) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + + locator->saxreader->xmldecl_encoding = saxreader_string_to_bstr(locator, &buffer); + TRACE("encoding name %s\n", debugstr_w(locator->saxreader->xmldecl_encoding)); + + /* Switch encoding only in UTF-8 -> mbtowc-able direction. */ + + if (locator->buffer.encoding == XML_ENCODING_UTF8) + { + if (saxreader_get_encoding_codepage(locator->saxreader->xmldecl_encoding, &codepage)) + saxreader_switch_encoding(locator, codepage); + else if (!saxreader_can_ignore_encoding(locator->saxreader->xmldecl_encoding)) + FIXME("Unsupported encoding %s.\n", debugstr_w(locator->saxreader->xmldecl_encoding)); + } +} + +/* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */ +static void saxreader_parse_sddecl(struct saxlocator *locator) +{ + WCHAR q[2] = { 0 }; + + saxreader_skipspaces(locator); + + if (!saxreader_cmp(locator, L"standalone")) + return; + + saxreader_parse_eq(locator); + + if (!(q[0] = saxreader_is_quote(locator))) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + saxreader_skip(locator, 1); + + if (saxreader_cmp(locator, L"yes")) + locator->saxreader->xmldecl_standalone = saxreader_alloc_string(locator, L"yes"); + else if (saxreader_cmp(locator, L"no")) + locator->saxreader->xmldecl_standalone = saxreader_alloc_string(locator, L"no"); + else + saxreader_set_error(locator, E_SAX_INVALID_STANDALONE); + + if (!saxreader_cmp(locator, q)) + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); +} + +/* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */ +static void saxreader_parse_xmldecl(struct saxlocator *locator) +{ + WCHAR ch; + + saxreader_more(locator); + + ch = saxreader_get_char(locator, 5); + if (!saxreader_peek(locator, L"<?xml", 5) || !saxreader_is_space(ch)) + return; + saxreader_skip(locator, 5); + saxreader_skipspaces(locator); + + saxreader_parse_versioninfo(locator); + + if (saxreader_cmp(locator, L"?>")) + return; + + saxreader_parse_encdecl(locator); + + if (locator->saxreader->xmldecl_encoding) + { + if (saxreader_cmp(locator, L"?>")) + return; + saxreader_skip_required_spaces(locator); + } + + saxreader_skipspaces(locator); + saxreader_parse_sddecl(locator); + saxreader_skipspaces(locator); + + if (!saxreader_cmp(locator, L"?>")) + saxreader_set_error(locator, E_SAX_BAD_XMLDECL); +} + +/* [85] BaseChar ::= ... */ +static bool saxreader_is_basechar(WCHAR ch) +{ + return (ch >= 0x41 && ch <= 0x5a) + || (ch >= 0x61 && ch <= 0x7a) || (ch >= 0xc0 && ch <= 0xd6) + || (ch >= 0xd8 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0x131) + || (ch >= 0x134 && ch <= 0x13e) || (ch >= 0x141 && ch <= 0x148) + || (ch >= 0x14a && ch <= 0x17e) || (ch >= 0x180 && ch <= 0x1c3) + || (ch >= 0x1cd && ch <= 0x1f0) || (ch >= 0x1f4 && ch <= 0x1f5) + || (ch >= 0x1fa && ch <= 0x217) || (ch >= 0x250 && ch <= 0x2a8) + || (ch >= 0x2bb && ch <= 0x2c1) || ch == 0x386 || (ch >= 0x388 && ch <= 0x38a) + || ch == 0x38c || (ch >= 0x38e && ch <= 0x3a1) + || (ch >= 0x3a3 && ch <= 0x3ce) || (ch >= 0x3d0 && ch <= 0x3d6) + || ch == 0x3da || ch == 0x3dc || ch == 0x3de || ch == 0x3e0 + || (ch >= 0x3e2 && ch <= 0x3f3) || (ch >= 0x401 && ch <= 0x40c) + || (ch >= 0x40e && ch <= 0x44f) || (ch >= 0x451 && ch <= 0x45c) + || (ch >= 0x45e && ch <= 0x481) || (ch >= 0x490 && ch <= 0x4c4) + || (ch >= 0x4c7 && ch <= 0x4c8) || (ch >= 0x4cb && ch <= 0x4cc) + || (ch >= 0x4d0 && ch <= 0x4eb) || (ch >= 0x4ee && ch <= 0x4f5) + || (ch >= 0x4f8 && ch <= 0x4f9) || (ch >= 0x531 && ch <= 0x556) + || ch == 0x559 || (ch >= 0x561 && ch <= 0x586) + || (ch >= 0x5d0 && ch <= 0x5ea) || (ch >= 0x5f0 && ch <= 0x5f2) + || (ch >= 0x621 && ch <= 0x63a) || (ch >= 0x641 && ch <= 0x64a) + || (ch >= 0x671 && ch <= 0x6b7) || (ch >= 0x6ba && ch <= 0x6be) + || (ch >= 0x6c0 && ch <= 0x6ce) || (ch >= 0x6d0 && ch <= 0x6d3) + || ch == 0x6d5 || (ch >= 0x6e5 && ch <= 0x6e6) + || (ch >= 0x905 && ch <= 0x939) || ch == 0x93d || (ch >= 0x958 && ch <= 0x961) + || (ch >= 0x985 && ch <= 0x98c) || (ch >= 0x98f && ch <= 0x990) + || (ch >= 0x993 && ch <= 0x9a8) || (ch >= 0x9aa && ch <= 0x9b0) + || ch == 0x9b2 || (ch >= 0x9b6 && ch <= 0x9b9) + || (ch >= 0x9dc && ch <= 0x9dd) || (ch >= 0x9df && ch <= 0x9e1) + || (ch >= 0x9f0 && ch <= 0x9f1) || (ch >= 0xa05 && ch <= 0xa0a) + || (ch >= 0xa0f && ch <= 0xa10) || (ch >= 0xa13 && ch <= 0xa28) + || (ch >= 0xa2a && ch <= 0xa30) || (ch >= 0xa32 && ch <= 0xa33) + || (ch >= 0xa35 && ch <= 0xa36) || (ch >= 0xa38 && ch <= 0xa39) + || (ch >= 0xa59 && ch <= 0xa5c) + || ch == 0xa5e || (ch >= 0xa72 && ch <= 0xa74) || (ch >= 0xa85 && ch <= 0xa8b) + || ch == 0xa8d || (ch >= 0xa8f && ch <= 0xa91) + || (ch >= 0xa93 && ch <= 0xaa8) || (ch >= 0xaaa && ch <= 0xab0) + || (ch >= 0xab2 && ch <= 0xab3) || (ch >= 0xab5 && ch <= 0xab9) + || ch == 0xabd || ch == 0xae0 || (ch >= 0xb05 && ch <= 0xb0c) + || (ch >= 0xb0f && ch <= 0xb10) || (ch >= 0xb13 && ch <= 0xb28) + || (ch >= 0xb2a && ch <= 0xb30) || (ch >= 0xb32 && ch <= 0xb33) + || (ch >= 0xb36 && ch <= 0xb39) || ch == 0xb3d || (ch >= 0xb5c && ch <= 0xb5d) + || (ch >= 0xb5f && ch <= 0xb61) || (ch >= 0xb85 && ch <= 0xb8a) + || (ch >= 0xb8e && ch <= 0xb90) || (ch >= 0xb92 && ch <= 0xb95) + || (ch >= 0xb99 && ch <= 0xb9a) || ch == 0xb9c || (ch >= 0xb9e && ch <= 0xb9f) + || (ch >= 0xba3 && ch <= 0xba4) || (ch >= 0xba8 && ch <= 0xbaa) + || (ch >= 0xbae && ch <= 0xbb5) || (ch >= 0xbb7 && ch <= 0xbb9) + || (ch >= 0xc05 && ch <= 0xc0c) || (ch >= 0xc0e && ch <= 0xc10) + || (ch >= 0xc12 && ch <= 0xc28) || (ch >= 0xc2a && ch <= 0xc33) + || (ch >= 0xc35 && ch <= 0xc39) || (ch >= 0xc60 && ch <= 0xc61) + || (ch >= 0xc85 && ch <= 0xc8c) || (ch >= 0xc8e && ch <= 0xc90) + || (ch >= 0xc92 && ch <= 0xca8) || (ch >= 0xcaa && ch <= 0xcb3) + || (ch >= 0xcb5 && ch <= 0xcb9) || ch == 0xcde || (ch >= 0xce0 && ch <= 0xce1) + || (ch >= 0xd05 && ch <= 0xd0c) || (ch >= 0xd0e && ch <= 0xd10) + || (ch >= 0xd12 && ch <= 0xd28) || (ch >= 0xd2a && ch <= 0xd39) + || (ch >= 0xd60 && ch <= 0xd61) || (ch >= 0xe01 && ch <= 0xe2e) + || ch == 0xe30 || (ch >= 0xe32 && ch <= 0xe33) + || (ch >= 0xe40 && ch <= 0xe45) || (ch >= 0xe81 && ch <= 0xe82) + || ch == 0xe84 || (ch >= 0xe87 && ch <= 0xe88) + || ch == 0xe8a || ch == 0xe8d || (ch >= 0xe94 && ch <= 0xe97) + || (ch >= 0xe99 && ch <= 0xe9f) || (ch >= 0xea1 && ch <= 0xea3) + || ch == 0xea5 || ch == 0xea7 || (ch >= 0xeaa && ch <= 0xeab) + || (ch >= 0xead && ch <= 0xeae) || ch == 0xeb0 || (ch >= 0xeb2 && ch <= 0xeb3) + || ch == 0xebd || (ch >= 0xec0 && ch <= 0xec4) + || (ch >= 0xf40 && ch <= 0xf47) || (ch >= 0xf49 && ch <= 0xf69) + || (ch >= 0x10a0 && ch <= 0x10c5) || (ch >= 0x10d0 && ch <= 0x10f6) + || ch == 0x1100 || (ch >= 0x1102 && ch <= 0x1103) + || (ch >= 0x1105 && ch <= 0x1107) || ch == 0x1109 || (ch >= 0x110b && ch <= 0x110c) + || (ch >= 0x110e && ch <= 0x1112) || ch == 0x113c || ch == 0x113e || ch == 0x1140 + || ch == 0x114c || ch == 0x114e || ch == 0x1150 + || (ch >= 0x1154 && ch <= 0x1155) || ch == 0x1159 || (ch >= 0x115f && ch <= 0x1161) + || ch == 0x1163 || ch == 0x1165 || ch == 0x1167 + || ch == 0x1169 || (ch >= 0x116d && ch <= 0x116e) + || (ch >= 0x1172 && ch <= 0x1173) || ch == 0x1175 || ch == 0x119e || ch == 0x11a8 + || ch == 0x11ab || (ch >= 0x11ae && ch <= 0x11af) + || (ch >= 0x11b7 && ch <= 0x11b8) || ch == 0x11ba || (ch >= 0x11bc && ch <= 0x11c2) + || ch == 0x11eb || ch == 0x11f0 || ch == 0x11f9 + || (ch >= 0x1e00 && ch <= 0x1e9b) || (ch >= 0x1ea0 && ch <= 0x1ef9) + || (ch >= 0x1f00 && ch <= 0x1f15) || (ch >= 0x1f18 && ch <= 0x1f1d) + || (ch >= 0x1f20 && ch <= 0x1f45) || (ch >= 0x1f48 && ch <= 0x1f4d) + || (ch >= 0x1f50 && ch <= 0x1f57) || ch == 0x1f59 || ch == 0x1f5b || ch == 0x1f5d + || (ch >= 0x1f5f && ch <= 0x1f7d) || (ch >= 0x1f80 && ch <= 0x1fb4) + || (ch >= 0x1fb6 && ch <= 0x1fbc) || ch == 0x1fbe || (ch >= 0x1fc2 && ch <= 0x1fc4) + || (ch >= 0x1fc6 && ch <= 0x1fcc) || (ch >= 0x1fd0 && ch <= 0x1fd3) + || (ch >= 0x1fd6 && ch <= 0x1fdb) || (ch >= 0x1fe0 && ch <= 0x1fec) + || (ch >= 0x1ff2 && ch <= 0x1ff4) || (ch >= 0x1ff6 && ch <= 0x1ffc) + || ch == 0x2126 || (ch >= 0x212a && ch <= 0x212b) + || ch == 0x212e || (ch >= 0x2180 && ch <= 0x2182) + || (ch >= 0x3041 && ch <= 0x3094) || (ch >= 0x30a1 && ch <= 0x30fa) + || (ch >= 0x3105 && ch <= 0x312c) || (ch >= 0xac00 && ch <= 0xd7a3); +} + +/* [86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029] */ +static bool saxreader_is_ideographic(WCHAR ch) +{ + return (ch >= 0x4e00 && ch <= 0x9fa5) || ch == 0x3007 + || (ch >= 0x3021 && ch <= 0x3029); +} + +/* [84] Letter ::= BaseChar | Ideographic */ +static bool saxreader_is_letter(WCHAR ch) +{ + return saxreader_is_basechar(ch) || saxreader_is_ideographic(ch); +} + +/* [87] CombiningChar ::= ... */ +static bool saxreader_is_combiningchar(WCHAR ch) +{ + return (ch >= 0x300 && ch <= 0x345) || (ch >= 0x360 && ch <= 0x361) + || (ch >= 0x483 && ch <= 0x486) || (ch >= 0x591 && ch <= 0x5a1) || ch == 0x670 + || (ch >= 0x6d6 && ch <= 0x6e4) || (ch >= 0x6e7 && ch <= 0x6e8) + || (ch >= 0x6ea && ch <= 0x6ed) || (ch >= 0x901 && ch <= 0x903) || ch == 0x93c + || (ch >= 0x93e && ch <= 0x94d) || (ch >= 0x951 && ch <= 0x954) + || (ch >= 0x962 && ch <= 0x963) || (ch >= 0x981 && ch <= 0x983) || ch == 0x9bc + || (ch >= 0x9be && ch <= 0x9bf) || (ch >= 0x9c0 && ch <= 0x9c4) + || (ch >= 0x9c7 && ch <= 0x9c8) || (ch >= 0x9cb && ch <= 0x9cd) || ch == 0x9d7 + || (ch >= 0x9e2 && ch <= 0x9e3) || ch == 0xa02 + || ch == 0xa3c || (ch >= 0xa3e && ch <= 0xa42) + || (ch >= 0xa47 && ch <= 0xa48) || (ch >= 0xa4b && ch <= 0xa4d) + || (ch >= 0xa70 && ch <= 0xa71) || (ch >= 0xa81 && ch <= 0xa83) + || ch == 0xabc || (ch >= 0xabe && ch <= 0xac5) + || (ch >= 0xac7 && ch <= 0xac9) || (ch >= 0xacb && ch <= 0xacd) + || (ch >= 0xb01 && ch <= 0xb03) || ch == 0xb3c || (ch >= 0xb3e && ch <= 0xb43) + || (ch >= 0xb47 && ch <= 0xb48) || (ch >= 0xb4b && ch <= 0xb4d) + || (ch >= 0xb56 && ch <= 0xb57) || (ch >= 0xb82 && ch <= 0xb83) + || (ch >= 0xbbe && ch <= 0xbc2) || (ch >= 0xbc6 && ch <= 0xbc8) + || (ch >= 0xbca && ch <= 0xbcd) + || ch == 0xbd7 || (ch >= 0xc01 && ch <= 0xc03) + || (ch >= 0xc3e && ch <= 0xc44) || (ch >= 0xc46 && ch <= 0xc48) + || (ch >= 0xc4a && ch <= 0xc4d) || (ch >= 0xc55 && ch <= 0xc56) + || (ch >= 0xc82 && ch <= 0xc83) || (ch >= 0xcbe && ch <= 0xcc4) + || (ch >= 0xcc6 && ch <= 0xcc8) || (ch >= 0xcca && ch <= 0xccd) + || (ch >= 0xcd5 && ch <= 0xcd6) || (ch >= 0xd02 && ch <= 0xd03) + || (ch >= 0xd3e && ch <= 0xd43) || (ch >= 0xd46 && ch <= 0xd48) + || (ch >= 0xd4a && ch <= 0xd4d) || ch == 0xd57 || ch == 0xe31 + || (ch >= 0xe34 && ch <= 0xe3a) || (ch >= 0xe47 && ch <= 0xe4e) + || ch == 0xeb1 || (ch >= 0xeb4 && ch <= 0xeb9) + || (ch >= 0xebb && ch <= 0xebc) || (ch >= 0xec8 && ch <= 0xecd) + || (ch >= 0xf18 && ch <= 0xf19) || ch == 0xf35 || ch == 0xf37 || ch == 0xf39 + || (ch >= 0xf3e && ch <= 0xf3f) || (ch >= 0xf71 && ch <= 0xf84) + || (ch >= 0xf86 && ch <= 0xf8b) || (ch >= 0xf90 && ch <= 0xf95) + || ch == 0xf97 || (ch >= 0xf99 && ch <= 0xfad) + || (ch >= 0xfb1 && ch <= 0xfb7) || ch == 0xfb9 || (ch >= 0x20d0 && ch <= 0x20dc) + || ch == 0x20e1 || (ch >= 0x302a && ch <= 0x302f) + || ch == 0x3099 || ch == 0x309a; +} + +/* [88] Digit ::= [#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | [#x0966-#x096F] | + [#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | + [#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] | + [#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29] */ +static bool saxreader_is_digit(WCHAR ch) +{ + return (ch >= 0x30 && ch <= 0x39) || (ch >= 0x660 && ch <= 0x669) + || (ch >= 0x6f0 && ch <= 0x6f9) || (ch >= 0x966 && ch <= 0x96f) + || (ch >= 0x9e6 && ch <= 0x9ef) || (ch >= 0xa66 && ch <= 0xa6f) + || (ch >= 0xae6 && ch <= 0xaef) || (ch >= 0xb66 && ch <= 0xb6f) + || (ch >= 0xbe7 && ch <= 0xbef) || (ch >= 0xc66 && ch <= 0xc6f) + || (ch >= 0xce6 && ch <= 0xcef) || (ch >= 0xd66 && ch <= 0xd6f) + || (ch >= 0xe50 && ch <= 0xe59) || (ch >= 0xed0 && ch <= 0xed9) + || (ch >= 0xf20 && ch <= 0xf29); +} + +/* [89] Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | + [#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE] */ +static bool saxreader_is_extender(WCHAR ch) +{ + return ch == 0xb7 || ch == 0x2d0 || ch == 0x2d1 || ch == 0x387 || ch == 0x640 || ch == 0xe46 + || ch == 0xec6 || ch == 0x3005 || (ch >= 0x3031 && ch <= 0x3035) + || (ch >= 0x309d && ch <= 0x309e) || (ch >= 0x30fc && ch <= 0x30fe); +} + +/* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */ +static bool saxreader_is_namechar(WCHAR ch) +{ + return saxreader_is_letter(ch) + || saxreader_is_digit(ch) + || ch == '.' || ch == '-' || ch == '_' || ch == ':' + || saxreader_is_combiningchar(ch) + || saxreader_is_extender(ch); +} + +/* [5] NCNameChar ::= NameChar - ':' */ +static bool saxreader_is_ncnamechar(WCHAR ch) +{ + return saxreader_is_namechar(ch) && ch != ':'; +} + +/* [6] NCNameStartChar ::= Letter | '_' */ +static bool saxreader_is_ncname_startchar(WCHAR ch) +{ + return saxreader_is_letter(ch) || ch == '_'; +} + +static void *saxreader_calloc(struct saxlocator *locator, size_t count, size_t size) +{ + void *ret; + + if (!(ret = calloc(count, size))) + saxreader_set_error(locator, E_OUTOFMEMORY); + + return ret; +} + +/* [7] Nmtoken ::= (NameChar)+ */ +static void saxreader_parse_nmtoken(struct saxlocator *locator, BSTR *token) +{ + WCHAR ch = *saxreader_get_ptr(locator), *ptr; + UINT len = 0; + + *token = NULL; + + while (saxreader_is_namechar(ch)) + { + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr(locator); + ++len; + } + + if (len) + { + ptr = saxreader_get_ptr(locator); + *token = saxreader_alloc_stringlen(locator, ptr - len, len); + } + else + { + saxreader_set_error(locator, E_SAX_BADNAMECHAR); + } +} + +/* [5] Name ::= (Letter | '_' | ':') (NameChar)* + + Disallow leading ':', and optionally check for it, without stopping. */ +static void saxreader_parse_name_opt(struct saxlocator *locator, bool no_colons, BSTR *name) +{ + WCHAR ch = *saxreader_get_ptr(locator), *ptr; + UINT len, col_count = 0; + + *name = NULL; + + if (!saxreader_is_letter(ch) && ch != '_') + { + saxreader_set_error(locator, E_SAX_BADSTARTNAMECHAR); + return; + } + + len = 0; + while (saxreader_is_namechar(ch)) + { + if (ch == ':') ++col_count; + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr(locator); + ++len; + } + + if (no_colons && col_count) + { + saxreader_set_error(locator, E_SAX_CONTAINSCOLON); + } + else if (col_count > 1) + { + saxreader_set_error(locator, E_SAX_MULTIPLE_COLONS); + } + else if (len) + { + ptr = saxreader_get_ptr(locator); + *name = saxreader_alloc_stringlen(locator, ptr - len, len); + } + else + { + saxreader_set_error(locator, E_SAX_BADNAMECHAR); + } +} + +static void saxreader_parse_name(struct saxlocator *locator, BSTR *name) +{ + saxreader_parse_name_opt(locator, false, name); +} + +/* Parse the name, then abort if it contains a colon. + + Used for entities, PIs, notations and most of attribute values */ +static void saxreader_parse_name_strict(struct saxlocator *locator, BSTR *name) +{ + saxreader_parse_name_opt(locator, true, name); +} + +/* [4 NS] NCName ::= NCNameStartChar NCNameChar* */ +static BSTR saxreader_parse_ncname(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + WCHAR ch; + + if (locator->status != S_OK) + return NULL; + + ch = *saxreader_get_ptr(locator); + if (!saxreader_is_ncname_startchar(ch)) + { + saxreader_set_error(locator, E_SAX_BADSTARTNAMECHAR); + return NULL; + } + + do + { + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr(locator); + } + while (saxreader_is_ncnamechar(ch) && locator->status == S_OK); + + saxreader_shrink(locator); + + return saxreader_string_to_bstr(locator, &buffer); +} + +/* [7 NS] QName ::= PrefixedName | UnprefixedName + [8 NS] PrefixedName ::= Prefix ':' LocalPart + [9 NS] UnprefixedName ::= LocalPart + [10 NS] Prefix ::= NCName */ +static void saxreader_parse_qname(struct saxlocator *locator, struct name *name) +{ + struct string_buffer buffer = { 0 }; + BSTR ncname; + + memset(name, 0, sizeof(*name)); + + ncname = saxreader_parse_ncname(locator); + + if (saxreader_cmp(locator, L":")) + { + name->prefix = ncname; + name->local = saxreader_parse_ncname(locator); + if (*saxreader_get_ptr(locator) == ':') + saxreader_set_error(locator, E_SAX_MULTIPLE_COLONS); + } + else + { + name->local = ncname; + } + + if (name->prefix) + { + saxreader_string_append(locator, &buffer, name->prefix, SysStringLen(name->prefix)); + saxreader_string_append(locator, &buffer, L":", 1); + } + saxreader_string_append(locator, &buffer, name->local, SysStringLen(name->local)); + name->qname = saxreader_string_to_bstr(locator, &buffer); +} + +static struct element *saxreader_new_element(struct saxlocator *locator, struct name *name) +{ + struct element *element; + + if (!(element = saxreader_calloc(locator, 1, sizeof(*element)))) + return NULL; + list_add_head(&locator->elements, &element->entry); + + element->name = *name; + saxreader_clear_attributes(locator); + + return element; +} + +static void saxreader_free_name(struct name *name) +{ + SysFreeString(name->prefix); + SysFreeString(name->local); + SysFreeString(name->qname); + memset(name, 0, sizeof(*name)); +} + +static void saxreader_free_element(struct element *element) +{ + saxreader_free_name(&element->name); + free(element); +} + +static bool bstr_equal(BSTR s1, BSTR s2) +{ + if (SysStringLen(s1) != SysStringLen(s2)) return false; + return !memcmp(s1, s2, SysStringLen(s1) * sizeof(WCHAR)); +} + +static bool bstr_startswith(BSTR s, const WCHAR *prefix, UINT length) +{ + if (SysStringLen(s) < length) return false; + return !memcmp(s, prefix, length * sizeof(WCHAR)); +} + +static const struct ns *saxreader_get_namespace(struct saxlocator *locator, BSTR prefix) +{ + struct element *element; + + LIST_FOR_EACH_ENTRY(element, &locator->elements, struct element, entry) + { + for (size_t i = 0; i < element->ns.count; ++i) + { + if (bstr_equal(element->ns.entries[i].prefix, prefix)) + return &element->ns.entries[i]; + } + } + + return NULL; +} + +static void saxreader_add_namespace(struct saxlocator *locator, struct element *element, BSTR prefix, BSTR uri) +{ + struct ns *ns; + + if (bstr_startswith(prefix, L"xml", 3)) + return saxreader_set_error(locator, E_SAX_RESERVEDNAMESPACE); + + if (!saxreader_array_reserve(locator, (void **)&element->ns.entries, &element->ns.capacity, + element->ns.count + 1, sizeof(*element->ns.entries))) + { + return; + } + + ns = &element->ns.entries[element->ns.count++]; + ns->prefix = saxreader_alloc_string(locator, prefix); + ns->uri = saxreader_alloc_string(locator, uri); +} + +static bool saxreader_has_attribute(struct saxlocator *locator, BSTR name) +{ + for (size_t i = 0; i < locator->attributes.count; ++i) + { + if (bstr_equal(locator->attributes.entries[i].qname, name)) + return true; + } + + return false; +} + +static void saxreader_add_attribute(struct saxlocator *locator, const struct name *name, BSTR value, bool nsdef) +{ + struct attribute *attr; + + if (nsdef && !(locator->saxreader->features & NamespacePrefixes)) + return; + + if (saxreader_has_attribute(locator, name->qname)) + return saxreader_set_error(locator, E_SAX_DUPLICATEATTRIBUTE); + + if (!saxreader_array_reserve(locator, (void **)&locator->attributes.entries, &locator->attributes.capacity, + locator->attributes.count + 1, sizeof(*locator->attributes.entries))) + { + return; + } + + attr = &locator->attributes.entries[locator->attributes.count++]; + attr->prefix = name->prefix ? saxreader_alloc_string(locator, name->prefix) : NULL; + attr->local = saxreader_alloc_string(locator, nsdef ? L"" : name->local); + attr->qname = saxreader_alloc_string(locator, name->qname); + attr->value = saxreader_alloc_string(locator, value); + attr->nsdef = nsdef; +} + +static void saxreader_add_default_attributes(struct saxlocator *locator, struct element *element) +{ + struct attlist_decl *decl; + + LIST_FOR_EACH_ENTRY(decl, &locator->dtd.attr_decls, struct attlist_decl, entry) + { + if (bstr_equal(decl->name, element->name.qname)) + { + for (size_t i = 0; i < decl->count; ++i) + { + const struct attlist_attr *attr = &decl->attributes[i]; + + if (!saxreader_has_attribute(locator, attr->name.qname)) + saxreader_add_attribute(locator, &attr->name, attr->value, false); + } + } + } +} + +static void saxreader_set_element_uri(struct saxlocator *locator, struct element *element) +{ + struct attribute *attr; + const struct ns *ns; + + if (element->name.prefix) + { + if (!(ns = saxreader_get_namespace(locator, element->name.prefix))) + return saxreader_set_error(locator, E_SAX_UNDECLAREDPREFIX); + element->uri = ns->uri; + } + else if ((ns = saxreader_get_namespace(locator, locator->saxreader->empty_bstr))) + { + element->uri = ns->uri; + } + + for (size_t i = 0; i < locator->attributes.count; ++i) + { + attr = &locator->attributes.entries[i]; + + if (attr->nsdef) + { + attr->uri = locator->xmlns_uri; + } + else if (!attr->prefix) + { + attr->uri = locator->null_uri; + } + else if (!wcscmp(attr->prefix, L"xml")) + { + attr->uri = locator->xml_uri; + } + else if (!(ns = saxreader_get_namespace(locator, attr->prefix))) + { + return saxreader_set_error(locator, E_SAX_UNDECLAREDPREFIX); + } + else + { + attr->uri = ns->uri; + } + saxreader_free_bstr(&attr->prefix); + } +} + +/* [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] */ +static bool saxreader_is_char(UINT32 ch) +{ + return (ch == 0x9) || (ch == 0xa) || (ch == 0xd) || + (ch >= 0x20 && ch <= 0xd7ff) || + (ch >= 0xe000 && ch <= 0xfffd) || + (ch >= 0x10000 && ch <= 0x10ffff); +} + +/* [66] CharRef ::= '' [0-9]+ ';' | '' [0-9a-fA-F]+ ';' */ +static void saxreader_parse_charref(struct saxlocator *locator, struct string_buffer *buffer, + struct text_position *position) +{ + WCHAR u16[3], *ptr; + unsigned int len; + UINT32 ch = 0; + + /* Skip */ + saxreader_skip(locator, 2); + *position = locator->buffer.position; + + ptr = saxreader_get_ptr(locator); + + /* hex char or decimal */ + if (*ptr == 'x') + { + saxreader_skip(locator, 1); + ptr = saxreader_get_ptr(locator); + + while (*ptr != ';') + { + if ((*ptr >= '0' && *ptr <= '9')) + ch = ch*16 + *ptr - '0'; + else if ((*ptr >= 'a' && *ptr <= 'f')) + ch = ch*16 + *ptr - 'a' + 10; + else if ((*ptr >= 'A' && *ptr <= 'F')) + ch = ch*16 + *ptr - 'A' + 10; + else + return saxreader_set_error(locator, E_SAX_BADCHARINENTREF); + + if (ch > 0x10ffff) + return saxreader_set_error(locator, E_SAX_INVALID_UNICODE); + + saxreader_skip(locator, 1); + ptr = saxreader_get_ptr(locator); + } + } + else + { + while (*ptr != ';') + { + if (!(*ptr >= '0' && *ptr <= '9')) + return saxreader_set_error(locator, E_SAX_BADCHARINENTREF); + + ch = ch*10 + *ptr - '0'; + saxreader_skip(locator, 1); + ptr = saxreader_get_ptr(locator); + + if (ch > 0x10ffff) + return saxreader_set_error(locator, E_SAX_INVALID_UNICODE); } - This->column = 0; - for(; p>=This->pParserCtxt->input->base && *p!='\n' && *p!='\r'; p--) - This->column++; } + /* For the closing ';' */ + saxreader_skip(locator, 1); + + if (!saxreader_is_char(ch)) + return saxreader_set_error(locator, E_SAX_INVALID_UNICODE); + + len = saxreader_convert_u32_to_u16(ch, u16); + saxreader_string_append(locator, buffer, u16, len); +} + +static struct entity_decl *saxreader_get_entity(struct saxlocator *locator, BSTR name) +{ + struct entity_decl *entity; + + if (!name) + return NULL; - /* store version value, declaration has to contain version attribute */ - if (This->pParserCtxt->standalone != -1) + LIST_FOR_EACH_ENTRY(entity, &locator->dtd.entities, struct entity_decl, entry) { - SysFreeString(This->saxreader->xmldecl_version); - This->saxreader->xmldecl_version = bstr_from_xmlChar(This->pParserCtxt->version); + if (bstr_equal(entity->name, name)) + return entity; } - if (saxreader_has_handler(This, SAXContentHandler)) + return NULL; +} + +static const WCHAR *saxreader_get_predefined_entity(BSTR name) +{ + if (!wcscmp(name, L"amp")) return L"&"; + if (!wcscmp(name, L"lt")) return L"<"; + if (!wcscmp(name, L"gt")) return L">"; + if (!wcscmp(name, L"apos")) return L"'"; + if (!wcscmp(name, L"quot")) return L"\""; + return NULL; +} + +static void saxreader_stringify_entity(struct saxlocator *locator, struct string_buffer *buffer, BSTR name) +{ + struct entity_decl *entity; + const WCHAR *value; + + if ((value = saxreader_get_predefined_entity(name))) + return saxreader_string_append(locator, buffer, value, 1); + + if (!(entity = saxreader_get_entity(locator, name))) + return saxreader_set_error(locator, E_SAX_UNDEFINEDREF); + + if (entity->visited) + return saxreader_set_error(locator, E_SAX_INFINITEREFLOOP); + + for (size_t i = 0; i < entity->content.count && locator->status == S_OK; ++i) { - if(This->vbInterface) - hr = IVBSAXContentHandler_startDocument(handler->vbhandler); - else - hr = ISAXContentHandler_startDocument(handler->handler); + const struct entity_part *part = &entity->content.parts[i]; - if (sax_callback_failed(This, hr)) - format_error_message_from_id(This, hr); + if (part->reference) + { + entity->visited = true; + saxreader_stringify_entity(locator, buffer, part->value); + entity->visited = false; + } + else + { + saxreader_string_append(locator, buffer, part->value, SysStringLen(part->value)); + } } } -static void libxmlEndDocument(void *ctx) +/* [10] AttValue ::= '"' ([^<&"] | Reference)* '"' + | "'" ([^<&'] | Reference)* "'" */ +static BSTR saxreader_parse_attvalue(struct saxlocator *locator) { - saxlocator *This = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); - HRESULT hr; + struct string_buffer buffer = { 0 }; + struct text_position position; + WCHAR quote[2] = { 0 }, ch; + BSTR name; - if (This->saxreader->version >= MSXML4) { - update_position(This, FALSE); - if(This->column > 1) - This->line++; - This->column = 0; - } else { - This->column = 0; - This->line = 0; + if (!(quote[0] = saxreader_is_quote(locator))) + { + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + return NULL; } + saxreader_skip(locator, 1); - if(This->ret != S_OK) return; + /* All references are resolved */ - if (saxreader_has_handler(This, SAXContentHandler)) + while (locator->status == S_OK) { - if(This->vbInterface) - hr = IVBSAXContentHandler_endDocument(handler->vbhandler); + if (saxreader_cmp(locator, quote)) + break; + + if (saxreader_cmp(locator, L"<")) + { + saxreader_set_error(locator, E_SAX_BADCHARINSTRING); + break; + } + + if (saxreader_peek(locator, L"", 2)) + { + saxreader_parse_charref(locator, &buffer, &position); + } + else if (saxreader_peek(locator, L"&", 1)) + { + /* Skip '&' */ + saxreader_skip(locator, 1); + + saxreader_parse_name(locator, &name); + if (saxreader_cmp(locator, L";")) + saxreader_stringify_entity(locator, &buffer, name); + else + saxreader_set_error(locator, E_SAX_INVALID_UNICODE); + SysFreeString(name); + } else - hr = ISAXContentHandler_endDocument(handler->handler); + { + ch = *saxreader_get_ptr_noread(locator); + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + } + } + + if (locator->status != S_OK) return NULL; + + return saxreader_string_to_bstr(locator, &buffer); +} + +static bool saxreader_add_namespace_attribute(struct saxlocator *locator, struct element *element, + struct name *name, BSTR value) +{ + if (name->prefix && !wcscmp(name->prefix, L"xmlns")) + saxreader_add_namespace(locator, element, name->local, value); + else if (!name->prefix && name->local && !wcscmp(name->local, L"xmlns")) + saxreader_add_namespace(locator, element, locator->saxreader->empty_bstr, value); + else + return false; + + return true; +} + +/* [15 NS] Attribute ::= NSAttName Eq AttValue | QName Eq AttValue */ +static void saxreader_parse_attribute(struct saxlocator *locator, struct element *element) +{ + struct name name; + BSTR value; + bool ns; + + saxreader_parse_qname(locator, &name); + saxreader_skipspaces(locator); + if (!saxreader_cmp(locator, L"=")) + saxreader_set_error(locator, E_SAX_MISSINGEQUALS); + saxreader_skipspaces(locator); + value = saxreader_parse_attvalue(locator); + + ns = saxreader_add_namespace_attribute(locator, element, &name, value); + saxreader_add_attribute(locator, &name, value, !!ns); - if (sax_callback_failed(This, hr)) - format_error_message_from_id(This, hr); + saxreader_free_name(&name); + SysFreeString(value); +} + +static void saxreader_reorder_attributes(struct saxlocator *locator) +{ + size_t count, i, idx; + + if (locator->saxreader->version < MSXML4) + return; + if (!(locator->saxreader->features & Namespaces)) + return; + + count = locator->attributes.count; + if (!(locator->attributes.map = saxreader_calloc(locator, count, sizeof(*locator->attributes.map)))) + return; + + /* Regular attributes first, then namespace definitions. */ + + for (i = 0, idx = 0; i < count; ++i) + { + if (locator->attributes.entries[i].nsdef) continue; + locator->attributes.map[idx++] = i; + } + + for (i = 0; i < count; ++i) + { + if (!locator->attributes.entries[i].nsdef) continue; + locator->attributes.map[idx++] = i; } } -static void libxmlStartElementNS( - void *ctx, - const xmlChar *localname, - const xmlChar *prefix, - const xmlChar *URI, - int nb_namespaces, - const xmlChar **namespaces, - int nb_attributes, - int nb_defaulted, - const xmlChar **attributes) +/* [12 NS] STag ::= '<' QName (S Attribute)* S? '>' + [14 NS] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>' */ +static void saxreader_parse_starttag(struct saxlocator *locator) { - saxlocator *This = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); - element_entry *element; - HRESULT hr = S_OK; - BSTR uri; + struct text_position position = { 0 }; + bool empty_element = false; + struct element *element; + struct name name; - update_position(This, TRUE); - if(*(This->pParserCtxt->input->cur) == '/') - This->column++; - if(This->saxreader->version < MSXML4) - This->column++; + if (!saxreader_cmp(locator, L"<")) + return saxreader_set_error(locator, E_SAX_INVALIDATROOTLEVEL); - element = alloc_element_entry(localname, prefix, nb_namespaces, namespaces); - push_element_ns(This, element); + saxreader_parse_qname(locator, &name); - if (is_namespaces_enabled(This->saxreader)) + if (bstr_startswith(name.prefix, L"xml", 3)) { - int i; + saxreader_free_name(&name); + return saxreader_set_error(locator, E_SAX_RESERVEDNAMESPACE); + } + + if (!(element = saxreader_new_element(locator, &name))) + { + saxreader_free_name(&name); + return; + } + + while (locator->status == S_OK) + { + saxreader_skipspaces(locator); - for (i = 0; i < nb_namespaces && saxreader_has_handler(This, SAXContentHandler); i++) + if (saxreader_cmp(locator, L"/>")) { - if (This->vbInterface) - hr = IVBSAXContentHandler_startPrefixMapping( - handler->vbhandler, - &element->ns[i].prefix, - &element->ns[i].uri); - else - hr = ISAXContentHandler_startPrefixMapping( - handler->handler, - element->ns[i].prefix, - SysStringLen(element->ns[i].prefix), - element->ns[i].uri, - SysStringLen(element->ns[i].uri)); + empty_element = true; + position = locator->buffer.position; + break; + } - if (sax_callback_failed(This, hr)) - { - format_error_message_from_id(This, hr); - return; - } + if (saxreader_cmp(locator, L">")) + { + position = locator->buffer.position; + break; } + + saxreader_parse_attribute(locator, element); + } + + /* Append default attributes from DTD. */ + saxreader_add_default_attributes(locator, element); + saxreader_reorder_attributes(locator); + + /* The 'uri' string pointers are reused from the namespace stacks. + Namespace definitions can appear after attributes that are using them, + so we have to reiterate after all attributes were parsed. */ + saxreader_set_element_uri(locator, element); + + saxlocator_start_element(locator, &position, element); + if (empty_element) + { + saxlocator_end_element(locator, &position, element); + list_remove(&element->entry); + saxreader_free_element(element); } +} + +/* [13 NS] ETag ::= '</' QName S? '>' */ +static void saxreader_parse_endtag(struct saxlocator *locator) +{ + struct text_position position; + struct element *element; + HRESULT hr = S_OK; + struct name name; + + /* Skip '</' */ + saxreader_skip(locator, 2); + position = locator->buffer.position; + + saxreader_parse_qname(locator, &name); + saxreader_skipspaces(locator); + + if (!saxreader_cmp(locator, L">")) + hr = E_SAX_UNCLOSEDENDTAG; + else if (list_empty(&locator->elements)) + hr = E_SAX_UNEXPECTEDENDTAG; - uri = find_element_uri(This, URI); - hr = SAXAttributes_populate(This, nb_namespaces, namespaces, nb_attributes, attributes); - if (hr == S_OK && saxreader_has_handler(This, SAXContentHandler)) + if (SUCCEEDED(hr)) { - BSTR local; + element = LIST_ENTRY(list_head(&locator->elements), struct element, entry); - if (is_namespaces_enabled(This->saxreader)) - local = element->local; + if (!wcscmp(element->name.qname, name.qname)) + saxlocator_end_element(locator, &position, element); else - uri = local = NULL; + hr = E_SAX_ENDTAGMISMATCH; + + list_remove(&element->entry); + saxreader_free_element(element); + } + + saxreader_free_name(&name); + saxreader_set_error(locator, hr); +} + +static bool saxreader_is_xml_pi(BSTR name) +{ + if (SysStringLen(name) != 3) return false; + return (name[0] == 'x' || name[0] == 'X') + && (name[1] == 'm' || name[1] == 'M') + && (name[2] == 'l' || name[2] == 'L'); +} + +/* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' + [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) */ +static void saxreader_parse_pi(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + struct text_position position; + BSTR target, chars; + WCHAR ch; + + /* Skip '<?' */ + saxreader_skip(locator, 2); + saxreader_parse_name_strict(locator, &target); + + if (saxreader_is_xml_pi(target)) + { + SysFreeString(target); + return saxreader_set_error(locator, E_SAX_PIDECLSYNTAX); + } + + saxreader_skipspaces(locator); + position = locator->buffer.position; + + ch = *saxreader_get_ptr_noread(locator); + while (ch && locator->status == S_OK) + { + if (saxreader_cmp(locator, L"?>")) + { + chars = saxreader_string_to_bstr(locator, &buffer); + + saxlocator_pi(locator, &position, target, chars); + + SysFreeString(target); + SysFreeString(chars); + + return; + } + + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr_noread(locator); + } + + SysFreeString(target); + saxreader_set_error(locator, E_SAX_UNCLOSEDPI); +} + +/* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */ +static void saxreader_parse_comment(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + struct text_position position; + BSTR chars; + WCHAR ch; + + /* Skip <!-- */ + saxreader_skip(locator, 4); + position = locator->buffer.position; - if (This->vbInterface) + ch = *saxreader_get_ptr_noread(locator); + while (ch && locator->status == S_OK) + { + if (saxreader_cmp(locator, L"-->")) + { + chars = saxreader_string_to_bstr(locator, &buffer); + saxlocator_comment(locator, &position, chars); + SysFreeString(chars); + return; + } + else if (saxreader_cmp(locator, L"--")) { - if (!uri) uri = This->saxreader->empty_bstr; - hr = IVBSAXContentHandler_startElement(handler->vbhandler, - &uri, &local, &element->qname, &This->IVBSAXAttributes_iface); + saxreader_set_error(locator, E_SAX_COMMENTSYNTAX); + break; } else { - hr = ISAXContentHandler_startElement(handler->handler, - uri ? uri : &empty_str, SysStringLen(uri), - local ? local : &empty_str, SysStringLen(local), - element->qname, SysStringLen(element->qname), - &This->ISAXAttributes_iface); + saxreader_string_append(locator, &buffer, &ch, 1); } - if (sax_callback_failed(This, hr)) - format_error_message_from_id(This, hr); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr_noread(locator); } + + free(buffer.data); + saxreader_set_error(locator, E_SAX_UNEXPECTED_EOF); } -static void libxmlEndElementNS( - void *ctx, - const xmlChar *localname, - const xmlChar *prefix, - const xmlChar *URI) +static bool saxreader_predefined_entity(struct saxlocator *locator, const struct text_position *position, BSTR name) { - saxlocator *This = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); - element_entry *element; - const xmlChar *p; - BSTR uri, local; - HRESULT hr; + const WCHAR *value; + BSTR str; - update_position(This, FALSE); - p = This->pParserCtxt->input->cur; + if (locator->status != S_OK) + return false; - if (This->saxreader->version >= MSXML4) + if (!(value = saxreader_get_predefined_entity(name))) + return false; + + str = SysAllocString(value); + saxlocator_characters(locator, position, str); + SysFreeString(str); + + return true; +} + +static void saxreader_push_entity(struct saxlocator *locator, const struct text_position *position, + struct entity_decl *entity) +{ + struct encoded_buffer *buffer = &locator->buffer.utf16; + ULONG needed = SysStringLen(entity->value); + void *dst, *src; + size_t size; + + if (entity->visited) + return saxreader_set_error(locator, E_SAX_INFINITEREFLOOP); + + /* Squeeze entity value at current input position. Empty entities are valid. */ + if (needed) { - p--; - while(p>This->pParserCtxt->input->base && *p!='>') + if (!saxreader_array_reserve(locator, (void **)&buffer->data, &buffer->allocated, + buffer->written + needed * sizeof(WCHAR), sizeof(*buffer->data))) + { + return saxreader_set_error(locator, E_OUTOFMEMORY); + } + + if (buffer->written > buffer->cur * sizeof(WCHAR)) { - if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) - This->line--; - p--; + dst = (WCHAR *)buffer->data + buffer->cur + needed; + src = (WCHAR *)buffer->data + buffer->cur; + size = buffer->written - buffer->cur * sizeof(WCHAR); + memmove(dst, src, size); } + + dst = (WCHAR *)buffer->data + buffer->cur; + src = entity->value; + size = needed * sizeof(WCHAR); + memcpy(dst, src, size); + + buffer->written += needed * sizeof(WCHAR); + } + + entity->visited = true; + entity->remaining = needed; + entity->position = *position; + + /* Recursive referencing is explicitly detected, making sure that entities could only be linked once. */ + list_add_head(&locator->buffer.entities, &entity->input_entry); + + saxlocator_start_entity(locator, &entity->position, entity->name); +} + +/* [66] CharRef ::= '' [0-9]+ ';' | '' [0-9a-fA-F]+ ';' + [67] Reference ::= EntityRef | CharRef + [68] EntityRef ::= '&' Name ';' */ +static void saxreader_parse_reference(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + struct text_position position; + struct entity_decl *entity; + BSTR str; + + if (saxreader_peek(locator, L"", 2)) + { + saxreader_parse_charref(locator, &buffer, &position); + str = saxreader_string_to_bstr(locator, &buffer); + saxlocator_characters(locator, &position, str); } - else if(*(p-1)!='>' || *(p-2)!='/') + else { - p--; - while(p-2>=This->pParserCtxt->input->base - && *(p-2)!='<' && *(p-1)!='/') + /* Skip '&' */ + saxreader_skip(locator, 1); + position = locator->buffer.position; + + saxreader_parse_name(locator, &str); + if (!saxreader_cmp(locator, L";")) + saxreader_set_error(locator, E_SAX_MISSINGSEMICOLON); + + if (!saxreader_predefined_entity(locator, &position, str)) { - if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) - This->line--; - p--; + if ((entity = saxreader_get_entity(locator, str))) + { + if (entity->unparsed) + saxreader_set_error(locator, E_SAX_UNPARSEDENTITYREF); + else if (*entity->value) + saxreader_push_entity(locator, &position, entity); + else + saxlocator_skipped_entity(locator, &position, entity->name); + } + else + { + saxlocator_skipped_entity(locator, &position, str); + } } } - This->column = 0; - for(; p>=This->pParserCtxt->input->base && *p!='\n' && *p!='\r'; p--) - This->column++; + SysFreeString(str); +} + +struct chardata_context +{ + struct string_buffer buffer; + struct text_position position; + size_t count; + WCHAR ch; +}; - uri = find_element_uri(This, URI); - element = pop_element_ns(This); +static void saxreader_parse_characters(struct saxlocator *locator, struct chardata_context *ctxt) +{ + BSTR chars; - if (!saxreader_has_handler(This, SAXContentHandler)) + /* Normalize line breaks */ + if (ctxt->ch == '\r') { - free_attribute_values(This); - This->attr_count = 0; - free_element_entry(element); - return; - } + bool move_position = false; + + saxreader_skip(locator, 1); + ctxt->ch = *saxreader_get_ptr_noread(locator); + if (ctxt->ch == '\n') + saxreader_skip(locator, 1); + else if (ctxt->ch != '\r') + move_position = true; + + saxreader_string_append(locator, &ctxt->buffer, L"\n", 1); + chars = saxreader_string_to_bstr(locator, &ctxt->buffer); - if (is_namespaces_enabled(This->saxreader)) - local = element->local; + saxlocator_characters(locator, &ctxt->position, chars); + SysFreeString(chars); + + saxreader_shrink(locator); + + if (move_position) + ctxt->position = locator->buffer.position; + else + ctxt->position.column += (locator->buffer.consumed - ctxt->count); + + ctxt->count = locator->buffer.consumed; + } else - uri = local = NULL; + { + saxreader_string_append(locator, &ctxt->buffer, &ctxt->ch, 1); + saxreader_skip(locator, 1); + } - if (This->vbInterface) + ctxt->ch = *saxreader_get_ptr(locator); +} + +static void saxreader_parse_characters_newparser(struct saxlocator *locator, struct chardata_context *ctxt) +{ + BSTR chars; + + /* Normalize line breaks */ + if (ctxt->ch == '\r') { - if (!uri) uri = This->saxreader->empty_bstr; - hr = IVBSAXContentHandler_endElement( - handler->vbhandler, - &uri, &local, &element->qname); + bool move_position = false; + + saxreader_skip(locator, 1); + ctxt->ch = *saxreader_get_ptr_noread(locator); + if (ctxt->ch == '\n') + saxreader_skip(locator, 1); + else if (ctxt->ch != '\r') + move_position = true; + + /* Flush current block first, then new line separately */ + chars = saxreader_string_to_bstr(locator, &ctxt->buffer); + saxlocator_characters(locator, &ctxt->position, chars); + SysFreeString(chars); + + saxreader_shrink(locator); + + ctxt->position = locator->buffer.position; + ctxt->position.column = 0; + saxreader_string_append(locator, &ctxt->buffer, L"\n", 1); + chars = saxreader_string_to_bstr(locator, &ctxt->buffer); + saxlocator_characters(locator, &ctxt->position, chars); + SysFreeString(chars); + + if (move_position) + ctxt->position = locator->buffer.position; + else + ctxt->position.column += (locator->buffer.consumed - ctxt->count); + + ctxt->count = locator->buffer.consumed; } else { - hr = ISAXContentHandler_endElement( - handler->handler, - uri ? uri : &empty_str, SysStringLen(uri), - local ? local : &empty_str, SysStringLen(local), - element->qname, SysStringLen(element->qname)); + saxreader_string_append(locator, &ctxt->buffer, &ctxt->ch, 1); + saxreader_skip(locator, 1); + } + + ctxt->ch = *saxreader_get_ptr(locator); +} + +/* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) */ +static void saxreader_parse_chardata(struct saxlocator *locator) +{ + struct chardata_context context = { 0 }; + BSTR chars; + + context.position = locator->buffer.position; + context.count = locator->buffer.consumed; + context.ch = *saxreader_get_ptr(locator); + + while (context.ch) + { + if (saxreader_cmp(locator, L"]]>")) + { + saxreader_set_error(locator, E_SAX_INVALID_CDATACLOSINGTAG); + break; + } + + if (context.ch == '&' || context.ch == '<') + { + if (context.buffer.count) + { + if (locator->saxreader->version >= MSXML4) + { + context.position.line = locator->buffer.position.line; + context.position.column = locator->buffer.position.column; + } + + chars = saxreader_string_to_bstr(locator, &context.buffer); + saxlocator_characters(locator, &context.position, chars); + SysFreeString(chars); + } + else + { + free(context.buffer.data); + } + return; + } + + if (locator->saxreader->version >= MSXML4) + saxreader_parse_characters_newparser(locator, &context); + else + saxreader_parse_characters(locator, &context); + } + + free(context.buffer.data); + saxreader_set_error(locator, E_SAX_UNEXPECTED_EOF); +} + +/* [18] CDSect ::= CDStart CData CDEnd + [19] CDStart ::= '<![CDATA[' + [20] CData ::= (Char* - (Char* ']]>' Char*)) + [21] CDEnd ::= ']]>' */ +static void saxreader_parse_cdata(struct saxlocator *locator) +{ + struct chardata_context context = { 0 }; + BSTR chars; + + /* Skip <![CDATA[ */ + saxreader_skip(locator, 9); + + context.position = locator->buffer.position; + context.count = locator->buffer.consumed; + context.ch = *saxreader_get_ptr(locator); + + saxlocator_start_cdata(locator, &context.position); + + while (context.ch && locator->status == S_OK) + { + if (saxreader_cmp(locator, L"]]>")) + { + /* Point to closing '>' */ + if (locator->saxreader->version >= MSXML4) + { + context.position.line = locator->buffer.position.line; + context.position.column = locator->buffer.position.column - 1; + } + + chars = saxreader_string_to_bstr(locator, &context.buffer); + saxlocator_characters(locator, &context.position, chars); + SysFreeString(chars); + + return saxlocator_end_cdata(locator, &context.position); + } + + if (locator->saxreader->version >= MSXML4) + saxreader_parse_characters_newparser(locator, &context); + else + saxreader_parse_characters(locator, &context); } - free_attribute_values(This); - This->attr_count = 0; + free(context.buffer.data); + saxreader_set_error(locator, E_SAX_UNCLOSEDCDATA); +} + +/* [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' */ +static void saxreader_parse_mixed_contentspec(struct saxlocator *locator, struct string_buffer *buffer) +{ + BSTR name; + WCHAR ch; - if (sax_callback_failed(This, hr)) + saxreader_string_append(locator, buffer, L"#PCDATA", 7); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L")")) { - format_error_message_from_id(This, hr); - free_element_entry(element); + saxreader_string_append(locator, buffer, L")", 1); + if (saxreader_cmp(locator, L"*")) + saxreader_string_append(locator, buffer, L"*", 1); return; } - if (is_namespaces_enabled(This->saxreader)) + ch = *saxreader_get_ptr_noread(locator); + while (ch == '|' && locator->status == S_OK) { - int i = -1; - while (iterate_endprefix_index(This, element, &i) && saxreader_has_handler(This, SAXContentHandler)) - { - if (This->vbInterface) - hr = IVBSAXContentHandler_endPrefixMapping( - handler->vbhandler, &element->ns[i].prefix); - else - hr = ISAXContentHandler_endPrefixMapping( - handler->handler, element->ns[i].prefix, SysStringLen(element->ns[i].prefix)); - - if (sax_callback_failed(This, hr)) break; - } - - if (sax_callback_failed(This, hr)) - format_error_message_from_id(This, hr); + saxreader_skip(locator, 1); + saxreader_string_append(locator, buffer, L"|", 1); + saxreader_skipspaces(locator); + saxreader_parse_name(locator, &name); + saxreader_string_append_bstr(locator, buffer, &name); + saxreader_skipspaces(locator); + ch = *saxreader_get_ptr_noread(locator); } - free_element_entry(element); + if (!saxreader_cmp(locator, L")")) + saxreader_set_error(locator, E_SAX_BADCHARINMIXEDMODEL); + else if (!saxreader_cmp(locator, L"*")) + saxreader_set_error(locator, E_SAX_MISSING_STAR); + saxreader_string_append(locator, buffer, L")*", 2); } -static void libxmlCharacters( - void *ctx, - const xmlChar *ch, - int len) -{ - saxlocator *This = ctx; - BSTR Chars; - HRESULT hr; - xmlChar *cur, *end; - BOOL lastEvent = FALSE; +static void saxreader_parse_children_contentspec(struct saxlocator *locator, struct string_buffer *buffer); - if (!saxreader_has_handler(This, SAXContentHandler)) return; +static void saxreader_parse_occurence_spec(struct saxlocator *locator, struct string_buffer *buffer) +{ + WCHAR ch; - update_position(This, FALSE); - cur = (xmlChar*)This->pParserCtxt->input->cur; - while(cur>=This->pParserCtxt->input->base && *cur!='>') + ch = *saxreader_get_ptr(locator); + if (ch == '?' || ch == '*' || ch == '+') { - if(*cur=='\n' || (*cur=='\r' && *(cur+1)!='\n')) - This->line--; - cur--; + saxreader_string_append(locator, buffer, &ch, 1); + saxreader_skip(locator, 1); } - This->column = 1; - for(; cur>=This->pParserCtxt->input->base && *cur!='\n' && *cur!='\r'; cur--) - This->column++; +} - cur = (xmlChar*)ch; - if(*(ch-1)=='\r') cur--; - end = cur; +static void saxreader_parse_child_contentspec(struct saxlocator *locator, struct string_buffer *buffer) +{ + BSTR name; - while(1) + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L"(")) { - while(end-ch<len && *end!='\r') end++; - if(end-ch==len) - { - lastEvent = TRUE; - } - else - { - *end = '\n'; - end++; - } + saxreader_string_append(locator, buffer, L"(", 1); + saxreader_skipspaces(locator); + saxreader_parse_children_contentspec(locator, buffer); + saxreader_skipspaces(locator); + } + else + { + saxreader_parse_name(locator, &name); + saxreader_string_append_bstr(locator, buffer, &name); + saxreader_parse_occurence_spec(locator, buffer); + } + saxreader_skipspaces(locator); +} - if (This->saxreader->version >= MSXML4) - { - xmlChar *p; +static void saxreader_parse_children_contentspec(struct saxlocator *locator, struct string_buffer *buffer) +{ + WCHAR ch, sep = 0; - for(p=cur; p!=end; p++) - { - if(*p=='\n') - { - This->line++; - This->column = 1; - } - else - { - This->column++; - } - } + /* TODO: limit recursion depth */ - if(!lastEvent) - This->column = 0; - } + if (locator->status != S_OK) + return; - Chars = pooled_bstr_from_xmlCharN(&This->saxreader->pool, cur, end-cur); - hr = saxreader_saxcharacters(This, Chars); + saxreader_parse_child_contentspec(locator, buffer); - if (sax_callback_failed(This, hr)) + ch = *saxreader_get_ptr(locator); + while (ch != ')' && locator->status == S_OK) + { + if (ch == ',') { - format_error_message_from_id(This, hr); - return; - } + /* Check for "Name | Name , Name" */ + if (sep == 0) sep = ch; + else if (sep != ch) + return saxreader_set_error(locator, E_SAX_INVALID_MODEL); - if (This->saxreader->version < MSXML4) - This->column += end-cur; - - if(lastEvent) - break; + saxreader_skip(locator, 1); + saxreader_string_append(locator, buffer, &ch, 1); + } + else if (ch == '|') + { + /* Check for "Name , Name | Name" */ + if (sep == 0) sep = ch; + else if (sep != ch) + return saxreader_set_error(locator, E_SAX_INVALID_MODEL); - *(end-1) = '\r'; - if(*end == '\n') + saxreader_skip(locator, 1); + saxreader_string_append(locator, buffer, &ch, 1); + } + else { - end++; - This->column++; + return saxreader_set_error(locator, E_SAX_INVALID_MODEL); } - cur = end; - if(end-ch == len) break; + saxreader_parse_child_contentspec(locator, buffer); + ch = *saxreader_get_ptr(locator); } + + saxreader_skip(locator, 1); + saxreader_string_append(locator, buffer, L")", 1); + saxreader_parse_occurence_spec(locator, buffer); } -static void libxmlSetDocumentLocator( - void *ctx, - xmlSAXLocatorPtr loc) +/* [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children + [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' */ +static void saxreader_parse_contentspec(struct saxlocator *locator, BSTR *model) { - saxlocator *This = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); - HRESULT hr = S_OK; + struct string_buffer buffer = { 0 }; - if (saxreader_has_handler(This, SAXContentHandler)) + *model = NULL; + + if (saxreader_cmp(locator, L"EMPTY")) { - if(This->vbInterface) - hr = IVBSAXContentHandler_putref_documentLocator(handler->vbhandler, - &This->IVBSAXLocator_iface); - else - hr = ISAXContentHandler_putDocumentLocator(handler->handler, &This->ISAXLocator_iface); + *model = saxreader_alloc_string(locator, L"EMPTY"); } - - if(FAILED(hr)) - format_error_message_from_id(This, hr); -} - -static void libxmlComment(void *ctx, const xmlChar *value) -{ - saxlocator *This = ctx; - struct saxlexicalhandler_iface *handler = saxreader_get_lexicalhandler(This->saxreader); - BSTR bValue; - HRESULT hr; - const xmlChar *p = This->pParserCtxt->input->cur; - - update_position(This, FALSE); - while(p-4>=This->pParserCtxt->input->base - && memcmp(p-4, "<!--", sizeof(char[4]))) + else if (saxreader_cmp(locator, L"ANY")) { - if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) - This->line--; - p--; + *model = saxreader_alloc_string(locator, L"ANY"); } + else if (saxreader_cmp(locator, L"(")) + { + saxreader_skipspaces(locator); - This->column = 0; - for(; p>=This->pParserCtxt->input->base && *p!='\n' && *p!='\r'; p--) - This->column++; + saxreader_string_append(locator, &buffer, L"(", 1); + if (saxreader_cmp(locator, L"#PCDATA")) + saxreader_parse_mixed_contentspec(locator, &buffer); + else + saxreader_parse_children_contentspec(locator, &buffer); - if (!saxreader_has_handler(This, SAXLexicalHandler)) return; + *model = saxreader_string_to_bstr(locator, &buffer); + } + else + { + saxreader_set_error(locator, E_SAX_INVALID_MODEL); + } +} - bValue = pooled_bstr_from_xmlChar(&This->saxreader->pool, value); +/* [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' */ +static void saxreader_parse_elementdecl(struct saxlocator *locator) +{ + BSTR name, model; - if (This->vbInterface) - hr = IVBSAXLexicalHandler_comment(handler->vbhandler, &bValue); + /* Skip <!ELEMENT */ + saxreader_skip(locator, 9); + saxreader_skip_required_spaces(locator); + saxreader_parse_name(locator, &name); + saxreader_skip_required_spaces(locator); + saxreader_parse_contentspec(locator, &model); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + saxlocator_elementdecl(locator, name, model); else - hr = ISAXLexicalHandler_comment(handler->handler, bValue, SysStringLen(bValue)); + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); - if(FAILED(hr)) - format_error_message_from_id(This, hr); + SysFreeString(name); + SysFreeString(model); } -static void WINAPIV libxmlFatalError(void *ctx, const char *msg, ...) +/* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */ +static bool saxreader_is_pubidchar(WCHAR ch) { - saxlocator *This = ctx; - struct saxerrorhandler_iface *handler = saxreader_get_errorhandler(This->saxreader); - char message[1024]; - WCHAR *error; - DWORD len; - va_list args; - - if(This->ret != S_OK) { - xmlStopParser(This->pParserCtxt); - return; - } + return (ch == 0x20) || (ch == 0xd) || (ch == 0xa) || + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + (ch >= '-' && ch <= ';') || /* '()*+,-./:; */ + (ch == '=') || (ch == '?') || + (ch == '@') || (ch == '!') || + (ch == '#') || (ch == '$') || (ch == '%') || + (ch == '_'); +} - va_start(args, msg); - vsprintf(message, msg, args); - va_end(args); +/* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" + [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */ +static BSTR saxreader_parse_pubidliteral(struct saxlocator *locator) +{ + struct string_buffer buffer = { 0 }; + WCHAR quote, ch; + BSTR str; - len = MultiByteToWideChar(CP_UNIXCP, 0, message, -1, NULL, 0); - error = malloc(sizeof(WCHAR) * len); - if(error) + if (!(quote = saxreader_is_quote(locator))) { - MultiByteToWideChar(CP_UNIXCP, 0, message, -1, error, len); - TRACE("fatal error for %p: %s\n", This, debugstr_w(error)); + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + return NULL; } - if (!saxreader_has_handler(This, SAXErrorHandler)) + saxreader_skip(locator, 1); + saxreader_more(locator); + + ch = *saxreader_get_ptr_noread(locator); + while (saxreader_is_pubidchar(ch) && ch != quote) { - xmlStopParser(This->pParserCtxt); - This->ret = E_FAIL; - free(error); - return; + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr_noread(locator); } - FIXME("Error handling is not compatible.\n"); - - if(This->vbInterface) + if (ch == quote) { - BSTR bstrError = SysAllocString(error); - IVBSAXErrorHandler_fatalError(handler->vbhandler, &This->IVBSAXLocator_iface, - &bstrError, E_FAIL); - SysFreeString(bstrError); + str = saxreader_string_to_bstr(locator, &buffer); + /* Skip closing quote */ + saxreader_skip(locator, 1); + return str; } - else - ISAXErrorHandler_fatalError(handler->handler, &This->ISAXLocator_iface, error, E_FAIL); - free(error); - - xmlStopParser(This->pParserCtxt); - This->ret = E_FAIL; + free(buffer.data); + saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); + return NULL; } -/* The only reason this helper exists is that CDATA section are reported by chunks, - newlines are used as delimiter. More than that, reader even alters input data before reporting. - - This helper should be called for substring with trailing newlines. -*/ -static BSTR saxreader_get_cdata_chunk(const xmlChar *str, int len) +/* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */ +static BSTR saxreader_parse_systemliteral(struct saxlocator *locator) { - BSTR bstr = bstr_from_xmlCharN(str, len), ret; - WCHAR *ptr; + struct string_buffer buffer = { 0 }; + WCHAR quote, ch; - len = SysStringLen(bstr); - ptr = bstr + len - 1; - while ((*ptr == '\r' || *ptr == '\n') && ptr >= bstr) - ptr--; - - while (*++ptr) + if (!(quote = saxreader_is_quote(locator))) { - /* replace returns as: + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + return NULL; + } + + saxreader_skip(locator, 1); + saxreader_more(locator); - - "\r<char>" -> "\n<char>" - - "\r\r" -> "\r" - - "\r\n" -> "\n" - */ - if (*ptr == '\r') + ch = *saxreader_get_ptr_noread(locator); + while (ch) + { + if (ch == quote) { - if (*(ptr+1) == '\r' || *(ptr+1) == '\n') - { - /* shift tail */ - memmove(ptr, ptr+1, len-- - (ptr-bstr)); - } - else - *ptr = '\n'; + saxreader_skip(locator, 1); + return saxreader_string_to_bstr(locator, &buffer); } + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr_noread(locator); } - ret = SysAllocStringLen(bstr, len); - SysFreeString(bstr); - return ret; + free(buffer.data); + saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); + return NULL; } -static void libxml_cdatablock(void *ctx, const xmlChar *value, int len) +/* [75] ExternalID ::= 'SYSTEM' S SystemLiteral + | 'PUBLIC' S PubidLiteral S SystemLiteral + [83] PublicID ::= 'PUBLIC' S PubidLiteral */ +static void saxreader_parse_externalid(struct saxlocator *locator, BSTR *systemid, BSTR *publicid, bool is_notation) { - const xmlChar *start, *end; - saxlocator *locator = ctx; - struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); - HRESULT hr = S_OK; - BSTR chars; - int i; + *systemid = *publicid = NULL; - update_position(locator, FALSE); - if (saxreader_has_handler(locator, SAXLexicalHandler)) + if (saxreader_cmp(locator, L"SYSTEM")) { - if (locator->vbInterface) - hr = IVBSAXLexicalHandler_startCDATA(lexical->vbhandler); - else - hr = ISAXLexicalHandler_startCDATA(lexical->handler); - } + if (!saxreader_skip_required_spaces(locator)) + return; - if(FAILED(hr)) - { - format_error_message_from_id(locator, hr); - return; + *systemid = saxreader_parse_systemliteral(locator); } + else if (saxreader_cmp(locator, L"PUBLIC")) + { + if (!saxreader_skip_required_spaces(locator)) + return; - start = value; - end = NULL; - i = 0; + *publicid = saxreader_parse_pubidliteral(locator); - while (i < len) - { - /* scan for newlines */ - if (value[i] == '\r' || value[i] == '\n') + if (is_notation) + { + if (!saxreader_skipspaces(locator)) + return; + if (!saxreader_is_quote(locator)) + return; + } + else { - /* skip newlines/linefeeds */ - while (i < len) + if (!saxreader_skip_required_spaces(locator)) { - if (value[i] != '\r' && value[i] != '\n') break; - i++; + SysFreeString(*publicid); + *publicid = NULL; + return; } - end = &value[i]; - - /* report */ - chars = saxreader_get_cdata_chunk(start, end-start); - TRACE("(chunk %s)\n", debugstr_w(chars)); - hr = saxreader_saxcharacters(locator, chars); - SysFreeString(chars); - - start = &value[i]; - end = NULL; } - i++; - locator->column++; - } - - /* no newline chars (or last chunk) report as a whole */ - if (!end && start == value) - { - /* report */ - chars = bstr_from_xmlCharN(start, len-(start-value)); - TRACE("(%s)\n", debugstr_w(chars)); - hr = saxreader_saxcharacters(locator, chars); - SysFreeString(chars); - } - if (saxreader_has_handler(locator, SAXLexicalHandler)) - { - if (locator->vbInterface) - hr = IVBSAXLexicalHandler_endCDATA(lexical->vbhandler); - else - hr = ISAXLexicalHandler_endCDATA(lexical->handler); + *systemid = saxreader_parse_systemliteral(locator); } - - if(FAILED(hr)) - format_error_message_from_id(locator, hr); } -static void libxml_pi(void *ctx, const xmlChar *_target, const xmlChar *_data) +/* Read EntityRef without much validation */ +static void saxreader_parse_entityvalue_entityref(struct saxlocator *locator, struct string_buffer *buffer) { - saxlocator *locator = ctx; - struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(locator->saxreader); - BSTR target, data; - HRESULT hr = S_OK; - - update_position(locator, FALSE); - if (!saxreader_has_handler(locator, SAXContentHandler)) return; - - target = pooled_bstr_from_xmlChar(&locator->saxreader->pool, _target); - data = pooled_bstr_from_xmlChar(&locator->saxreader->pool, _data); - - if (locator->vbInterface) - hr = IVBSAXContentHandler_processingInstruction(handler->vbhandler, &target, &data); - else - hr = ISAXContentHandler_processingInstruction(handler->handler, target, SysStringLen(target), - data, SysStringLen(data)); + WCHAR ch; - if (FAILED(hr)) - format_error_message_from_id(locator, hr); -} - -static void libxml_internalsubset(void *ctx, const xmlChar *_name, const xmlChar *external_id, const xmlChar *system_id) -{ - saxlocator *locator = ctx; - struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); - BSTR name, pubid, sysid; - HRESULT hr; + saxreader_string_append(locator, buffer, L"&", 1); + saxreader_skip(locator, 1); - update_position(locator, FALSE); - if (!saxreader_has_handler(locator, SAXLexicalHandler)) return; + /* Check for empty name */ + ch = *saxreader_get_ptr_noread(locator); + if (!saxreader_is_namechar(ch)) + saxreader_set_error(locator, E_SAX_BADCHARINENTREF); - name = pooled_bstr_from_xmlChar(&locator->saxreader->pool, _name); - pubid = pooled_bstr_from_xmlChar(&locator->saxreader->pool, external_id); - sysid = pooled_bstr_from_xmlChar(&locator->saxreader->pool, system_id); + while (saxreader_is_namechar(ch)) + { + saxreader_string_append(locator, buffer, &ch, 1); + saxreader_skip(locator, 1); + ch = *saxreader_get_ptr_noread(locator); + } - if (locator->vbInterface) - hr = IVBSAXLexicalHandler_startDTD(lexical->vbhandler, &name, &pubid, &sysid); + if (saxreader_cmp(locator, L";")) + saxreader_string_append(locator, buffer, L";", 1); else - hr = ISAXLexicalHandler_startDTD(lexical->handler, name, SysStringLen(name), - pubid, SysStringLen(pubid), sysid, SysStringLen(sysid)); - - if (FAILED(hr)) - format_error_message_from_id(locator, hr); + saxreader_set_error(locator, E_SAX_BADCHARINENTREF); } -static void libxml_externalsubset(void *ctx, const xmlChar *name, const xmlChar *external_id, const xmlChar *system_id) +static void saxreader_entity_add_part(struct saxlocator *locator, struct entity_decl *decl, + struct string_buffer *buffer, bool reference) { - saxlocator *locator = ctx; - struct saxlexicalhandler_iface *lexical = saxreader_get_lexicalhandler(locator->saxreader); - HRESULT hr = S_OK; + struct entity_part *part; - update_position(locator, FALSE); - if (!saxreader_has_handler(locator, SAXLexicalHandler)) return; - - if (locator->vbInterface) - hr = IVBSAXLexicalHandler_endDTD(lexical->vbhandler); - else - hr = ISAXLexicalHandler_endDTD(lexical->handler); + if (!buffer->count) + return; - if (FAILED(hr)) - format_error_message_from_id(locator, hr); -} + if (!saxreader_array_reserve(locator, (void **)&decl->content.parts, &decl->content.capacity, + decl->content.count + 1, sizeof(*decl->content.parts))) + { + return; + } + part = &decl->content.parts[decl->content.count++]; -static xmlParserInputPtr libxmlresolveentity(void *ctx, const xmlChar *publicid, const xmlChar *systemid) -{ - FIXME("entity resolving not implemented, %s, %s\n", publicid, systemid); - return xmlSAX2ResolveEntity(ctx, publicid, systemid); + part->value = saxreader_string_to_bstr(locator, buffer); + part->reference = reference; } -/*** IVBSAXLocator interface ***/ -/*** IUnknown methods ***/ -static HRESULT WINAPI ivbsaxlocator_QueryInterface(IVBSAXLocator* iface, REFIID riid, void **ppvObject) +/* [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' + | "'" ([^%&'] | PEReference | Reference)* "'" */ +static void saxreader_parse_entityvalue(struct saxlocator *locator, struct entity_decl *decl) { - saxlocator *This = impl_from_IVBSAXLocator( iface ); + struct string_buffer buffer = { 0 }, part = { 0 }; + struct text_position position; + WCHAR quote, ch; + size_t count; - TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject); - - *ppvObject = NULL; - - if ( IsEqualGUID( riid, &IID_IUnknown ) || - IsEqualGUID( riid, &IID_IDispatch) || - IsEqualGUID( riid, &IID_IVBSAXLocator )) - { - *ppvObject = iface; - } - else if ( IsEqualGUID( riid, &IID_IVBSAXAttributes )) + if (!(quote = saxreader_is_quote(locator))) { - *ppvObject = &This->IVBSAXAttributes_iface; - } - else - { - FIXME("interface %s not implemented\n", debugstr_guid(riid)); - return E_NOINTERFACE; + saxreader_set_error(locator, E_SAX_MISSINGQUOTE); + return; } - IVBSAXLocator_AddRef( iface ); + saxreader_skip(locator, 1); - return S_OK; -} + /* Only character references are resolved. Entity references are parsed + lazily, checking for valid name characters but not for a valid name. + Referenced entities do not have to be already declared, and are not expanded + if they are declared. */ -static ULONG WINAPI ivbsaxlocator_AddRef(IVBSAXLocator* iface) -{ - saxlocator *This = impl_from_IVBSAXLocator( iface ); - TRACE("%p\n", This ); - return ISAXLocator_AddRef(&This->ISAXLocator_iface); -} + saxreader_more(locator); + ch = *saxreader_get_ptr_noread(locator); + while (ch != quote) + { + if (saxreader_peek(locator, L"", 2)) + { + count = buffer.count; + saxreader_parse_charref(locator, &buffer, &position); + saxreader_string_append(locator, &part, buffer.data + count, buffer.count - count); + } + else if (saxreader_peek(locator, L"&", 1)) + { + saxreader_entity_add_part(locator, decl, &part, false); -static ULONG WINAPI ivbsaxlocator_Release(IVBSAXLocator* iface) -{ - saxlocator *This = impl_from_IVBSAXLocator( iface ); - return ISAXLocator_Release(&This->ISAXLocator_iface); -} + count = buffer.count; + saxreader_parse_entityvalue_entityref(locator, &buffer); + /* Skip markup in &name; */ + if (buffer.count - count > 2) + saxreader_string_append(locator, &part, buffer.data + count + 1, buffer.count - count - 2); -/*** IDispatch methods ***/ -static HRESULT WINAPI ivbsaxlocator_GetTypeInfoCount( IVBSAXLocator *iface, UINT* pctinfo ) -{ - saxlocator *This = impl_from_IVBSAXLocator( iface ); + saxreader_entity_add_part(locator, decl, &part, true); + } + else + { + saxreader_string_append(locator, &buffer, &ch, 1); + saxreader_string_append(locator, &part, &ch, 1); + saxreader_skip(locator, 1); + } + ch = *saxreader_get_ptr_noread(locator); + } - TRACE("(%p)->(%p)\n", This, pctinfo); + if (ch == quote) + { + saxreader_entity_add_part(locator, decl, &part, false); + decl->value = saxreader_string_to_bstr(locator, &buffer); - *pctinfo = 1; + /* Skip closing quote */ + saxreader_skip(locator, 1); + return; + } - return S_OK; + free(part.data); + free(buffer.data); + saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); } -static HRESULT WINAPI ivbsaxlocator_GetTypeInfo( - IVBSAXLocator *iface, - UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo ) +static void saxreader_release_entity(struct entity_decl *entity) { - TRACE("%p, %u, %lx, %p.\n", iface, iTInfo, lcid, ppTInfo); - - return get_typeinfo(IVBSAXLocator_tid, ppTInfo); + SysFreeString(entity->name); + SysFreeString(entity->sysid); + SysFreeString(entity->value); + free(entity); } -static HRESULT WINAPI ivbsaxlocator_GetIDsOfNames( - IVBSAXLocator *iface, - REFIID riid, - LPOLESTR* rgszNames, - UINT cNames, - LCID lcid, - DISPID* rgDispId) +/* [70] EntityDecl ::= GEDecl | PEDecl + [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>' + [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>' + [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) + [74] PEDef ::= EntityValue | ExternalID + [76] NDataDecl ::= S 'NDATA' S Name */ +static void saxreader_parse_entitydecl(struct saxlocator *locator) { - ITypeInfo *typeinfo; - HRESULT hr; + BSTR notation = NULL, pubid = NULL; + struct string_buffer buffer = { 0 }; + struct entity_decl *decl; + bool pe, undeclared; - TRACE("%p, %s, %p, %u, %lx, %p.\n", iface, debugstr_guid(riid), rgszNames, cNames, - lcid, rgDispId); + /* Skip <!ENTITY */ + saxreader_skip(locator, 8); + saxreader_skip_required_spaces(locator); - if(!rgszNames || cNames == 0 || !rgDispId) - return E_INVALIDARG; + if ((pe = saxreader_cmp(locator, L"%"))) + saxreader_skip_required_spaces(locator); - hr = get_typeinfo(IVBSAXLocator_tid, &typeinfo); - if(SUCCEEDED(hr)) + if (!(decl = saxreader_calloc(locator, 1, sizeof(*decl)))) + return; + + saxreader_parse_name_strict(locator, &decl->name); + saxreader_skip_required_spaces(locator); + + if (pe) { - hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); - ITypeInfo_Release(typeinfo); - } + saxreader_string_append(locator, &buffer, L"%", 1); + saxreader_string_append_bstr(locator, &buffer, &decl->name); + decl->name = saxreader_string_to_bstr(locator, &buffer); + undeclared = saxreader_get_entity(locator, decl->name) == NULL; - return hr; -} + if (saxreader_is_quote(locator)) + { + saxreader_parse_entityvalue(locator, decl); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + { + if (undeclared) + saxlocator_internal_entitydecl(locator, decl->name, decl->value); + } + else + { + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + } + } + else + { + saxreader_parse_externalid(locator, &decl->sysid, &pubid, false); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + { + if (undeclared) + saxlocator_external_entitydecl(locator, decl->name, pubid, decl->sysid); + } + else + { + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + } + } + } + else + { + undeclared = saxreader_get_entity(locator, decl->name) == NULL; -static HRESULT WINAPI ivbsaxlocator_Invoke( - IVBSAXLocator *iface, - DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS* pDispParams, - VARIANT* pVarResult, - EXCEPINFO* pExcepInfo, - UINT* puArgErr) -{ - ITypeInfo *typeinfo; - HRESULT hr; + if (saxreader_is_quote(locator)) + { + saxreader_parse_entityvalue(locator, decl); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + { + if (undeclared) + saxlocator_internal_entitydecl(locator, decl->name, decl->value); + } + else + { + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + } + } + else + { + saxreader_parse_externalid(locator, &decl->sysid, &pubid, false); - TRACE("%p, %ld, %s, %lx, %d, %p, %p, %p, %p.\n", iface, dispIdMember, debugstr_guid(riid), - lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + saxreader_more(locator); + if (!saxreader_peek(locator, L">", 1)) + saxreader_skip_required_spaces(locator); - hr = get_typeinfo(IVBSAXLocator_tid, &typeinfo); - if(SUCCEEDED(hr)) - { - hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); - ITypeInfo_Release(typeinfo); + if (saxreader_cmp(locator, L"NDATA")) + { + decl->unparsed = true; + saxreader_skip_required_spaces(locator); + saxreader_parse_name(locator, ¬ation); + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + { + if (undeclared) + saxlocator_unparsed_entitydecl(locator, decl->name, pubid, decl->sysid, notation); + } + else + { + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + } + } + else + { + saxreader_skipspaces(locator); + if (saxreader_cmp(locator, L">")) + { + if (undeclared) + saxlocator_external_entitydecl(locator, decl->name, pubid, decl->sysid); + } + else + { + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + } + } + } } - return hr; -} - -/*** IVBSAXLocator methods ***/ -static HRESULT WINAPI ivbsaxlocator_get_columnNumber( - IVBSAXLocator* iface, - int *pnColumn) -{ - saxlocator *This = impl_from_IVBSAXLocator( iface ); - return ISAXLocator_getColumnNumber(&This->ISAXLocator_iface, pnColumn); -} + if (undeclared) + list_add_tail(&locator->dtd.entities, &decl->entry); + else + saxreader_release_entity(decl); -static HRESULT WINAPI ivbsaxlocator_get_lineNumber( - IVBSAXLocator* iface, - int *pnLine) -{ - saxlocator *This = impl_from_IVBSAXLocator( iface ); - return ISAXLocator_getLineNumber(&This->ISAXLocator_iface, pnLine); + SysFreeString(pubid); + SysFreeString(notation); } -static HRESULT WINAPI ivbsaxlocator_get_publicId(IVBSAXLocator* iface, BSTR *ret) +/* [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' */ +static BSTR saxreader_parse_notation_type(struct saxlocator *locator) { - saxlocator *This = impl_from_IVBSAXLocator( iface ); - const WCHAR *publicidW; - HRESULT hr; + struct string_buffer buffer = { 0 }; + BSTR name, value = NULL; - TRACE("(%p)->(%p)\n", This, ret); + /* Skip NOTATION */ + saxreader_skip(locator, 8); + saxreader_skip_required_spaces(locator); + if (!saxreader_cmp(locator, L"(")) + { + saxreader_set_error(locator, E_SAX_MISSING_PAREN); + return NULL; + } - if (!ret) - return E_POINTER; + saxreader_string_append(locator, &buffer, L"NOTATION (", 10); + while (locator->status == S_OK && !value) + { + saxreader_skipspaces(locator); + saxreader_parse_name_strict(locator, &name); + saxreader_string_append_bstr(locator, &buffer, &name); + saxreader_skipspaces(locator); - *ret = NULL; - hr = ISAXLocator_getPublicId(&This->ISAXLocator_iface, &publicidW); - if (FAILED(hr)) - return hr; + if (saxreader_cmp(locator, L")")) + { + saxreader_string_append(locator, &buffer, L")", 1); + value = saxreader_string_to_bstr(locator, &buffer); + } + else if (saxreader_cmp(locator, L"|")) + saxreader_string_append(locator, &buffer, L"|", 1); + else + saxreader_set_error(locator, E_SAX_BADCHARINENUMERATION); + } - return return_bstr(publicidW, ret); + return value; } -static HRESULT WINAPI ivbsaxlocator_get_systemId(IVBSAXLocator* iface, BSTR *ret) +/* [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' */ +static BSTR saxreader_parse_enumeration_type(struct saxlocator *locator) { - saxlocator *This = impl_from_IVBSAXLocator( iface ); - const WCHAR *systemidW; - HRESULT hr; + struct string_buffer buffer = { 0 }; + BSTR token, value = NULL; - TRACE("(%p)->(%p)\n", This, ret); + /* Skip '(' */ + saxreader_skip(locator, 1); + saxreader_string_append(locator, &buffer, L"(", 1); - if (!ret) - return E_POINTER; + while (locator->status == S_OK && !value) + { + saxreader_skipspaces(locator); + saxreader_parse_nmtoken(locator, &token); + saxreader_string_append_bstr(locator, &buffer, &token); + saxreader_skipspaces(locator); - *ret = NULL; - hr = ISAXLocator_getSystemId(&This->ISAXLocator_iface, &systemidW); - if (FAILED(hr)) - return hr; + if (saxreader_cmp(locator, L")")) + { + saxreader_string_append(locator, &buffer, L")", 1); + value = saxreader_string_to_bstr(locator, &buffer); + } + else if (saxreader_cmp(locator, L"|")) + saxreader_string_append(locator, &buffer, L"|", 1); + else + saxreader_set_error(locator, E_SAX_BADCHARINENUMERATION); + } - return return_bstr(systemidW, ret); + return value; +} + +/* [54] AttType ::= StringType | TokenizedType | EnumeratedType + [55] StringType ::= 'CDATA' + [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' + | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS' + [57] EnumeratedType ::= NotationType | Enumeration */ +static BSTR saxreader_parse_atttype(struct saxlocator *locator) +{ + /* TODO: this could reuse strings */ + + if (saxreader_cmp(locator, L"CDATA")) + return saxreader_alloc_string(locator, L"CDATA"); + if (saxreader_cmp(locator, L"IDREFS")) + return saxreader_alloc_string(locator, L"IDREFS"); + if (saxreader_cmp(locator, L"IDREF")) + return saxreader_alloc_string(locator, L"IDREF"); + if (saxreader_cmp(locator, L"ID")) + return saxreader_alloc_string(locator, L"ID"); + if (saxreader_cmp(locator, L"ENTITY")) + return saxreader_alloc_string(locator, L"ENTITY"); + if (saxreader_cmp(locator, L"ENTITIES")) + return saxreader_alloc_string(locator, L"ENTITIES"); + if (saxreader_cmp(locator, L"NMTOKENS")) + return saxreader_alloc_string(locator, L"NMTOKENS"); + if (saxreader_cmp(locator, L"NMTOKEN")) + return saxreader_alloc_string(locator, L"NMTOKEN"); + + if (saxreader_peek(locator, L"NOTATION", 8)) + return saxreader_parse_notation_type(locator); + else if (saxreader_peek(locator, L"(", 1)) + return saxreader_parse_enumeration_type(locator); + + saxreader_set_error(locator, E_SAX_INVALID_TYPE); + return NULL; } -static const struct IVBSAXLocatorVtbl VBSAXLocatorVtbl = -{ - ivbsaxlocator_QueryInterface, - ivbsaxlocator_AddRef, - ivbsaxlocator_Release, - ivbsaxlocator_GetTypeInfoCount, - ivbsaxlocator_GetTypeInfo, - ivbsaxlocator_GetIDsOfNames, - ivbsaxlocator_Invoke, - ivbsaxlocator_get_columnNumber, - ivbsaxlocator_get_lineNumber, - ivbsaxlocator_get_publicId, - ivbsaxlocator_get_systemId -}; - -/*** ISAXLocator interface ***/ -/*** IUnknown methods ***/ -static HRESULT WINAPI isaxlocator_QueryInterface(ISAXLocator* iface, REFIID riid, void **ppvObject) +/* [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) */ +static BSTR saxreader_parse_defaultdecl(struct saxlocator *locator, BSTR *value) { - saxlocator *This = impl_from_ISAXLocator( iface ); + BSTR ret = NULL; - TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject ); + *value = NULL; - *ppvObject = NULL; + if (saxreader_cmp(locator, L"#REQUIRED")) + return saxreader_alloc_string(locator, L"#REQUIRED"); + if (saxreader_cmp(locator, L"#IMPLIED")) + return saxreader_alloc_string(locator, L"#IMPLIED"); - if ( IsEqualGUID( riid, &IID_IUnknown ) || - IsEqualGUID( riid, &IID_ISAXLocator )) - { - *ppvObject = iface; - } - else if ( IsEqualGUID( riid, &IID_ISAXAttributes )) - { - *ppvObject = &This->ISAXAttributes_iface; - } - else + if (saxreader_cmp(locator, L"#FIXED")) { - WARN("interface %s not implemented\n", debugstr_guid(riid)); - return E_NOINTERFACE; + ret = saxreader_alloc_string(locator, L"#FIXED"); + saxreader_skip_required_spaces(locator); } + *value = saxreader_parse_attvalue(locator); - ISAXLocator_AddRef( iface ); - - return S_OK; + return ret; } -static ULONG WINAPI isaxlocator_AddRef(ISAXLocator* iface) +/* [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' + [53] AttDef ::= S Name S AttType S DefaultDecl */ +static void saxreader_parse_attlistdecl(struct saxlocator *locator) { - saxlocator *This = impl_from_ISAXLocator( iface ); - ULONG ref = InterlockedIncrement( &This->ref ); - TRACE("%p, refcount %lu.\n", iface, ref); - return ref; -} + struct attlist_decl *decl; -static ULONG WINAPI isaxlocator_Release( - ISAXLocator* iface) -{ - saxlocator *This = impl_from_ISAXLocator( iface ); - ULONG ref = InterlockedDecrement( &This->ref ); + if (!(decl = saxreader_calloc(locator, 1, sizeof(*decl)))) + return; + list_add_tail(&locator->dtd.attr_decls, &decl->entry); - TRACE("%p, refcount %ld.\n", iface, ref ); + /* Skip <!ATTLIST */ + saxreader_skip(locator, 9); + saxreader_skip_required_spaces(locator); + saxreader_parse_name(locator, &decl->name); + saxreader_skipspaces(locator); - if (!ref) + while (locator->status == S_OK) { - element_entry *element, *element2; - int index; + struct attlist_attr *attr; - SysFreeString(This->publicId); - SysFreeString(This->systemId); - SysFreeString(This->namespaceUri); + if (saxreader_cmp(locator, L">")) + break; - for(index = 0; index < This->attr_alloc_count; index++) + if (!saxreader_array_reserve(locator, (void **)&decl->attributes, &decl->capacity, + decl->count + 1, sizeof(*decl->attributes))) { - SysFreeString(This->attributes[index].szLocalname); - SysFreeString(This->attributes[index].szValue); - SysFreeString(This->attributes[index].szQName); + break; } - free(This->attributes); + attr = &decl->attributes[decl->count++]; - /* element stack */ - LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, element_entry, entry) - { - list_remove(&element->entry); - free_element_entry(element); - } + saxreader_parse_qname(locator, &attr->name); + saxreader_skip_required_spaces(locator); + attr->type = saxreader_parse_atttype(locator); + saxreader_skip_required_spaces(locator); + attr->valuetype = saxreader_parse_defaultdecl(locator, &attr->value); + + if (saxreader_cmp(locator, L">")) + break; - ISAXXMLReader_Release(&This->saxreader->ISAXXMLReader_iface); - free(This); + saxreader_skip_required_spaces(locator); } - return ref; + saxlocator_attribute_decl(locator, decl); } -/*** ISAXLocator methods ***/ -static HRESULT WINAPI isaxlocator_getColumnNumber( - ISAXLocator* iface, - int *pnColumn) +/* [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' */ +static void saxreader_parse_notationdecl(struct saxlocator *locator) { - saxlocator *This = impl_from_ISAXLocator( iface ); + BSTR name = NULL, sysid = NULL, pubid = NULL; - *pnColumn = This->column; - return S_OK; + /* Skip <!NOTATION */ + saxreader_skip(locator, 10); + saxreader_skip_required_spaces(locator); + saxreader_parse_name_strict(locator, &name); + saxreader_shrink(locator); + + saxreader_skip_required_spaces(locator); + saxreader_parse_externalid(locator, &sysid, &pubid, true); + saxreader_skipspaces(locator); + + if (!saxreader_cmp(locator, L">")) + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); + + saxlocator_notationdecl(locator, &locator->buffer.position, name, pubid, sysid); + + SysFreeString(name); + SysFreeString(sysid); + SysFreeString(pubid); } -static HRESULT WINAPI isaxlocator_getLineNumber( - ISAXLocator* iface, - int *pnLine) +/* [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment */ +static void saxreader_parse_markupdecl(struct saxlocator *locator) { - saxlocator *This = impl_from_ISAXLocator( iface ); - - *pnLine = This->line; - return S_OK; + saxreader_more(locator); + if (saxreader_peek(locator, L"<!--", 4)) + saxreader_parse_comment(locator); + else if (saxreader_peek(locator, L"<!", 2)) + { + if (saxreader_peek(locator, L"<!ELEMENT", 9)) + saxreader_parse_elementdecl(locator); + else if (saxreader_peek(locator, L"<!ENTITY", 8)) + saxreader_parse_entitydecl(locator); + else if (saxreader_peek(locator, L"<!ATTLIST", 9)) + saxreader_parse_attlistdecl(locator); + else if (saxreader_peek(locator, L"<!NOTATION", 10)) + saxreader_parse_notationdecl(locator); + else + saxreader_set_error(locator, E_SAX_BADDECLNAME); + } + else if (saxreader_peek(locator, L"<?", 2)) + saxreader_parse_pi(locator); + else + saxreader_set_error(locator, E_SAX_BADCHARINDTD); } -static HRESULT WINAPI isaxlocator_getPublicId( - ISAXLocator* iface, - const WCHAR ** ppwchPublicId) +/* [69] PEReference ::= '%' Name ';' */ +static void saxreader_parse_pe_reference(struct saxlocator *locator) { - BSTR publicId; - saxlocator *This = impl_from_ISAXLocator( iface ); + struct string_buffer buffer = { 0 }; + struct text_position position; + struct entity_decl *entity; + BSTR name; - SysFreeString(This->publicId); + /* Skip '%' */ + saxreader_skip(locator, 1); + position = locator->buffer.position; - publicId = bstr_from_xmlChar(xmlSAX2GetPublicId(This->pParserCtxt)); - if(SysStringLen(publicId)) - This->publicId = publicId; - else - { - SysFreeString(publicId); - This->publicId = NULL; - } + saxreader_parse_name(locator, &name); + if (!saxreader_cmp(locator, L";")) + saxreader_set_error(locator, E_SAX_MISSINGSEMICOLON); - *ppwchPublicId = This->publicId; - return S_OK; + saxreader_string_append(locator, &buffer, L"%", 1); + saxreader_string_append_bstr(locator, &buffer, &name); + name = saxreader_string_to_bstr(locator, &buffer); + + if ((entity = saxreader_get_entity(locator, name))) + saxreader_push_entity(locator, &locator->buffer.position, entity); + else + saxlocator_skipped_entity(locator, &position, name); } -static HRESULT WINAPI isaxlocator_getSystemId( - ISAXLocator* iface, - const WCHAR ** ppwchSystemId) +/* [28a] DeclSep ::= PEReference | S + [28b] intSubset ::= (markupdecl | DeclSep)* + [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment */ +static void saxreader_parse_intsubset(struct saxlocator *locator) { - BSTR systemId; - saxlocator *This = impl_from_ISAXLocator( iface ); + WCHAR *ptr; - SysFreeString(This->systemId); + /* Opening '[' bracket already skipped */ + saxreader_skipspaces(locator); - systemId = bstr_from_xmlChar(xmlSAX2GetSystemId(This->pParserCtxt)); - if(SysStringLen(systemId)) - This->systemId = systemId; - else + while (locator->status == S_OK) { - SysFreeString(systemId); - This->systemId = NULL; - } + if (saxreader_cmp(locator, L"]")) + return; - *ppwchSystemId = This->systemId; - return S_OK; + // TODO: conditional blocks should be supported too + + if (saxreader_peek(locator, L"<!", 2) || saxreader_peek(locator, L"<?", 2)) + saxreader_parse_markupdecl(locator); + else if (saxreader_peek(locator, L"%", 1)) + saxreader_parse_pe_reference(locator); + else + saxreader_set_error(locator, E_SAX_BADCHARINDTD); + + saxreader_skipspaces(locator); + saxreader_shrink(locator); + ptr = saxreader_get_ptr(locator); + if (!*ptr) + saxreader_set_error(locator, E_SAX_UNEXPECTED_EOF); + } } -static const struct ISAXLocatorVtbl SAXLocatorVtbl = +/* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */ +static void saxreader_parse_doctype(struct saxlocator *locator) { - isaxlocator_QueryInterface, - isaxlocator_AddRef, - isaxlocator_Release, - isaxlocator_getColumnNumber, - isaxlocator_getLineNumber, - isaxlocator_getPublicId, - isaxlocator_getSystemId -}; + BSTR name, sysid, pubid; -static HRESULT SAXLocator_create(saxreader *reader, saxlocator **ppsaxlocator, BOOL vbInterface) -{ - saxlocator *locator; + if (locator->saxreader->features & ProhibitDTD) + return saxreader_set_error(locator, E_SAX_INVALIDATROOTLEVEL); - locator = malloc(sizeof(*locator)); - if( !locator ) - return E_OUTOFMEMORY; + /* Skip <!DOCTYPE */ + saxreader_skip(locator, 9); + saxreader_skip_required_spaces(locator); + saxreader_parse_name(locator, &name); - locator->IVBSAXLocator_iface.lpVtbl = &VBSAXLocatorVtbl; - locator->ISAXLocator_iface.lpVtbl = &SAXLocatorVtbl; - locator->IVBSAXAttributes_iface.lpVtbl = &ivbsaxattributes_vtbl; - locator->ISAXAttributes_iface.lpVtbl = &isaxattributes_vtbl; - locator->ref = 1; - locator->vbInterface = vbInterface; + saxreader_skipspaces(locator); + saxreader_parse_externalid(locator, &sysid, &pubid, false); - locator->saxreader = reader; - ISAXXMLReader_AddRef(&reader->ISAXXMLReader_iface); + saxreader_skipspaces(locator); + saxreader_shrink(locator); - locator->pParserCtxt = NULL; - locator->publicId = NULL; - locator->systemId = NULL; - locator->line = reader->version < MSXML4 ? 0 : 1; - locator->column = 0; - locator->ret = S_OK; - if (locator->saxreader->version >= MSXML6) - locator->namespaceUri = SysAllocString(L"http://www.w3.org/2000/xmlns/"); - else - locator->namespaceUri = SysAllocStringLen(NULL, 0); - if(!locator->namespaceUri) + if (saxreader_cmp(locator, L"[")) { - ISAXXMLReader_Release(&reader->ISAXXMLReader_iface); - free(locator); - return E_OUTOFMEMORY; + saxlocator_startdtd(locator, name, pubid, sysid); + saxreader_parse_intsubset(locator); + saxreader_skipspaces(locator); + if (!saxreader_cmp(locator, L">")) + saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); } - - locator->attr_alloc_count = 8; - locator->attr_count = 0; - locator->attributes = calloc(locator->attr_alloc_count, sizeof(*locator->attributes)); - if(!locator->attributes) + else if (saxreader_cmp(locator, L">")) { - ISAXXMLReader_Release(&reader->ISAXXMLReader_iface); - SysFreeString(locator->namespaceUri); - free(locator); - return E_OUTOFMEMORY; + saxlocator_startdtd(locator, name, pubid, sysid); } - list_init(&locator->elements); - - *ppsaxlocator = locator; + if (sysid) + FIXME("External subset is not supported.\n"); - TRACE("returning %p\n", *ppsaxlocator); + saxlocator_enddtd(locator); - return S_OK; + SysFreeString(sysid); + SysFreeString(pubid); } -/*** SAXXMLReader internal functions ***/ -static HRESULT internal_parseBuffer(saxreader *This, const char *buffer, int size, BOOL vbInterface) +static enum xmlencoding saxreader_match_encoding(const char *data, size_t size, size_t *bom) { - xmlCharEncoding encoding = XML_CHAR_ENCODING_NONE; - xmlChar *enc_name = NULL; - saxlocator *locator; - HRESULT hr; + const BYTE *b = (const BYTE *)data; - TRACE("(%p)->(%p %d)\n", This, buffer, size); + *bom = 0; - hr = SAXLocator_create(This, &locator, vbInterface); - if (FAILED(hr)) - return hr; + if (size < 4) + return XML_ENCODING_UNKNOWN; - if (size >= 4) + if (b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == '<') + return XML_ENCODING_UCS4BE; + if (b[0] == '<' && b[1] == 0 && b[2] == 0 && b[3] == 0) + return XML_ENCODING_UCS4LE; + if (b[0] == '<' && b[1] == 0 && b[2] == '?' && b[3] == 0) + return XML_ENCODING_UTF16LE; + if (b[0] == '<' && b[1] == 0 && b[2] && b[2] != '?') + return XML_ENCODING_UTF16LE; + if (b[0] == 0 && b[1] == '<' && b[2] == 0 && b[3] == '?') + return XML_ENCODING_UTF16BE; + if (b[0] == '<' && b[1] == '?' && b[2] == 'x' && b[3] == 'm') + return XML_ENCODING_UTF8; + if (b[0] == '<' && b[1] && b[1] != '?') + return XML_ENCODING_UTF8; + + if (b[0] == 0xef && b[1] == 0xbb && b[2] == 0xbf) { - encoding = xmlDetectCharEncoding((xmlChar*)buffer, 4); - enc_name = (xmlChar*)xmlGetCharEncodingName(encoding); - TRACE("detected encoding: %s\n", enc_name); + *bom = 3; + return XML_ENCODING_UTF8; } - /* if libxml2 detection failed try to guess */ - if (encoding == XML_CHAR_ENCODING_NONE) + if (b[0] == 0xfe && b[1] == 0xff) { - const WCHAR *ptr = (WCHAR*)buffer; - /* an xml declaration with optional encoding will still be handled by the parser */ - if ((size >= 2) && *ptr == '<' && ptr[1] != '?') - { - enc_name = (xmlChar*)xmlGetCharEncodingName(XML_CHAR_ENCODING_UTF16LE); - encoding = XML_CHAR_ENCODING_UTF16LE; - } + *bom = 2; + return XML_ENCODING_UTF16BE; } - else if (encoding == XML_CHAR_ENCODING_UTF8) - enc_name = (xmlChar*)xmlGetCharEncodingName(encoding); - else - enc_name = NULL; - locator->pParserCtxt = xmlCreateMemoryParserCtxt(buffer, size); - if (!locator->pParserCtxt) + if (b[0] == 0xff && b[1] == 0xfe) { - ISAXLocator_Release(&locator->ISAXLocator_iface); - return E_FAIL; + *bom = 2; + return XML_ENCODING_UTF16LE; } - if (enc_name) + return XML_ENCODING_UNKNOWN; +} + +static void saxreader_detect_encoding(struct saxlocator *locator) +{ + struct encoded_buffer *utf16 = &locator->buffer.utf16; + struct encoded_buffer *raw = &locator->buffer.raw; + enum xmlencoding encoding; + size_t bom = 0, size; + ULONG read = 0; + HRESULT hr; + + /* In principle there is no need to read as much to detect encoding, + but experiments show that application never gets smaller input buffer. */ + + if (!saxreader_reserve_buffer(locator, raw, locator->buffer.chunk_size)) + return; + + if (FAILED(hr = ISequentialStream_Read(locator->stream, raw->data, locator->buffer.chunk_size, &read))) + return saxreader_set_error(locator, hr); + if (!read) + return saxreader_set_error(locator, E_SAX_MISSINGROOT); + + raw->written = read; + + encoding = saxreader_match_encoding(raw->data, read, &bom); + + if (encoding == XML_ENCODING_UNKNOWN) { - locator->pParserCtxt->encoding = xmlStrdup(enc_name); - if (encoding == XML_CHAR_ENCODING_UTF16LE) { - TRACE("switching to %s\n", enc_name); - xmlSwitchEncoding(locator->pParserCtxt, encoding); - } + WARN("Failed to detect document encoding.\n"); + return saxreader_set_error(locator, E_SAX_INVALIDENCODING); } - xmlFree(locator->pParserCtxt->sax); - locator->pParserCtxt->sax = &locator->saxreader->sax; - locator->pParserCtxt->userData = locator; + locator->buffer.encoding = encoding; + locator->buffer.converter = convert_get_converter(encoding); + locator->buffer.code_page = convert_get_codepage(encoding); - This->isParsing = TRUE; - if(xmlParseDocument(locator->pParserCtxt) == -1 && locator->ret == S_OK) - hr = E_FAIL; - else - hr = locator->ret; - This->isParsing = FALSE; + TRACE("detected encoding %s\n", saxreader_get_encoding_name(encoding)); + + size = read - bom; - if(locator->pParserCtxt) + if (encoding == XML_ENCODING_UTF16LE) { - locator->pParserCtxt->sax = NULL; - xmlFreeParserCtxt(locator->pParserCtxt); - locator->pParserCtxt = NULL; - } + if (!saxreader_reserve_buffer(locator, utf16, size)) + return; - ISAXLocator_Release(&locator->ISAXLocator_iface); - return hr; + memcpy(utf16->data + utf16->written, raw->data + bom, size); + utf16->written += size; + + encoded_buffer_cleanup(raw); + } + else if (bom) + { + memmove(raw->data, raw->data + bom, size); + raw->written -= bom; + } } -static HRESULT internal_parseStream(saxreader *This, ISequentialStream *stream, BOOL vbInterface) +static HRESULT saxreader_parse_stream(struct saxreader *reader, ISequentialStream *stream, bool vbInterface) { - saxlocator *locator; + struct saxlocator *locator; HRESULT hr; - ULONG dataRead; - char data[2048]; - int ret; - dataRead = 0; - hr = ISequentialStream_Read(stream, data, sizeof(data), &dataRead); - if(FAILED(hr)) return hr; - - hr = SAXLocator_create(This, &locator, vbInterface); - if(FAILED(hr)) return hr; + if (FAILED(hr = saxlocator_create(reader, stream, vbInterface, &locator))) + return hr; - locator->pParserCtxt = xmlCreatePushParserCtxt( - &locator->saxreader->sax, locator, - data, dataRead, NULL); - if(!locator->pParserCtxt) + while (locator->state != SAX_PARSER_EOF) { - ISAXLocator_Release(&locator->ISAXLocator_iface); - return E_FAIL; - } + if (locator->status != S_OK) + { + saxreader_fatal_error(locator); + break; + } - This->isParsing = TRUE; + switch (locator->state) + { + case SAX_PARSER_START: + saxreader_detect_encoding(locator); + locator->state = SAX_PARSER_XML_DECL; + break; + case SAX_PARSER_XML_DECL: + saxreader_parse_xmldecl(locator); + locator->buffer.switched_encoding = true; + saxlocator_put_document_locator(locator); + saxlocator_start_document(locator); + locator->state = SAX_PARSER_MISC; + break; + case SAX_PARSER_MISC: + case SAX_PARSER_EPILOG: + saxreader_skipspaces(locator); + saxreader_more(locator); - do { - dataRead = 0; - hr = ISequentialStream_Read(stream, data, sizeof(data), &dataRead); - if (FAILED(hr) || !dataRead) break; + if (saxreader_peek(locator, L"<", 1)) + { + if (saxreader_peek(locator, L"<?", 2)) + { + saxreader_parse_pi(locator); + break; + } + else if (saxreader_peek(locator, L"<!--", 4)) + { + saxreader_parse_comment(locator); + break; + } + else if (locator->state == SAX_PARSER_MISC) + { + if (saxreader_peek(locator, L"<!DOCTYPE", 9)) + { + saxreader_parse_doctype(locator); + break; + } + } + } - ret = xmlParseChunk(locator->pParserCtxt, data, dataRead, 0); - hr = ret!=XML_ERR_OK && locator->ret==S_OK ? E_FAIL : locator->ret; - }while(hr == S_OK); + if (locator->state == SAX_PARSER_EPILOG) + { + locator->state = SAX_PARSER_EOF; + saxlocator_end_document(locator); + } + else + { + locator->state = SAX_PARSER_START_TAG; + } + break; + case SAX_PARSER_START_TAG: + saxreader_parse_starttag(locator); + if (list_empty(&locator->elements)) + locator->state = SAX_PARSER_EPILOG; + else + locator->state = SAX_PARSER_CONTENT; + break; + case SAX_PARSER_END_TAG: + saxreader_parse_endtag(locator); + if (list_empty(&locator->elements)) + locator->state = SAX_PARSER_EPILOG; + else + locator->state = SAX_PARSER_CONTENT; + break; + case SAX_PARSER_CONTENT: + saxreader_more(locator); - if(SUCCEEDED(hr)) - { - ret = xmlParseChunk(locator->pParserCtxt, data, 0, 1); - hr = ret!=XML_ERR_OK && locator->ret==S_OK ? E_FAIL : locator->ret; + if (saxreader_peek(locator, L"<", 1)) + { + if (saxreader_peek(locator, L"</", 2)) + { + locator->state = SAX_PARSER_END_TAG; + } + else if (saxreader_peek(locator, L"<?", 2)) + { + saxreader_parse_pi(locator); + locator->state = SAX_PARSER_CONTENT; + } + else if (saxreader_peek(locator, L"<!--", 4)) + { + saxreader_parse_comment(locator); + locator->state = SAX_PARSER_CONTENT; + } + else if (saxreader_peek(locator, L"<![CDATA[", 9)) + { + locator->state = SAX_PARSER_CDATA; + } + else + { + locator->state = SAX_PARSER_START_TAG; + } + } + else if (saxreader_peek(locator, L"&", 1)) + { + saxreader_parse_reference(locator); + } + else + { + saxreader_parse_chardata(locator); + } + break; + case SAX_PARSER_CDATA: + saxreader_parse_cdata(locator); + locator->state = SAX_PARSER_CONTENT; + break; + default: + locator->state = SAX_PARSER_EOF; + } } - - This->isParsing = FALSE; - - xmlFreeParserCtxt(locator->pParserCtxt); - locator->pParserCtxt = NULL; + hr = locator->status; ISAXLocator_Release(&locator->ISAXLocator_iface); + return hr; } -static HRESULT internal_parse( - saxreader* This, - VARIANT varInput, - BOOL vbInterface) +static HRESULT saxreader_parse(struct saxreader *reader, VARIANT input, bool vbInterface) { + ISequentialStream *stream; HRESULT hr; - TRACE("(%p)->(%s)\n", This, debugstr_variant(&varInput)); + TRACE("%p, %s.\n", reader, debugstr_variant(&input)); - /* Dispose of the BSTRs in the pool from a prior run, if any. */ - free_bstr_pool(&This->pool); - - switch(V_VT(&varInput)) + switch (V_VT(&input)) { case VT_BSTR: case VT_BSTR|VT_BYREF: { - BSTR str = V_ISBYREF(&varInput) ? *V_BSTRREF(&varInput) : V_BSTR(&varInput); - hr = internal_parseBuffer(This, (const char*)str, lstrlenW(str)*sizeof(WCHAR), vbInterface); + BSTR str = V_ISBYREF(&input) ? *V_BSTRREF(&input) : V_BSTR(&input); + + if (FAILED(hr = stream_wrapper_create((const void *)str, SysStringByteLen(str), &stream))) + return hr; + hr = saxreader_parse_stream(reader, stream, vbInterface); + ISequentialStream_Release(stream); break; } - case VT_ARRAY|VT_UI1: { - void *pSAData; + case VT_ARRAY|VT_UI1: + { LONG lBound, uBound; - ULONG dataRead; - - hr = SafeArrayGetLBound(V_ARRAY(&varInput), 1, &lBound); - if(hr != S_OK) break; - hr = SafeArrayGetUBound(V_ARRAY(&varInput), 1, &uBound); - if(hr != S_OK) break; - dataRead = (uBound-lBound)*SafeArrayGetElemsize(V_ARRAY(&varInput)); - hr = SafeArrayAccessData(V_ARRAY(&varInput), &pSAData); - if(hr != S_OK) break; - hr = internal_parseBuffer(This, pSAData, dataRead, vbInterface); - SafeArrayUnaccessData(V_ARRAY(&varInput)); + ULONG size; + void *data; + + if (FAILED(hr = SafeArrayGetLBound(V_ARRAY(&input), 1, &lBound))) return hr; + if (FAILED(hr = SafeArrayGetUBound(V_ARRAY(&input), 1, &uBound))) return hr; + if (FAILED(hr = SafeArrayAccessData(V_ARRAY(&input), &data))) return hr; + + size = (uBound - lBound + 1) * SafeArrayGetElemsize(V_ARRAY(&input)); + if (SUCCEEDED(hr = stream_wrapper_create(data, size, &stream))) + { + hr = saxreader_parse_stream(reader, stream, vbInterface); + ISequentialStream_Release(stream); + } + SafeArrayUnaccessData(V_ARRAY(&input)); break; } case VT_UNKNOWN: - case VT_DISPATCH: { + case VT_DISPATCH: + { ISequentialStream *stream = NULL; IXMLDOMDocument *xmlDoc; - if (!V_UNKNOWN(&varInput)) + if (!V_UNKNOWN(&input)) return E_INVALIDARG; - if(IUnknown_QueryInterface(V_UNKNOWN(&varInput), - &IID_IXMLDOMDocument, (void**)&xmlDoc) == S_OK) + if (IUnknown_QueryInterface(V_UNKNOWN(&input), &IID_IXMLDOMDocument, (void**)&xmlDoc) == S_OK) { BSTR bstrData; IXMLDOMDocument_get_xml(xmlDoc, &bstrData); - hr = internal_parseBuffer(This, (const char*)bstrData, - SysStringByteLen(bstrData), vbInterface); + stream_wrapper_create(bstrData, SysStringByteLen(bstrData), &stream); + hr = saxreader_parse_stream(reader, stream, vbInterface); + ISequentialStream_Release(stream); IXMLDOMDocument_Release(xmlDoc); SysFreeString(bstrData); break; } /* try base interface first */ - IUnknown_QueryInterface(V_UNKNOWN(&varInput), &IID_ISequentialStream, (void**)&stream); + IUnknown_QueryInterface(V_UNKNOWN(&input), &IID_ISequentialStream, (void **)&stream); if (!stream) /* this should never happen if IStream is implemented properly, but just in case */ - IUnknown_QueryInterface(V_UNKNOWN(&varInput), &IID_IStream, (void**)&stream); + IUnknown_QueryInterface(V_UNKNOWN(&input), &IID_IStream, (void **)&stream); - if(stream) + if (stream) { - hr = internal_parseStream(This, stream, vbInterface); + hr = saxreader_parse_stream(reader, stream, vbInterface); ISequentialStream_Release(stream); } else @@ -2682,7 +5456,7 @@ static HRESULT internal_parse( break; } default: - WARN("vt %d not implemented\n", V_VT(&varInput)); + WARN("input type %d is not supported\n", V_VT(&input)); hr = E_INVALIDARG; } @@ -2691,19 +5465,33 @@ static HRESULT internal_parse( static HRESULT internal_vbonDataAvailable(void *obj, char *ptr, DWORD len) { - saxreader *This = obj; + struct saxreader *reader = obj; + ISequentialStream *stream; + HRESULT hr; - return internal_parseBuffer(This, ptr, len, TRUE); + if (SUCCEEDED(hr = stream_wrapper_create(ptr, len, &stream))) + { + hr = saxreader_parse_stream(reader, stream, true); + ISequentialStream_Release(stream); + } + return hr; } static HRESULT internal_onDataAvailable(void *obj, char *ptr, DWORD len) { - saxreader *This = obj; + struct saxreader *reader = obj; + ISequentialStream *stream; + HRESULT hr; - return internal_parseBuffer(This, ptr, len, FALSE); + if (SUCCEEDED(hr = stream_wrapper_create(ptr, len, &stream))) + { + hr = saxreader_parse_stream(reader, stream, false); + ISequentialStream_Release(stream); + } + return hr; } -static HRESULT internal_parseURL(saxreader *reader, const WCHAR *url, BOOL vbInterface) +static HRESULT saxreader_parse_url(struct saxreader *reader, const WCHAR *url, bool vbInterface) { IMoniker *mon; bsc_t *bsc; @@ -2715,25 +5503,26 @@ static HRESULT internal_parseURL(saxreader *reader, const WCHAR *url, BOOL vbInt return E_INVALIDARG; hr = create_moniker_from_url(url, &mon); - if(FAILED(hr)) + if (FAILED(hr)) return hr; - if(vbInterface) hr = bind_url(mon, internal_vbonDataAvailable, reader, &bsc); + if (vbInterface) hr = bind_url(mon, internal_vbonDataAvailable, reader, &bsc); else hr = bind_url(mon, internal_onDataAvailable, reader, &bsc); IMoniker_Release(mon); - if(FAILED(hr)) + if (FAILED(hr)) return hr; return detach_bsc(bsc); } -static HRESULT saxreader_put_handler_from_variant(saxreader *This, enum saxhandler_type type, const VARIANT *v, BOOL vb) +static HRESULT saxreader_put_handler_from_variant(struct saxreader *reader, enum saxhandler_type type, + const VARIANT *v, bool vb) { const IID *riid; if (V_VT(v) == VT_EMPTY) - return saxreader_put_handler(This, type, NULL, vb); + return saxreader_put_handler(reader, type, NULL, vb); switch (type) { @@ -2761,7 +5550,7 @@ static HRESULT saxreader_put_handler_from_variant(saxreader *This, enum saxhandl if (FAILED(hr)) return hr; } - saxreader_put_handler(This, type, handler, vb); + saxreader_put_handler(reader, type, handler, vb); if (handler) IUnknown_Release(handler); break; } @@ -2773,120 +5562,112 @@ static HRESULT saxreader_put_handler_from_variant(saxreader *This, enum saxhandl return S_OK; } -static HRESULT internal_putProperty( - saxreader* This, - const WCHAR *prop, - VARIANT value, - BOOL vbInterface) +static HRESULT saxreader_put_property(struct saxreader *reader, const WCHAR *prop, VARIANT value, bool vbInterface) { VARIANT *v; - TRACE("(%p)->(%s %s)\n", This, debugstr_w(prop), debugstr_variant(&value)); + TRACE("%p, %s, %s.\n", reader, debugstr_w(prop), debugstr_variant(&value)); - if (This->isParsing) return E_FAIL; + if (reader->isParsing) return E_FAIL; v = V_VT(&value) == (VT_VARIANT|VT_BYREF) ? V_VARIANTREF(&value) : &value; - if(!memcmp(prop, PropertyDeclHandlerW, sizeof(PropertyDeclHandlerW))) - return saxreader_put_handler_from_variant(This, SAXDeclHandler, v, vbInterface); - if(!memcmp(prop, PropertyLexicalHandlerW, sizeof(PropertyLexicalHandlerW))) - return saxreader_put_handler_from_variant(This, SAXLexicalHandler, v, vbInterface); + if (!wcscmp(prop, L"http://xml.org/sax/properties/declaration-handler")) + return saxreader_put_handler_from_variant(reader, SAXDeclHandler, v, vbInterface); - if(!memcmp(prop, L"max-xml-size", sizeof(L"max-xml-size"))) + if (!wcscmp(prop, L"http://xml.org/sax/properties/lexical-handler")) + return saxreader_put_handler_from_variant(reader, SAXLexicalHandler, v, vbInterface); + + if (!wcscmp(prop, L"max-xml-size")) { if (V_VT(v) == VT_I4 && V_I4(v) == 0) return S_OK; - FIXME("(%p)->(%s): max-xml-size unsupported\n", This, debugstr_variant(v)); + FIXME("(%p)->(%s): max-xml-size unsupported\n", reader, debugstr_variant(v)); return E_NOTIMPL; } - if(!memcmp(prop, L"max-element-depth", sizeof(L"max-element-depth"))) + if (!wcscmp(prop, L"max-element-depth")) { if (V_VT(v) == VT_I4 && V_I4(v) == 0) return S_OK; - FIXME("(%p)->(%s): max-element-depth unsupported\n", This, debugstr_variant(v)); + FIXME("(%p)->(%s): max-element-depth unsupported\n", reader, debugstr_variant(v)); return E_NOTIMPL; } - FIXME("(%p)->(%s:%s): unsupported property\n", This, debugstr_w(prop), debugstr_variant(v)); + FIXME("(%p)->(%s:%s): unsupported property\n", reader, debugstr_w(prop), debugstr_variant(v)); - if(!memcmp(prop, L"charset", sizeof(L"charset"))) + if (!wcscmp(prop, L"charset")) return E_NOTIMPL; - if(!memcmp(prop, PropertyDomNodeW, sizeof(PropertyDomNodeW))) + if (!wcscmp(prop, L"http://xml.org/sax/properties/dom-node")) return E_FAIL; - if(!memcmp(prop, L"input-source", sizeof(L"input-source"))) + if (!wcscmp(prop, L"input-source")) return E_NOTIMPL; - if(!memcmp(prop, L"schema-declaration-handler", sizeof(L"schema-declaration-handler"))) + if (!wcscmp(prop, L"schema-declaration-handler")) return E_NOTIMPL; - if(!memcmp(prop, L"xmldecl-encoding", sizeof(L"xmldecl-encoding"))) + if (!wcscmp(prop, L"xmldecl-encoding")) return E_FAIL; - if(!memcmp(prop, L"xmldecl-standalone", sizeof(L"xmldecl-standalone"))) + if (!wcscmp(prop, L"xmldecl-standalone")) return E_FAIL; - if(!memcmp(prop, L"xmldecl-version", sizeof(L"xmldecl-version"))) + if (!wcscmp(L"xmldecl-version", prop)) return E_FAIL; return E_INVALIDARG; } -static HRESULT internal_getProperty(const saxreader* This, const WCHAR *prop, VARIANT *value, BOOL vb) +static HRESULT saxreader_get_property(const struct saxreader *reader, const WCHAR *prop, VARIANT *value, bool vb) { - TRACE("(%p)->(%s)\n", This, debugstr_w(prop)); - if (!value) return E_POINTER; - if (!memcmp(PropertyLexicalHandlerW, prop, sizeof(PropertyLexicalHandlerW))) + if (!wcscmp(prop, L"http://xml.org/sax/properties/lexical-handler")) { - V_VT(value) = VT_UNKNOWN; - saxreader_get_handler(This, SAXLexicalHandler, vb, (void**)&V_UNKNOWN(value)); + V_VT(value) = vb ? VT_DISPATCH : VT_UNKNOWN; + saxreader_get_handler(reader, SAXLexicalHandler, vb, (void**)&V_UNKNOWN(value)); return S_OK; } - if (!memcmp(PropertyDeclHandlerW, prop, sizeof(PropertyDeclHandlerW))) + if (!wcscmp(prop, L"http://xml.org/sax/properties/declaration-handler")) { - V_VT(value) = VT_UNKNOWN; - saxreader_get_handler(This, SAXDeclHandler, vb, (void**)&V_UNKNOWN(value)); + V_VT(value) = vb ? VT_DISPATCH : VT_UNKNOWN; + saxreader_get_handler(reader, SAXDeclHandler, vb, (void**)&V_UNKNOWN(value)); return S_OK; } - if (!memcmp(L"xmldecl-version", prop, sizeof(L"xmldecl-version"))) + if (!wcscmp(prop, L"xmldecl-version")) { V_VT(value) = VT_BSTR; - V_BSTR(value) = SysAllocString(This->xmldecl_version); - return S_OK; + return return_bstr(reader->xmldecl_version, &V_BSTR(value)); } - FIXME("(%p)->(%s) unsupported property\n", This, debugstr_w(prop)); + FIXME("(%p)->(%s) unsupported property\n", reader, debugstr_w(prop)); return E_NOTIMPL; } -/*** IVBSAXXMLReader interface ***/ -/*** IUnknown methods ***/ -static HRESULT WINAPI saxxmlreader_QueryInterface(IVBSAXXMLReader* iface, REFIID riid, void **ppvObject) +static HRESULT WINAPI saxxmlreader_QueryInterface(IVBSAXXMLReader *iface, REFIID riid, void **obj) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); - TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject ); + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - *ppvObject = NULL; + *obj = NULL; - if ( IsEqualGUID( riid, &IID_IUnknown ) || - IsEqualGUID( riid, &IID_IDispatch ) || - IsEqualGUID( riid, &IID_IVBSAXXMLReader )) + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IDispatch) || + IsEqualGUID(riid, &IID_IVBSAXXMLReader)) { - *ppvObject = iface; + *obj = iface; } - else if( IsEqualGUID( riid, &IID_ISAXXMLReader )) + else if (IsEqualGUID(riid, &IID_ISAXXMLReader)) { - *ppvObject = &This->ISAXXMLReader_iface; + *obj = &reader->ISAXXMLReader_iface; } - else if (dispex_query_interface(&This->dispex, riid, ppvObject)) + else if (dispex_query_interface(&reader->dispex, riid, obj)) { - return *ppvObject ? S_OK : E_NOINTERFACE; + return *obj ? S_OK : E_NOINTERFACE; } else { @@ -2894,34 +5675,31 @@ static HRESULT WINAPI saxxmlreader_QueryInterface(IVBSAXXMLReader* iface, REFIID return E_NOINTERFACE; } - IVBSAXXMLReader_AddRef( iface ); + IVBSAXXMLReader_AddRef(iface); return S_OK; } -static ULONG WINAPI saxxmlreader_AddRef(IVBSAXXMLReader* iface) +static ULONG WINAPI saxxmlreader_AddRef(IVBSAXXMLReader *iface) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - LONG refcount = InterlockedIncrement(&reader->ref); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + LONG refcount = InterlockedIncrement(&reader->refcount); - TRACE("%p refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); return refcount; } -static ULONG WINAPI saxxmlreader_Release( - IVBSAXXMLReader* iface) +static ULONG WINAPI saxxmlreader_Release(IVBSAXXMLReader *iface) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - LONG refcount = InterlockedDecrement(&reader->ref); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + LONG refcount = InterlockedDecrement(&reader->refcount); - TRACE("%p refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); if (!refcount) { - int i; - - for (i = 0; i < SAXHandler_Last; i++) + for (int i = 0; i < ARRAYSIZE(reader->saxhandlers); ++i) { struct saxanyhandler_iface *saxiface = &reader->saxhandlers[i].u.anyhandler; @@ -2934,7 +5712,6 @@ static ULONG WINAPI saxxmlreader_Release( SysFreeString(reader->xmldecl_version); SysFreeString(reader->empty_bstr); - free_bstr_pool(&reader->pool); free(reader); } @@ -2942,209 +5719,150 @@ static ULONG WINAPI saxxmlreader_Release( return refcount; } -/*** IDispatch ***/ -static HRESULT WINAPI saxxmlreader_GetTypeInfoCount( IVBSAXXMLReader *iface, UINT* pctinfo ) +static HRESULT WINAPI saxxmlreader_GetTypeInfoCount(IVBSAXXMLReader *iface, UINT *count) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return IDispatchEx_GetTypeInfoCount(&reader->dispex.IDispatchEx_iface, count); } -static HRESULT WINAPI saxxmlreader_GetTypeInfo( - IVBSAXXMLReader *iface, - UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo ) +static HRESULT WINAPI saxxmlreader_GetTypeInfo(IVBSAXXMLReader *iface, UINT index, LCID lcid, ITypeInfo **ti) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, - iTInfo, lcid, ppTInfo); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return IDispatchEx_GetTypeInfo(&reader->dispex.IDispatchEx_iface, index, lcid, ti); } -static HRESULT WINAPI saxxmlreader_GetIDsOfNames( - IVBSAXXMLReader *iface, - REFIID riid, - LPOLESTR* rgszNames, - UINT cNames, - LCID lcid, - DISPID* rgDispId) +static HRESULT WINAPI saxxmlreader_GetIDsOfNames(IVBSAXXMLReader *iface, REFIID riid, LPOLESTR *names, + UINT count, LCID lcid, DISPID *dispid) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, - riid, rgszNames, cNames, lcid, rgDispId); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return IDispatchEx_GetIDsOfNames(&reader->dispex.IDispatchEx_iface, riid, names, count, lcid, dispid); } -static HRESULT WINAPI saxxmlreader_Invoke( - IVBSAXXMLReader *iface, - DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS* pDispParams, - VARIANT* pVarResult, - EXCEPINFO* pExcepInfo, - UINT* puArgErr) +static HRESULT WINAPI saxxmlreader_Invoke(IVBSAXXMLReader *iface, DISPID dispid, REFIID riid, + LCID lcid, WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *ei, + UINT *argerr) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, - dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return IDispatchEx_Invoke(&reader->dispex.IDispatchEx_iface, dispid, riid, lcid, flags, params, result, ei, argerr); } -/*** IVBSAXXMLReader methods ***/ -static HRESULT WINAPI saxxmlreader_getFeature( - IVBSAXXMLReader* iface, - BSTR feature_name, - VARIANT_BOOL *value) +static HRESULT WINAPI saxxmlreader_getFeature(IVBSAXXMLReader *iface, BSTR name, VARIANT_BOOL *value) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return ISAXXMLReader_getFeature(&This->ISAXXMLReader_iface, feature_name, value); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return ISAXXMLReader_getFeature(&reader->ISAXXMLReader_iface, name, value); } -static HRESULT WINAPI saxxmlreader_putFeature( - IVBSAXXMLReader* iface, - BSTR feature_name, - VARIANT_BOOL value) +static HRESULT WINAPI saxxmlreader_putFeature(IVBSAXXMLReader *iface, BSTR name, VARIANT_BOOL value) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return ISAXXMLReader_putFeature(&This->ISAXXMLReader_iface, feature_name, value); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return ISAXXMLReader_putFeature(&reader->ISAXXMLReader_iface, name, value); } -static HRESULT WINAPI saxxmlreader_getProperty( - IVBSAXXMLReader* iface, - BSTR prop, - VARIANT *value) +static HRESULT WINAPI saxxmlreader_getProperty(IVBSAXXMLReader *iface, BSTR name, VARIANT *value) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return internal_getProperty(This, prop, value, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); + + return saxreader_get_property(reader, name, value, true); } -static HRESULT WINAPI saxxmlreader_putProperty( - IVBSAXXMLReader* iface, - BSTR pProp, - VARIANT value) +static HRESULT WINAPI saxxmlreader_putProperty(IVBSAXXMLReader *iface, BSTR name, VARIANT value) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return internal_putProperty(This, pProp, value, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + + TRACE("%p, %s, %s.\n", iface, debugstr_w(name), debugstr_variant(&value)); + + return saxreader_put_property(reader, name, value, true); } -static HRESULT WINAPI saxxmlreader_get_entityResolver( - IVBSAXXMLReader* iface, - IVBSAXEntityResolver **resolver) +static HRESULT WINAPI saxxmlreader_get_entityResolver(IVBSAXXMLReader *iface, IVBSAXEntityResolver **resolver) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return saxreader_get_handler(This, SAXEntityResolver, TRUE, (void**)resolver); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_get_handler(reader, SAXEntityResolver, true, (void **)resolver); } static HRESULT WINAPI saxxmlreader_putref_entityResolver(IVBSAXXMLReader *iface, IVBSAXEntityResolver *resolver) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, resolver); - - return saxreader_put_handler(reader, SAXEntityResolver, resolver, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_put_handler(reader, SAXEntityResolver, resolver, true); } -static HRESULT WINAPI saxxmlreader_get_contentHandler( - IVBSAXXMLReader* iface, - IVBSAXContentHandler **handler) +static HRESULT WINAPI saxxmlreader_get_contentHandler(IVBSAXXMLReader *iface, IVBSAXContentHandler **handler) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return saxreader_get_handler(This, SAXContentHandler, TRUE, (void**)handler); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_get_handler(reader, SAXContentHandler, true, (void **)handler); } static HRESULT WINAPI saxxmlreader_putref_contentHandler(IVBSAXXMLReader *iface, IVBSAXContentHandler *handler) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXContentHandler, handler, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_put_handler(reader, SAXContentHandler, handler, true); } -static HRESULT WINAPI saxxmlreader_get_dtdHandler( - IVBSAXXMLReader* iface, - IVBSAXDTDHandler **handler) +static HRESULT WINAPI saxxmlreader_get_dtdHandler(IVBSAXXMLReader *iface, IVBSAXDTDHandler **handler) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return saxreader_get_handler(This, SAXDTDHandler, TRUE, (void**)handler); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_get_handler(reader, SAXDTDHandler, true, (void **)handler); } static HRESULT WINAPI saxxmlreader_putref_dtdHandler(IVBSAXXMLReader *iface, IVBSAXDTDHandler *handler) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXDTDHandler, handler, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_put_handler(reader, SAXDTDHandler, handler, true); } -static HRESULT WINAPI saxxmlreader_get_errorHandler( - IVBSAXXMLReader* iface, - IVBSAXErrorHandler **handler) +static HRESULT WINAPI saxxmlreader_get_errorHandler(IVBSAXXMLReader *iface, IVBSAXErrorHandler **handler) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return saxreader_get_handler(This, SAXErrorHandler, TRUE, (void**)handler); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_get_handler(reader, SAXErrorHandler, true, (void **)handler); } static HRESULT WINAPI saxxmlreader_putref_errorHandler(IVBSAXXMLReader *iface, IVBSAXErrorHandler *handler) { - saxreader *reader = impl_from_IVBSAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXErrorHandler, handler, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_put_handler(reader, SAXErrorHandler, handler, true); } -static HRESULT WINAPI saxxmlreader_get_baseURL( - IVBSAXXMLReader* iface, - BSTR *pBaseUrl) +static HRESULT WINAPI saxxmlreader_get_baseURL(IVBSAXXMLReader *iface, BSTR *url) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); + FIXME("%p, %p stub\n", iface, url); - FIXME("(%p)->(%p) stub\n", This, pBaseUrl); return E_NOTIMPL; } -static HRESULT WINAPI saxxmlreader_put_baseURL( - IVBSAXXMLReader* iface, - BSTR pBaseUrl) +static HRESULT WINAPI saxxmlreader_put_baseURL(IVBSAXXMLReader *iface, BSTR url) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return ISAXXMLReader_putBaseURL(&This->ISAXXMLReader_iface, pBaseUrl); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return ISAXXMLReader_putBaseURL(&reader->ISAXXMLReader_iface, url); } -static HRESULT WINAPI saxxmlreader_get_secureBaseURL( - IVBSAXXMLReader* iface, - BSTR *pSecureBaseUrl) +static HRESULT WINAPI saxxmlreader_get_secureBaseURL(IVBSAXXMLReader *iface, BSTR *url) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); + FIXME("%p, %p stub\n", iface, url); - FIXME("(%p)->(%p) stub\n", This, pSecureBaseUrl); return E_NOTIMPL; } -static HRESULT WINAPI saxxmlreader_put_secureBaseURL( - IVBSAXXMLReader* iface, - BSTR secureBaseUrl) +static HRESULT WINAPI saxxmlreader_put_secureBaseURL(IVBSAXXMLReader *iface, BSTR url) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return ISAXXMLReader_putSecureBaseURL(&This->ISAXXMLReader_iface, secureBaseUrl); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return ISAXXMLReader_putSecureBaseURL(&reader->ISAXXMLReader_iface, url); } -static HRESULT WINAPI saxxmlreader_parse( - IVBSAXXMLReader* iface, - VARIANT varInput) +static HRESULT WINAPI saxxmlreader_parse(IVBSAXXMLReader *iface, VARIANT input) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return internal_parse(This, varInput, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_parse(reader, input, true); } -static HRESULT WINAPI saxxmlreader_parseURL( - IVBSAXXMLReader* iface, - BSTR url) +static HRESULT WINAPI saxxmlreader_parseURL(IVBSAXXMLReader *iface, BSTR url) { - saxreader *This = impl_from_IVBSAXXMLReader( iface ); - return internal_parseURL(This, url, TRUE); + struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); + return saxreader_parse_url(reader, url, true); } -static const struct IVBSAXXMLReaderVtbl VBSAXXMLReaderVtbl = +static const struct IVBSAXXMLReaderVtbl vbsaxxmlreadervtbl = { saxxmlreader_QueryInterface, saxxmlreader_AddRef, @@ -3173,63 +5891,54 @@ static const struct IVBSAXXMLReaderVtbl VBSAXXMLReaderVtbl = saxxmlreader_parseURL }; -/*** ISAXXMLReader interface ***/ -/*** IUnknown methods ***/ -static HRESULT WINAPI isaxxmlreader_QueryInterface(ISAXXMLReader* iface, REFIID riid, void **ppvObject) +static HRESULT WINAPI isaxxmlreader_QueryInterface(ISAXXMLReader *iface, REFIID riid, void **obj) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return IVBSAXXMLReader_QueryInterface(&This->IVBSAXXMLReader_iface, riid, ppvObject); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return IVBSAXXMLReader_QueryInterface(&reader->IVBSAXXMLReader_iface, riid, obj); } -static ULONG WINAPI isaxxmlreader_AddRef(ISAXXMLReader* iface) +static ULONG WINAPI isaxxmlreader_AddRef(ISAXXMLReader *iface) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return IVBSAXXMLReader_AddRef(&This->IVBSAXXMLReader_iface); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return IVBSAXXMLReader_AddRef(&reader->IVBSAXXMLReader_iface); } -static ULONG WINAPI isaxxmlreader_Release(ISAXXMLReader* iface) +static ULONG WINAPI isaxxmlreader_Release(ISAXXMLReader *iface) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return IVBSAXXMLReader_Release(&This->IVBSAXXMLReader_iface); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return IVBSAXXMLReader_Release(&reader->IVBSAXXMLReader_iface); } -/*** ISAXXMLReader methods ***/ -static HRESULT WINAPI isaxxmlreader_getFeature( - ISAXXMLReader* iface, - const WCHAR *feature_name, - VARIANT_BOOL *value) +static HRESULT WINAPI isaxxmlreader_getFeature(ISAXXMLReader *iface, const WCHAR *name, VARIANT_BOOL *value) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - saxreader_feature feature; + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + enum saxreader_feature feature; - TRACE("(%p)->(%s %p)\n", This, debugstr_w(feature_name), value); + TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); - feature = get_saxreader_feature(feature_name); + feature = get_saxreader_feature(name); - if (This->version < MSXML4 && (feature == ExhaustiveErrors || feature == SchemaValidation)) + if (reader->version < MSXML4 && (feature == ExhaustiveErrors || feature == SchemaValidation)) return E_INVALIDARG; if (feature == Namespaces || feature == NamespacePrefixes || feature == ExhaustiveErrors || feature == SchemaValidation) - return get_feature_value(This, feature, value); + return get_feature_value(reader, feature, value); - FIXME("(%p)->(%s %p) stub\n", This, debugstr_w(feature_name), value); + FIXME("%p, %s, %p stub\n", iface, debugstr_w(name), value); return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_putFeature( - ISAXXMLReader* iface, - const WCHAR *feature_name, - VARIANT_BOOL value) +static HRESULT WINAPI isaxxmlreader_putFeature(ISAXXMLReader *iface, const WCHAR *name, VARIANT_BOOL value) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - saxreader_feature feature; + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + enum saxreader_feature feature; - TRACE("(%p)->(%s %x)\n", This, debugstr_w(feature_name), value); + TRACE("%p, %s, %#x.\n", iface, debugstr_w(name), value); - feature = get_saxreader_feature(feature_name); + feature = get_saxreader_feature(name); /* accepted cases */ if ((feature == ExhaustiveErrors && value == VARIANT_FALSE) || @@ -3237,7 +5946,7 @@ static HRESULT WINAPI isaxxmlreader_putFeature( feature == Namespaces || feature == NamespacePrefixes) { - return set_feature_value(This, feature, value); + return set_feature_value(reader, feature, value); } if (feature == LexicalHandlerParEntities || @@ -3245,156 +5954,121 @@ static HRESULT WINAPI isaxxmlreader_putFeature( feature == ExternalGeneralEntities || feature == ExternalParameterEntities) { - FIXME("(%p)->(%s %x) stub\n", This, debugstr_w(feature_name), value); - return set_feature_value(This, feature, value); + FIXME("%p, %s, %#x stub\n", iface, debugstr_w(name), value); + return set_feature_value(reader, feature, value); } - FIXME("(%p)->(%s %x) stub\n", This, debugstr_w(feature_name), value); + FIXME("%p, %s, %#x stub\n", iface, debugstr_w(name), value); return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_getProperty( - ISAXXMLReader* iface, - const WCHAR *prop, - VARIANT *value) +static HRESULT WINAPI isaxxmlreader_getProperty(ISAXXMLReader *iface, const WCHAR *name, VARIANT *value) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return internal_getProperty(This, prop, value, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); + + return saxreader_get_property(reader, name, value, false); } -static HRESULT WINAPI isaxxmlreader_putProperty( - ISAXXMLReader* iface, - const WCHAR *pProp, - VARIANT value) +static HRESULT WINAPI isaxxmlreader_putProperty(ISAXXMLReader *iface, const WCHAR *name, VARIANT value) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return internal_putProperty(This, pProp, value, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + + TRACE("%p, %s, %s.\n", iface, debugstr_w(name), debugstr_variant(&value)); + + return saxreader_put_property(reader, name, value, false); } -static HRESULT WINAPI isaxxmlreader_getEntityResolver( - ISAXXMLReader* iface, - ISAXEntityResolver **resolver) +static HRESULT WINAPI isaxxmlreader_getEntityResolver(ISAXXMLReader *iface, ISAXEntityResolver **resolver) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return saxreader_get_handler(This, SAXEntityResolver, FALSE, (void**)resolver); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_get_handler(reader, SAXEntityResolver, false, (void **)resolver); } static HRESULT WINAPI isaxxmlreader_putEntityResolver(ISAXXMLReader *iface, ISAXEntityResolver *resolver) { - saxreader *reader = impl_from_ISAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, resolver); - - return saxreader_put_handler(reader, SAXEntityResolver, resolver, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_put_handler(reader, SAXEntityResolver, resolver, false); } -static HRESULT WINAPI isaxxmlreader_getContentHandler( - ISAXXMLReader* iface, - ISAXContentHandler **handler) +static HRESULT WINAPI isaxxmlreader_getContentHandler(ISAXXMLReader *iface, ISAXContentHandler **handler) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return saxreader_get_handler(This, SAXContentHandler, FALSE, (void**)handler); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_get_handler(reader, SAXContentHandler, false, (void **)handler); } static HRESULT WINAPI isaxxmlreader_putContentHandler(ISAXXMLReader *iface, ISAXContentHandler *handler) { - saxreader *reader = impl_from_ISAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXContentHandler, handler, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_put_handler(reader, SAXContentHandler, handler, false); } -static HRESULT WINAPI isaxxmlreader_getDTDHandler( - ISAXXMLReader* iface, - ISAXDTDHandler **handler) +static HRESULT WINAPI isaxxmlreader_getDTDHandler(ISAXXMLReader *iface, ISAXDTDHandler **handler) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return saxreader_get_handler(This, SAXDTDHandler, FALSE, (void**)handler); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_get_handler(reader, SAXDTDHandler, false, (void **)handler); } static HRESULT WINAPI isaxxmlreader_putDTDHandler(ISAXXMLReader *iface, ISAXDTDHandler *handler) { - saxreader *reader = impl_from_ISAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXDTDHandler, handler, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_put_handler(reader, SAXDTDHandler, handler, false); } -static HRESULT WINAPI isaxxmlreader_getErrorHandler( - ISAXXMLReader* iface, - ISAXErrorHandler **handler) +static HRESULT WINAPI isaxxmlreader_getErrorHandler(ISAXXMLReader *iface, ISAXErrorHandler **handler) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return saxreader_get_handler(This, SAXErrorHandler, FALSE, (void**)handler); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_get_handler(reader, SAXErrorHandler, false, (void **)handler); } static HRESULT WINAPI isaxxmlreader_putErrorHandler(ISAXXMLReader *iface, ISAXErrorHandler *handler) { - saxreader *reader = impl_from_ISAXXMLReader(iface); - - TRACE("%p, %p.\n", iface, handler); - - return saxreader_put_handler(reader, SAXErrorHandler, handler, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_put_handler(reader, SAXErrorHandler, handler, false); } -static HRESULT WINAPI isaxxmlreader_getBaseURL( - ISAXXMLReader* iface, - const WCHAR **base_url) +static HRESULT WINAPI isaxxmlreader_getBaseURL(ISAXXMLReader *iface, const WCHAR **url) { - saxreader *This = impl_from_ISAXXMLReader( iface ); + FIXME("%p, %p stub\n", iface, url); - FIXME("(%p)->(%p) stub\n", This, base_url); return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_putBaseURL( - ISAXXMLReader* iface, - const WCHAR *pBaseUrl) +static HRESULT WINAPI isaxxmlreader_putBaseURL(ISAXXMLReader *iface, const WCHAR *url) { - saxreader *This = impl_from_ISAXXMLReader( iface ); + FIXME("%p, %s stub\n", iface, debugstr_w(url)); - FIXME("(%p)->(%s) stub\n", This, debugstr_w(pBaseUrl)); return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_getSecureBaseURL( - ISAXXMLReader* iface, - const WCHAR **pSecureBaseUrl) +static HRESULT WINAPI isaxxmlreader_getSecureBaseURL(ISAXXMLReader *iface, const WCHAR **url) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - FIXME("(%p)->(%p) stub\n", This, pSecureBaseUrl); + FIXME("%p, %p stub\n", iface, url); + return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_putSecureBaseURL( - ISAXXMLReader* iface, - const WCHAR *secureBaseUrl) +static HRESULT WINAPI isaxxmlreader_putSecureBaseURL(ISAXXMLReader *iface, const WCHAR *url) { - saxreader *This = impl_from_ISAXXMLReader( iface ); + FIXME("%p, %s stub\n", iface, debugstr_w(url)); - FIXME("(%p)->(%s) stub\n", This, debugstr_w(secureBaseUrl)); return E_NOTIMPL; } -static HRESULT WINAPI isaxxmlreader_parse( - ISAXXMLReader* iface, - VARIANT varInput) +static HRESULT WINAPI isaxxmlreader_parse(ISAXXMLReader *iface, VARIANT input) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return internal_parse(This, varInput, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_parse(reader, input, false); } -static HRESULT WINAPI isaxxmlreader_parseURL( - ISAXXMLReader* iface, - const WCHAR *url) +static HRESULT WINAPI isaxxmlreader_parseURL(ISAXXMLReader *iface, const WCHAR *url) { - saxreader *This = impl_from_ISAXXMLReader( iface ); - return internal_parseURL(This, url, FALSE); + struct saxreader *reader = impl_from_ISAXXMLReader(iface); + return saxreader_parse_url(reader, url, false); } -static const struct ISAXXMLReaderVtbl SAXXMLReaderVtbl = +static const struct ISAXXMLReaderVtbl saxxmlreadervtbl = { isaxxmlreader_QueryInterface, isaxxmlreader_AddRef, @@ -3419,62 +6093,41 @@ static const struct ISAXXMLReaderVtbl SAXXMLReaderVtbl = isaxxmlreader_parseURL }; -static const tid_t saxreader_iface_tids[] = { +static const tid_t saxreader_iface_tids[] = +{ IVBSAXXMLReader_tid, 0 }; -static dispex_static_data_t saxreader_dispex = { + +static dispex_static_data_t saxreader_dispex = +{ NULL, IVBSAXXMLReader_tid, NULL, saxreader_iface_tids }; -HRESULT SAXXMLReader_create(MSXML_VERSION version, LPVOID *ppObj) +HRESULT SAXXMLReader_create(MSXML_VERSION version, void **obj) { - saxreader *reader; + struct saxreader *reader; - TRACE("(%p)\n", ppObj); + TRACE("%p\n", obj); - reader = malloc(sizeof(*reader)); - if( !reader ) + if (!(reader = calloc(1, sizeof(*reader)))) return E_OUTOFMEMORY; - reader->IVBSAXXMLReader_iface.lpVtbl = &VBSAXXMLReaderVtbl; - reader->ISAXXMLReader_iface.lpVtbl = &SAXXMLReaderVtbl; - reader->ref = 1; - memset(reader->saxhandlers, 0, sizeof(reader->saxhandlers)); - reader->isParsing = FALSE; - reader->xmldecl_version = NULL; - reader->pool.pool = NULL; - reader->pool.index = 0; - reader->pool.len = 0; + reader->IVBSAXXMLReader_iface.lpVtbl = &vbsaxxmlreadervtbl; + reader->ISAXXMLReader_iface.lpVtbl = &saxxmlreadervtbl; + reader->refcount = 1; reader->features = Namespaces | NamespacePrefixes; reader->version = version; reader->empty_bstr = SysAllocString(L""); - init_dispex(&reader->dispex, (IUnknown*)&reader->IVBSAXXMLReader_iface, &saxreader_dispex); - - memset(&reader->sax, 0, sizeof(xmlSAXHandler)); - reader->sax.initialized = XML_SAX2_MAGIC; - reader->sax.startDocument = libxmlStartDocument; - reader->sax.endDocument = libxmlEndDocument; - reader->sax.startElementNs = libxmlStartElementNS; - reader->sax.endElementNs = libxmlEndElementNS; - reader->sax.characters = libxmlCharacters; - reader->sax.setDocumentLocator = libxmlSetDocumentLocator; - reader->sax.comment = libxmlComment; - reader->sax.error = libxmlFatalError; - reader->sax.fatalError = libxmlFatalError; - reader->sax.cdataBlock = libxml_cdatablock; - reader->sax.resolveEntity = libxmlresolveentity; - reader->sax.processingInstruction = libxml_pi; - reader->sax.internalSubset = libxml_internalsubset; - reader->sax.externalSubset = libxml_externalsubset; - - *ppObj = &reader->IVBSAXXMLReader_iface; - - TRACE("returning iface %p\n", *ppObj); + init_dispex(&reader->dispex, (IUnknown *)&reader->IVBSAXXMLReader_iface, &saxreader_dispex); + + *obj = &reader->IVBSAXXMLReader_iface; + + TRACE("returning iface %p\n", *obj); return S_OK; } diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index 56164f66e2f..aca56a00de5 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -2524,12 +2524,7 @@ static void test_saxreader(void) set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); - else - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE); - + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); IStream_Release(stream); V_VT(&var) = VT_UNKNOWN; @@ -2655,13 +2650,7 @@ static void test_saxreader(void) V_BSTR(&var) = _bstr_(xmlspace_attr); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE); - } - else - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE); /* switch off 'namespaces' feature */ hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_FALSE); @@ -2681,7 +2670,7 @@ static void test_saxreader(void) set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); IStream_Release(stream); hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2798,7 +2787,10 @@ static void test_saxreader_cdata(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: cdata test", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + else + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); IStream_Release(stream); @@ -2816,7 +2808,10 @@ static void test_saxreader_cdata(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: cdata test 2", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + else + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); IStream_Release(stream); @@ -2834,7 +2829,7 @@ static void test_saxreader_cdata(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: cdata test 3", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); IStream_Release(stream); @@ -2891,7 +2886,7 @@ static void test_saxreader_pi(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: pi test", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); VariantClear(&var); ISAXXMLReader_Release(reader); @@ -2947,7 +2942,7 @@ static void test_saxreader_characters(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: char data test", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); VariantClear(&var); V_VT(&var) = VT_UNKNOWN; @@ -2962,7 +2957,10 @@ static void test_saxreader_characters(void) hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: char data test", table->name); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); + else + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); VariantClear(&var); ISAXXMLReader_Release(reader); @@ -3145,7 +3143,6 @@ static void test_saxreader_properties(void) V_DISPATCH(&v) = (IDispatch *)0xdeadbeef; hr = IVBSAXXMLReader_getProperty(vb_reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), &v); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(V_VT(&v) == VT_DISPATCH, "Unexpected type %d.\n", V_VT(&v)); ok(!V_DISPATCH(&v), "Unexpected value %p.\n", V_UNKNOWN(&v)); @@ -3153,7 +3150,6 @@ static void test_saxreader_properties(void) V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = IVBSAXXMLReader_getProperty(vb_reader, _bstr_("http://xml.org/sax/properties/declaration-handler"), &v); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(V_VT(&v) == VT_DISPATCH, "Unexpected type %d.\n", V_VT(&v)); ok(!V_DISPATCH(&v), "Unexpected value %p.\n", V_UNKNOWN(&v)); @@ -3497,9 +3493,9 @@ static void test_saxreader_encoding(void) create_test_file(testXmlA, ucs4_le_test, sizeof(ucs4_le_test)); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40)) + todo_wine ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); DeleteFileA(testXmlA); @@ -3507,9 +3503,9 @@ static void test_saxreader_encoding(void) create_test_file(testXmlA, ucs4_be_test, sizeof(ucs4_be_test)); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40)) + todo_wine ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); DeleteFileA(testXmlA); @@ -3535,29 +3531,28 @@ static void test_saxreader_encoding(void) set_expected_seq(xml_win1252_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1252_seq, "Content test with windows-1252", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1252_seq, "Content test with windows-1252", FALSE); DeleteFileA(testXmlA); create_test_file(testXmlA, xml_win1253_test, sizeof(xml_win1253_test) - 1); set_expected_seq(xml_win1253_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1253_seq, "Content test with windows-1252", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1253_seq, "Content test with windows-1253", FALSE); DeleteFileA(testXmlA); create_test_file(testXmlA, xml_us_ascii_test, sizeof(xml_us_ascii_test) - 1); set_expected_seq(xml_us_ascii_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_us_ascii_seq, "Content test with us-ascii", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_us_ascii_seq, "Content test with us-ascii", FALSE); DeleteFileA(testXmlA); create_test_file(testXmlA, xml_iso_8859_1_test, sizeof(xml_iso_8859_1_test) - 1); set_expected_seq(xml_iso_8859_1_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_iso_8859_1_seq, "Content test with iso-8859-1", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_iso_8859_1_seq, "Content test with iso-8859-1", FALSE); DeleteFileA(testXmlA); ISAXXMLReader_Release(reader); @@ -6534,7 +6529,6 @@ static void test_saxreader_dtd(void) V_BSTR(&var) = SysAllocString(xml_dtd); hr = ISAXXMLReader_parse(reader, var); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); VariantClear(&var); diff --git a/dlls/msxml3/tests/xmldoc.c b/dlls/msxml3/tests/xmldoc.c index e4ba2d5c154..898b5f3c114 100644 --- a/dlls/msxml3/tests/xmldoc.c +++ b/dlls/msxml3/tests/xmldoc.c @@ -1314,7 +1314,6 @@ static void test_xmldoc_version(void) SysFreeString(s); hr = load_document(doc, doc_data4, sizeof(doc_data4) - 1); - todo_wine ok(hr == XML_E_INVALID_VERSION, "Unexpected hr %#lx.\n", hr); IXMLDocument_Release(doc); diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 3f3657fa900..52519cee939 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -4749,7 +4749,7 @@ static void test_saxreader(void) V_BSTR(&var) = _bstr_(xmlspace_attr); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE); /* switch off 'namespaces' feature */ hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_FALSE); @@ -4763,7 +4763,7 @@ static void test_saxreader(void) set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); IStream_Release(stream); hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_TRUE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4813,7 +4813,7 @@ static void test_saxreader(void) set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "pi test 1", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "pi test 1", FALSE); VariantClear(&var); ISAXXMLReader_Release(reader); @@ -5059,7 +5059,7 @@ static void test_saxreader_cdata(void) set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 3", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 3", FALSE); IStream_Release(stream); @@ -5125,7 +5125,7 @@ static void test_saxreader_characters(void) set_expected_seq(chardata_test); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, chardata_test, "char data test", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, chardata_test, "char data test", FALSE); VariantClear(&var); V_VT(&var) = VT_UNKNOWN; @@ -5164,7 +5164,7 @@ static void test_saxreader_pi(void) set_expected_seq(pi_test); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok_sequence(sequences, CONTENT_HANDLER_INDEX, pi_test, "pi test 1", TRUE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, pi_test, "pi test 1", FALSE); VariantClear(&var); ISAXXMLReader_Release(reader); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/saxreader.c | 62 +++++++++++++++- dlls/msxml3/tests/saxreader.c | 135 ++++++++++++++++++++++++++++++++++ dlls/msxml6/tests/saxreader.c | 117 +++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+), 3 deletions(-) diff --git a/dlls/msxml3/saxreader.c b/dlls/msxml3/saxreader.c index 7846b512910..0321f18750f 100644 --- a/dlls/msxml3/saxreader.c +++ b/dlls/msxml3/saxreader.c @@ -455,6 +455,7 @@ struct saxreader BSTR xmldecl_version; BSTR xmldecl_standalone; BSTR xmldecl_encoding; + int max_xml_size; BSTR empty_bstr; MSXML_VERSION version; }; @@ -790,6 +791,7 @@ struct input_buffer UINT code_page; struct text_position position; size_t consumed; + size_t raw_size; bool last_cr; unsigned int chunk_size; @@ -1892,6 +1894,22 @@ static bool saxreader_reserve_buffer(struct saxlocator *locator, struct encoded_ buffer->written + size, sizeof(*buffer->data)); } +static bool saxreader_limit_xml_size(struct saxlocator *locator, ULONG read) +{ + if (locator->saxreader->max_xml_size > 0) + { + locator->buffer.raw_size += read; + if (locator->buffer.raw_size > locator->saxreader->max_xml_size * 1024) + { + saxreader_set_error(locator, E_FAIL); + locator->eos = true; + return false; + } + } + + return true; +} + static bool saxreader_stream_read(struct saxlocator *locator, void *buffer, ULONG size, ULONG *read) { HRESULT hr; @@ -1906,6 +1924,9 @@ static bool saxreader_stream_read(struct saxlocator *locator, void *buffer, ULON return false; } + if (!saxreader_limit_xml_size(locator, *read)) + return false; + locator->eos = *read == 0; return true; } @@ -5205,6 +5226,10 @@ static void saxreader_detect_encoding(struct saxlocator *locator) if (FAILED(hr = ISequentialStream_Read(locator->stream, raw->data, locator->buffer.chunk_size, &read))) return saxreader_set_error(locator, hr); + + if (!saxreader_limit_xml_size(locator, read)) + return; + if (!read) return saxreader_set_error(locator, E_SAX_MISSINGROOT); @@ -5562,6 +5587,18 @@ static HRESULT saxreader_put_handler_from_variant(struct saxreader *reader, enum return S_OK; } +static HRESULT saxreader_get_int_property(const VARIANT *v, int *ret) +{ + VARIANT dest; + + VariantInit(&dest); + if (FAILED(VariantChangeType(&dest, v, 0, VT_I4))) + return E_FAIL; + + *ret = V_I4(&dest); + return S_OK; +} + static HRESULT saxreader_put_property(struct saxreader *reader, const WCHAR *prop, VARIANT value, bool vbInterface) { VARIANT *v; @@ -5580,9 +5617,21 @@ static HRESULT saxreader_put_property(struct saxreader *reader, const WCHAR *pro if (!wcscmp(prop, L"max-xml-size")) { - if (V_VT(v) == VT_I4 && V_I4(v) == 0) return S_OK; - FIXME("(%p)->(%s): max-xml-size unsupported\n", reader, debugstr_variant(v)); - return E_NOTIMPL; + int size; + + if (FAILED(saxreader_get_int_property(&value, &size))) + return E_FAIL; + + if (size < 0) + return E_INVALIDARG; + + if (reader->version >= MSXML4 && size > 4194304) + return E_INVALIDARG; + if (reader->version < MSXML4 && size >= 4194304) + return E_INVALIDARG; + + reader->max_xml_size = size; + return S_OK; } if (!wcscmp(prop, L"max-element-depth")) @@ -5642,6 +5691,13 @@ static HRESULT saxreader_get_property(const struct saxreader *reader, const WCHA return return_bstr(reader->xmldecl_version, &V_BSTR(value)); } + if (!wcscmp(prop, L"max-xml-size")) + { + V_VT(value) = VT_I4; + V_I4(value) = reader->max_xml_size; + return S_OK; + } + FIXME("(%p)->(%s) unsupported property\n", reader, debugstr_w(prop)); return E_NOTIMPL; diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index aca56a00de5..2bd439d0905 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -3256,6 +3256,140 @@ static void test_saxreader_properties(void) free_bstrs(); } +static void test_saxreader_max_xml_size(void) +{ + static const char test_text[] = + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa"; + const struct msxmlsupported_data_t *table = reader_support_data; + ISAXXMLReader *reader; + LARGE_INTEGER pos; + IStream *stream; + DWORD written; + VARIANT var; + HRESULT hr; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IStream_Write(stream, "<a>", 3, &written); + for (int i = 0; i < 20; ++i) + IStream_Write(stream, test_text, sizeof(test_text)-1, &written); + IStream_Write(stream, "</a>", 4, &written); + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + while (table->clsid) + { + if (!is_clsid_supported(table->clsid, reader_support_data)) + { + table++; + continue; + } + + winetest_push_context("%s", table->name); + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 123; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(!V_I4(&var), "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_R4; + V_R4(&var) = 10.0; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("abc"); + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_I4; + V_I4(&var) = -123; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194303; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194304; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + else + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194305; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Limit to 1K */ + V_VT(&var) = VT_I4; + V_I4(&var) = 1; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 3; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + table++; + + winetest_pop_context(); + } + + free_bstrs(); + IStream_Release(stream); +} + struct feature_ns_entry_t { const GUID *guid; const char *clsid; @@ -6561,6 +6695,7 @@ START_TEST(saxreader) test_saxreader(); test_saxreader_properties(); + test_saxreader_max_xml_size(); test_saxreader_features(); test_saxreader_encoding(); test_saxreader_dispex(); diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 52519cee939..56847bb8525 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -5172,6 +5172,122 @@ static void test_saxreader_pi(void) free_bstrs(); } +static void test_saxreader_max_xml_size(void) +{ + static const char test_text[] = + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa"; + ISAXXMLReader *reader; + LARGE_INTEGER pos; + IStream *stream; + DWORD written; + VARIANT var; + HRESULT hr; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IStream_Write(stream, "<a>", 3, &written); + for (int i = 0; i < 20; ++i) + IStream_Write(stream, test_text, sizeof(test_text)-1, &written); + IStream_Write(stream, "</a>", 4, &written); + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 123; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(!V_I4(&var), "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_R4; + V_R4(&var) = 10.0; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("abc"); + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_I4; + V_I4(&var) = -123; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194303; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194304; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194305; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Limit to 1K */ + V_VT(&var) = VT_I4; + V_I4(&var) = 1; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 3; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + + free_bstrs(); + IStream_Release(stream); +} + START_TEST(saxreader) { HRESULT hr; @@ -5187,6 +5303,7 @@ START_TEST(saxreader) { test_saxreader(); test_saxreader_properties(); + test_saxreader_max_xml_size(); test_saxreader_features(); test_saxreader_encoding(); test_saxreader_dispex(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/saxreader.c | 287 +++++++++++++++++++++++++++++++++- dlls/msxml6/tests/saxreader.c | 23 +++ 2 files changed, 307 insertions(+), 3 deletions(-) diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index 2bd439d0905..1330093bbaf 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -344,6 +344,7 @@ typedef enum _CH { CH_SKIPPEDENTITY, LH_STARTCDATA, LH_ENDCDATA, + LH_COMMENT, EH_ERROR, EH_FATALERROR, EH_IGNORABLEWARNING, @@ -365,6 +366,7 @@ static const char *event_names[EVENT_LAST] = { "skippedEntity", "startCDATA", "endCDATA", + "comment", "error", "fatalError", "ignorableWarning" @@ -614,6 +616,7 @@ static void ok_sequence_(struct call_sequence **seq, int sequence_index, break; case CH_CHARACTERS: case CH_IGNORABLEWHITESPACE: + case LH_COMMENT: /* char data */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); break; @@ -2035,10 +2038,16 @@ static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface) } static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface, - const WCHAR * pChars, int nChars) + const WCHAR *chars, int len) { - ok(0, "call not expected\n"); - return E_NOTIMPL; + struct call_entry call; + + init_call_entry(locator, &call); + call.id = LH_COMMENT; + call.arg1W = SysAllocStringLen(chars, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); } static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl = @@ -3390,6 +3399,277 @@ static void test_saxreader_max_xml_size(void) IStream_Release(stream); } +static struct call_entry normalize_line_breaks_text_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { CH_CHARACTERS, 1, 4, S_OK, L"a\n" }, + { CH_CHARACTERS, 2, 1, S_OK, L"b" }, + { CH_ENDELEMENT, 2, 4, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_off_text_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { CH_CHARACTERS, 1, 4, S_OK, L"a\rb" }, + { CH_ENDELEMENT, 2, 4, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct attribute_entry normalize_line_breaks_attrs[] = +{ + { L"", L"attr", L"attr", L"a b" }, + { NULL } +}; + +static struct call_entry normalize_line_breaks_attr_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 2, 5, S_OK, L"", L"e", L"e", normalize_line_breaks_attrs }, + { CH_ENDELEMENT, 2, 7, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_comment_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { LH_COMMENT, 1, 8, S_OK, L" ab\n" }, + { LH_COMMENT, 2, 1, S_OK, L"c " }, + { CH_ENDELEMENT, 2, 8, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_off_comment_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { LH_COMMENT, 1, 8, S_OK, L" ab\rc " }, + { CH_ENDELEMENT, 2, 8, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_cdata_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { LH_STARTCDATA, 1, 13, S_OK }, + { CH_CHARACTERS, 1, 13, S_OK, L" ab\n" }, + { CH_CHARACTERS, 2, 1, S_OK, L"c " }, + { LH_ENDCDATA, 2, 1, S_OK }, + { CH_ENDELEMENT, 2, 8, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_off_cdata_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { LH_STARTCDATA, 1, 13, S_OK }, + { CH_CHARACTERS, 1, 13, S_OK, L" ab\rc " }, + { LH_ENDCDATA, 1, 13, S_OK }, + { CH_ENDELEMENT, 2, 8, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_pi_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { CH_PROCESSINGINSTRUCTION, 1, 9, S_OK, L"pi", L"ab\nc " }, + { CH_ENDELEMENT, 2, 7, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry normalize_line_breaks_off_pi_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, + { CH_STARTDOCUMENT, 0, 0, S_OK }, + { CH_STARTELEMENT, 1, 4, S_OK, L"", L"e", L"e" }, + { CH_PROCESSINGINSTRUCTION, 1, 9, S_OK, L"pi", L"ab\rc " }, + { CH_ENDELEMENT, 2, 7, S_OK, L"", L"e", L"e" }, + { CH_ENDDOCUMENT, 0, 0, S_OK }, + { CH_ENDTEST } +}; + +static void test_saxreader_normalize_line_breaks(void) +{ + static struct msxmlsupported_data_t classes[] = + { + { &CLSID_SAXXMLReader, "SAXReader" }, + { &CLSID_SAXXMLReader30, "SAXReader30" }, + { NULL } + }; + const struct msxmlsupported_data_t *table = classes; + ISAXXMLReader *reader; + VARIANT_BOOL v; + VARIANT var; + HRESULT hr; + + if (is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data)) + { + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + v = 123; + hr = ISAXXMLReader_getFeature(reader, L"normalize-line-breaks", &v); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(v == 123, "Unexpected value %d.\n", v); + + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_FALSE); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_TRUE); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + } + + while (table->clsid) + { + if (!is_clsid_supported(table->clsid, reader_support_data)) + { + table++; + continue; + } + + winetest_push_context("%s", table->name); + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + v = VARIANT_FALSE; + hr = ISAXXMLReader_getFeature(reader, L"normalize-line-breaks", &v); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(v == VARIANT_TRUE, "Unexpected value %d.\n", v); + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)&lexicalhandler.ISAXLexicalHandler_iface; + init_saxlexicalhandler(&lexicalhandler, S_OK); + hr = ISAXXMLReader_putProperty(reader, L"http://xml.org/sax/properties/lexical-handler", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Text content */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e>a\rb</e>"); + set_expected_seq(normalize_line_breaks_text_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_text_seq, "Normalize line breaks: text", FALSE); + + /* Attribute values */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e attr=\"a\rb\" ></e>"); + set_expected_seq(normalize_line_breaks_attr_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_attr_seq, "Normalize line breaks: attribute", TRUE); + + /* Comments */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><!-- ab\rc --></e>"); + set_expected_seq(normalize_line_breaks_comment_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_comment_seq, "Normalize line breaks: comment", TRUE); + + /* CDATA */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><![CDATA[ ab\rc ]]></e>"); + set_expected_seq(normalize_line_breaks_cdata_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_cdata_seq, "Normalize line breaks: cdata", FALSE); + + /* PI */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><?pi ab\rc ?></e>"); + set_expected_seq(normalize_line_breaks_pi_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_pi_seq, "Normalize line breaks: PI", TRUE); + + /* Disable normalization */ + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_FALSE); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Text content */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e>a\rb</e>"); + set_expected_seq(normalize_line_breaks_off_text_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_off_text_seq, "Normalize line breaks (off): text", TRUE); + + /* Attribute values */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e attr=\"a\rb\" ></e>"); + set_expected_seq(normalize_line_breaks_attr_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_attr_seq, "Normalize line breaks (off): attribute", TRUE); + + /* Comments */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><!-- ab\rc --></e>"); + set_expected_seq(normalize_line_breaks_off_comment_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_off_comment_seq, "Normalize line breaks (off): comment", FALSE); + + /* CDATA */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><![CDATA[ ab\rc ]]></e>"); + set_expected_seq(normalize_line_breaks_off_cdata_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_off_cdata_seq, "Normalize line breaks (off): cdata", TRUE); + + /* PI */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<e><?pi ab\rc ?></e>"); + set_expected_seq(normalize_line_breaks_off_pi_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, normalize_line_breaks_off_pi_seq, "Normalize line breaks (off): PI", FALSE); + + ISAXXMLReader_Release(reader); + table++; + + winetest_pop_context(); + } + + free_bstrs(); +} + struct feature_ns_entry_t { const GUID *guid; const char *clsid; @@ -6696,6 +6976,7 @@ START_TEST(saxreader) test_saxreader(); test_saxreader_properties(); test_saxreader_max_xml_size(); + test_saxreader_normalize_line_breaks(); test_saxreader_features(); test_saxreader_encoding(); test_saxreader_dispex(); diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 56847bb8525..1522589e622 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -4821,6 +4821,28 @@ static void test_saxreader(void) free_bstrs(); } +static void test_saxreader_normalize_line_breaks(void) +{ + ISAXXMLReader *reader; + VARIANT_BOOL v; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + v = 123; + hr = ISAXXMLReader_getFeature(reader, L"normalize-line-breaks", &v); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(v == 123, "Unexpected value %d.\n", v); + + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_FALSE); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); +} + static const WCHAR *feature_names[] = { L"http://xml.org/sax/features/namespaces", @@ -5304,6 +5326,7 @@ START_TEST(saxreader) test_saxreader(); test_saxreader_properties(); test_saxreader_max_xml_size(); + test_saxreader_normalize_line_breaks(); test_saxreader_features(); test_saxreader_encoding(); test_saxreader_dispex(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/saxreader.c | 746 +---- dlls/msxml4/tests/Makefile.in | 1 + dlls/msxml4/tests/saxreader.c | 5802 +++++++++++++++++++++++++++++++++ 3 files changed, 5891 insertions(+), 658 deletions(-) create mode 100644 dlls/msxml4/tests/saxreader.c diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index 1330093bbaf..8a747362776 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -788,25 +788,6 @@ static struct call_entry content_handler_test1[] = { { CH_ENDTEST } }; -/* applies to versions 4 and 6 */ -static struct call_entry content_handler_test1_alternate[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, - { CH_CHARACTERS, 3, 4, S_OK, L"\n " }, - { CH_STARTELEMENT, 3, 11, S_OK, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 3, 16, S_OK, L"1234" }, - { CH_ENDELEMENT, 3, 24, S_OK, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 4, 4, S_OK, L"\n " }, - { CH_STARTELEMENT, 4, 9, S_OK, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 4, 22, S_OK, L"Captain Ahab" }, - { CH_ENDELEMENT, 4, 28, S_OK, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 5, 1, S_OK, L"\n" }, - { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, - { CH_ENDDOCUMENT, 6, 0, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry content_handler_test2[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, @@ -827,38 +808,12 @@ static struct call_entry content_handler_test2[] = { { CH_ENDTEST } }; -static struct call_entry content_handler_test2_alternate[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 21, S_OK }, - { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, - { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 3, 2, S_OK, L"\t" }, - { CH_STARTELEMENT, 3, 9, S_OK, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 3, 14, S_OK, L"1234" }, - { CH_ENDELEMENT, 3, 22, S_OK, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 4, 2, S_OK, L"\t" }, - { CH_STARTELEMENT, 4, 7, S_OK, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 4, 20, S_OK, L"Captain Ahab" }, - { CH_ENDELEMENT, 4, 26, S_OK, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 5, 0, S_OK, L"\n" }, - { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, - { CH_ENDDOCUMENT, 6, 0, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry content_handler_testerror[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, E_FAIL }, { EH_FATALERROR, 0, 0, E_FAIL }, { CH_ENDTEST } }; -static struct call_entry content_handler_testerror_alternate[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL }, - { EH_FATALERROR, 1, 0, E_FAIL }, - { CH_ENDTEST } -}; - static struct call_entry content_handler_test_callback_rets[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_FALSE }, { CH_STARTDOCUMENT, 0, 0, S_FALSE }, @@ -866,24 +821,6 @@ static struct call_entry content_handler_test_callback_rets[] = { { CH_ENDTEST } }; -static struct call_entry content_handler_test_callback_rets_alt[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE }, - { CH_STARTDOCUMENT, 1, 22, S_FALSE }, - { CH_STARTELEMENT, 2, 13, S_FALSE, L"", L"BankAccount", L"BankAccount" }, - { CH_CHARACTERS, 3, 4, S_FALSE, L"\n " }, - { CH_STARTELEMENT, 3, 11, S_FALSE, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 3, 16, S_FALSE, L"1234" }, - { CH_ENDELEMENT, 3, 24, S_FALSE, L"", L"Number", L"Number" }, - { CH_CHARACTERS, 4, 4, S_FALSE, L"\n " }, - { CH_STARTELEMENT, 4, 9, S_FALSE, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 4, 22, S_FALSE, L"Captain Ahab" }, - { CH_ENDELEMENT, 4, 28, S_FALSE, L"", L"Name", L"Name" }, - { CH_CHARACTERS, 5, 1, S_FALSE, L"\n" }, - { CH_ENDELEMENT, 5, 14, S_FALSE, L"", L"BankAccount", L"BankAccount" }, - { CH_ENDDOCUMENT, 6, 0, S_FALSE }, - { CH_ENDTEST } -}; - static struct attribute_entry ch_attributes1[] = { { L"", L"", L"xmlns:test", L"prefix_test" }, { L"", L"", L"xmlns", L"prefix" }, @@ -916,56 +853,6 @@ static struct call_entry content_handler_test_attributes[] = { { CH_ENDTEST } }; -static struct attribute_entry ch_attributes_alt_4[] = -{ - { L"prefix_test", L"arg1", L"test:arg1", L"arg1" }, - { L"", L"arg2", L"arg2", L"arg2" }, - { L"prefix_test", L"ar3", L"test:ar3", L"arg3" }, - { L"", L"", L"xmlns:test", L"prefix_test" }, - { L"", L"", L"xmlns", L"prefix" }, - { NULL } -}; - -static struct call_entry content_handler_test_attributes_alternate_4[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, - { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_alt_4 }, - { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, - { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, - { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", ch_attributes2 }, - { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, - { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, - { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, - { CH_ENDDOCUMENT, 4, 0, S_OK }, - { CH_ENDTEST } -}; - -/* 'namespace' feature switched off */ -static struct attribute_entry ch_attributes_alt_no_ns[] = { - { L"", L"", L"xmlns:test", L"prefix_test" }, - { L"", L"", L"xmlns", L"prefix" }, - { L"", L"", L"test:arg1", L"arg1" }, - { L"", L"", L"arg2", L"arg2" }, - { L"", L"", L"test:ar3", L"arg3" }, - { NULL } -}; - -static struct call_entry content_handler_test_attributes_alt_no_ns[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 2, 95, S_OK, L"", L"", L"document", ch_attributes_alt_no_ns }, - { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, - { CH_STARTELEMENT, 3, 24, S_OK, L"", L"", L"node1", ch_attributes2 }, - { CH_ENDELEMENT, 3, 24, S_OK, L"", L"", L"node1" }, - { CH_ENDELEMENT, 3, 35, S_OK, L"", L"", L"document" }, - { CH_ENDDOCUMENT, 4, 0, S_OK }, - { CH_ENDTEST } -}; - /* 'namespaces' is on, 'namespace-prefixes' if off */ static struct attribute_entry ch_attributes_no_prefix[] = { @@ -975,24 +862,6 @@ static struct attribute_entry ch_attributes_no_prefix[] = { NULL } }; -static struct call_entry content_handler_test_attributes_alt_no_prefix[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, - { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, - { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_no_prefix }, - { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, - { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, - { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", NULL }, - { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, - { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, - { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, - { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, - { CH_ENDDOCUMENT, 4, 0, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry content_handler_test_attributes_no_prefix[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, @@ -1027,16 +896,6 @@ static struct call_entry xmlspaceattr_test[] = { { CH_ENDTEST } }; -static struct call_entry xmlspaceattr_test_alternate[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 39, S_OK }, - { CH_STARTELEMENT, 1, 63, S_OK, L"", L"a", L"a", xmlspace_attrs }, - { CH_CHARACTERS, 1, 80, S_OK, L" Some text data " }, - { CH_ENDELEMENT, 1, 83, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 1, 83, S_OK }, - { CH_ENDTEST } -}; - /* attribute value normalization test */ static const char attribute_normalize[] = "<?xml version=\"1.0\" ?>\n" @@ -1057,15 +916,6 @@ static struct call_entry attribute_norm[] = { { CH_ENDTEST } }; -static struct call_entry attribute_norm_alt[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 8, 3, S_OK, L"", L"a", L"a", attribute_norm_attrs }, - { CH_ENDELEMENT, 8, 3, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 9, 0, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry cdata_test[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, @@ -1118,17 +968,6 @@ static struct call_entry pi_test[] = { CH_ENDTEST } }; -static struct call_entry pi_test_v4[] = -{ - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { CH_PROCESSINGINSTRUCTION, 1, 41, S_OK, L"t", L"some text " }, - { CH_ENDELEMENT, 1, 45, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 1, 45, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry chardata_test[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, @@ -1144,21 +983,6 @@ static struct call_entry chardata_test[] = { CH_ENDTEST } }; -static struct call_entry chardata_test_v4[] = -{ - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { CH_CHARACTERS, 2, 4, S_OK, L"\nabc" }, - { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, - { CH_CHARACTERS, 3, 2, S_OK, L"de\nf" }, - { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, - { CH_CHARACTERS, 4, 1, S_OK, L"gh\n" }, - { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 4, 4, S_OK }, - { CH_ENDTEST } -}; - static struct call_entry chardata_test2[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, @@ -1176,72 +1000,6 @@ static struct call_entry chardata_test2[] = { CH_ENDTEST } }; -static struct call_entry chardata_test2_v4[] = -{ - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 2, 4, S_OK, L"abc" }, - { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, - { CH_CHARACTERS, 2, 9, S_OK, L"de" }, - { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 3, 2, S_OK, L"f" }, - { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, - { CH_CHARACTERS, 3, 8, S_OK, L"gh" }, - { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, - { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 4, 4, S_OK }, - { CH_ENDTEST } -}; - -/* this is what MSXML6 does */ -static struct call_entry cdata_test_alt[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 1, 40, S_OK, L"Some " }, - { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 3, 1, S_OK, L"text\n" }, - { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 6, 3, S_OK, L"data\n\n" }, - { LH_ENDCDATA, 6, 3, S_OK }, - { CH_ENDELEMENT, 6, 7, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 6, 7, S_OK }, - { CH_ENDTEST } -}; - -static struct call_entry cdata_test2_alt[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 2, 1, S_OK, L"\n" }, - { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 3, 6, S_OK, L"Some " }, - { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 5, 1, S_OK, L"text\n" }, - { CH_CHARACTERS, 6, 0, S_OK, L"\n" }, - { CH_CHARACTERS, 8, 3, S_OK, L"data\n\n" }, - { LH_ENDCDATA, 8, 3, S_OK }, - { CH_ENDELEMENT, 8, 7, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 8, 7, S_OK }, - { CH_ENDTEST } -}; - -static struct call_entry cdata_test3_alt[] = { - { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, - { CH_STARTDOCUMENT, 1, 22, S_OK }, - { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, - { LH_STARTCDATA, 1, 34, S_OK }, - { CH_CHARACTERS, 1, 51, S_OK, L"Some text data" }, - { LH_ENDCDATA, 1, 51, S_OK }, - { CH_ENDELEMENT, 1, 55, S_OK, L"", L"a", L"a" }, - { CH_ENDDOCUMENT, 1, 55, S_OK }, - { CH_ENDTEST } -}; - static struct attribute_entry read_test_attrs[] = { { L"", L"attr", L"attr", L"val" }, @@ -2368,7 +2126,6 @@ static struct msxmlsupported_data_t reader_support_data[] = { { &CLSID_SAXXMLReader, "SAXReader" }, { &CLSID_SAXXMLReader30, "SAXReader30" }, - { &CLSID_SAXXMLReader40, "SAXReader40" }, { NULL } }; @@ -2415,6 +2172,7 @@ static void test_saxreader(void) { struct call_entry *test_seq; ISAXEntityResolver *resolver; + IVBSAXXMLReader *vb_reader; BSTR str; if (!is_clsid_supported(table->clsid, reader_support_data)) @@ -2434,20 +2192,13 @@ static void test_saxreader(void) check_interface(reader, &IID_ISAXLocator, FALSE); check_interface(reader, &IID_IVBSAXLocator, FALSE); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - msxml_version = 4; - else msxml_version = 0; - /* crashes on old versions */ - if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - hr = ISAXXMLReader_getContentHandler(reader, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXXMLReader_getContentHandler(reader, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXXMLReader_getErrorHandler(reader, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - } + hr = ISAXXMLReader_getErrorHandler(reader, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); hr = ISAXXMLReader_getContentHandler(reader, &content); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2473,10 +2224,7 @@ static void test_saxreader(void) V_VT(&var) = VT_BSTR; V_BSTR(&var) = SysAllocString(szSimpleXML); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_test1_alternate; - else - test_seq = content_handler_test1; + test_seq = content_handler_test1; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2525,11 +2273,7 @@ static void test_saxreader(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_test_attributes_alternate_4; - else - test_seq = content_handler_test_attributes; - + test_seq = content_handler_test_attributes; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2550,11 +2294,7 @@ static void test_saxreader(void) V_VT(&var) = VT_BSTR; V_BSTR(&var) = SysAllocString(carriage_ret_test); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_test2_alternate; - else - test_seq = content_handler_test2; - + test_seq = content_handler_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2568,56 +2308,33 @@ static void test_saxreader(void) WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL); CloseHandle(file); - /* crashes on newer versions */ - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader) || - IsEqualGUID(table->clsid, &CLSID_SAXXMLReader30)) - { - IVBSAXXMLReader *vb_reader; - - hr = ISAXXMLReader_parseURL(reader, NULL); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + hr = ISAXXMLReader_parseURL(reader, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void **)&vb_reader); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IVBSAXXMLReader_parseURL(vb_reader, NULL); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - IVBSAXXMLReader_Release(vb_reader); - } + hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void **)&vb_reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IVBSAXXMLReader_parseURL(vb_reader, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + IVBSAXXMLReader_Release(vb_reader); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_test1_alternate; - else - test_seq = content_handler_test1; + test_seq = content_handler_test1; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE); /* error handler */ - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_testerror_alternate; - else - test_seq = content_handler_testerror; + test_seq = content_handler_testerror; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE); /* callback ret values */ - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - test_seq = content_handler_test_callback_rets_alt; - set_expected_seq(test_seq); - hr = ISAXXMLReader_parseURL(reader, L"test.xml"); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } - else - { - test_seq = content_handler_test_callback_rets; - set_expected_seq(test_seq); - hr = ISAXXMLReader_parseURL(reader, L"test.xml"); - ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); - } + test_seq = content_handler_test_callback_rets; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE); DeleteFileA(testXmlA); @@ -2635,11 +2352,7 @@ static void test_saxreader(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)doc; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = content_handler_test2_alternate; - else - test_seq = content_handler_test2; - + test_seq = content_handler_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2647,13 +2360,7 @@ static void test_saxreader(void) IXMLDOMDocument_Release(doc); /* xml:space test */ - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - test_seq = xmlspaceattr_test_alternate; - } - else - test_seq = xmlspaceattr_test; - + test_seq = xmlspaceattr_test; set_expected_seq(test_seq); V_VT(&var) = VT_BSTR; V_BSTR(&var) = _bstr_(xmlspace_attr); @@ -2669,13 +2376,7 @@ static void test_saxreader(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - test_seq = content_handler_test_attributes_alt_no_ns; - } - else - test_seq = content_handler_test_attributes; - + test_seq = content_handler_test_attributes; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2692,13 +2393,7 @@ static void test_saxreader(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - test_seq = content_handler_test_attributes_alt_no_prefix; - } - else - test_seq = content_handler_test_attributes_no_prefix; - + test_seq = content_handler_test_attributes_no_prefix; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2713,13 +2408,7 @@ static void test_saxreader(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - { - test_seq = attribute_norm_alt; - } - else - test_seq = attribute_norm; - + test_seq = attribute_norm; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2764,10 +2453,7 @@ static void test_saxreader_cdata(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); g_reader = reader; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - msxml_version = 4; - else - msxml_version = 0; + msxml_version = 0; hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2787,19 +2473,12 @@ static void test_saxreader_cdata(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = cdata_test_alt; - else - test_seq = cdata_test; - + test_seq = cdata_test; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: cdata test", table->name); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); - else - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); IStream_Release(stream); @@ -2808,19 +2487,12 @@ static void test_saxreader_cdata(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = cdata_test2_alt; - else - test_seq = cdata_test2; - + test_seq = cdata_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: cdata test 2", table->name); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); - else - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); IStream_Release(stream); @@ -2829,11 +2501,7 @@ static void test_saxreader_cdata(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)stream; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = cdata_test3_alt; - else - test_seq = cdata_test3; - + test_seq = cdata_test3; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2871,10 +2539,7 @@ static void test_saxreader_pi(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); g_reader = reader; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - msxml_version = 4; - else - msxml_version = 0; + msxml_version = 0; hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2886,11 +2551,7 @@ static void test_saxreader_pi(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_pi_xml, -1); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = pi_test_v4; - else - test_seq = pi_test; - + test_seq = pi_test; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2927,10 +2588,7 @@ static void test_saxreader_characters(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); g_reader = reader; - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - msxml_version = 4; - else - msxml_version = 0; + msxml_version = 0; hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2942,11 +2600,7 @@ static void test_saxreader_characters(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml, -1); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = chardata_test_v4; - else - test_seq = chardata_test; - + test_seq = chardata_test; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2957,19 +2611,12 @@ static void test_saxreader_characters(void) V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml2, -1); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - test_seq = chardata_test2_v4; - else - test_seq = chardata_test2; - + test_seq = chardata_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); sprintf(seqname, "%s: char data test", table->name); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); - else - ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, FALSE); VariantClear(&var); ISAXXMLReader_Release(reader); @@ -2998,7 +2645,6 @@ static void test_saxreader_properties(void) ISAXXMLReader *reader; HRESULT hr; VARIANT v; - BSTR str; hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); @@ -3164,104 +2810,6 @@ static void test_saxreader_properties(void) IVBSAXXMLReader_Release(vb_reader); - if (!is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data)) - return; - - hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, - &IID_ISAXXMLReader, (void**)&reader); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - /* xmldecl-version property */ - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - - /* stream without declaration */ - V_VT(&v) = VT_BSTR; - V_BSTR(&v) = _bstr_("<element></element>"); - hr = ISAXXMLReader_parse(reader, v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"xmldecl-encoding", &v); - todo_wine - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - todo_wine - ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"charset", &v); - todo_wine - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - todo_wine - ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - - /* stream with declaration */ - V_VT(&v) = VT_BSTR; - V_BSTR(&v) = _bstr_("<?xml version=\"1.0\"?><element></element>"); - hr = ISAXXMLReader_parse(reader, v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - /* VT_BSTR|VT_BYREF input type */ - str = _bstr_("<?xml version=\"1.0\"?><element></element>"); - V_VT(&v) = VT_BSTR|VT_BYREF; - V_BSTRREF(&v) = &str; - hr = ISAXXMLReader_parse(reader, v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - ok(!lstrcmpW(V_BSTR(&v), L"1.0"), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - VariantClear(&v); - - /* Encoding specified */ - V_VT(&v) = VT_BSTR; - V_BSTR(&v) = _bstr_("<?xml version=\"1.0\" encoding=\"uTf-16\"?><element></element>"); - hr = ISAXXMLReader_parse(reader, v); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"xmldecl-encoding", &v); - todo_wine - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - ok(!wcscmp(V_BSTR(&v), L"uTf-16"), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - VariantClear(&v); - } - - V_VT(&v) = VT_EMPTY; - V_BSTR(&v) = (void*)0xdeadbeef; - hr = ISAXXMLReader_getProperty(reader, L"charset", &v); - todo_wine - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine - ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); - todo_wine - ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); - - ISAXXMLReader_Release(reader); free_bstrs(); } @@ -3352,10 +2900,7 @@ static void test_saxreader_max_xml_size(void) V_VT(&var) = VT_I4; V_I4(&var) = 4194304; hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); - if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - else - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); V_VT(&var) = VT_I4; V_I4(&var) = 4194305; @@ -3524,28 +3069,6 @@ static void test_saxreader_normalize_line_breaks(void) VARIANT var; HRESULT hr; - if (is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data)) - { - hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - v = 123; - hr = ISAXXMLReader_getFeature(reader, L"normalize-line-breaks", &v); - todo_wine - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - ok(v == 123, "Unexpected value %d.\n", v); - - hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_FALSE); - todo_wine - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_TRUE); - todo_wine - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - ISAXXMLReader_Release(reader); - } - while (table->clsid) { if (!is_clsid_supported(table->clsid, reader_support_data)) @@ -3680,7 +3203,6 @@ struct feature_ns_entry_t { static const struct feature_ns_entry_t feature_ns_entry_data[] = { { &CLSID_SAXXMLReader, "CLSID_SAXXMLReader", VARIANT_TRUE, VARIANT_FALSE }, { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE }, - { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE }, { 0 } }; @@ -3710,34 +3232,15 @@ static void test_saxreader_features(void) continue; } - if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40)) - { - value = VARIANT_TRUE; - hr = ISAXXMLReader_getFeature(reader, L"exhaustive-errors", &value); - ok(hr == S_OK, "Failed to get feature value, hr %#lx.\n", hr); - ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); - hr = ISAXXMLReader_putFeature(reader, L"exhaustive-errors", VARIANT_FALSE); - ok(hr == S_OK, "Failed to put feature value, hr %#lx.\n", hr); - - value = VARIANT_TRUE; - hr = ISAXXMLReader_getFeature(reader, L"schema-validation", &value); - ok(hr == S_OK, "Failed to get feature value, hr %#lx.\n", hr); - ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); - hr = ISAXXMLReader_putFeature(reader, L"exhaustive-errors", VARIANT_FALSE); - ok(hr == S_OK, "Failed to put feature value, hr %#lx.\n", hr); - } - else - { - value = 123; - hr = ISAXXMLReader_getFeature(reader, L"exhaustive-errors", &value); - ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#lx.\n", hr); - ok(value == 123, "Unexpected value %d.\n", value); - - value = 123; - hr = ISAXXMLReader_getFeature(reader, L"schema-validation", &value); - ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#lx.\n", hr); - ok(value == 123, "Unexpected value %d.\n", value); - } + value = 123; + hr = ISAXXMLReader_getFeature(reader, L"exhaustive-errors", &value); + ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#lx.\n", hr); + ok(value == 123, "Unexpected value %d.\n", value); + + value = 123; + hr = ISAXXMLReader_getFeature(reader, L"schema-validation", &value); + ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#lx.\n", hr); + ok(value == 123, "Unexpected value %d.\n", value); name = feature_names; while (*name) @@ -3795,7 +3298,6 @@ static const struct enc_test_entry_t encoding_test_data[] = { { &CLSID_SAXXMLReader, 0xc00ce56f, TRUE }, { &CLSID_SAXXMLReader30, 0xc00ce56f, TRUE }, - { &CLSID_SAXXMLReader40, S_OK }, { 0 } }; @@ -3906,21 +3408,13 @@ static void test_saxreader_encoding(void) /* UCS-4 LE */ create_test_file(testXmlA, ucs4_le_test, sizeof(ucs4_le_test)); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); - if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40)) - todo_wine - ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); - else - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); DeleteFileA(testXmlA); /* UCS-4 BE */ create_test_file(testXmlA, ucs4_be_test, sizeof(ucs4_be_test)); hr = ISAXXMLReader_parseURL(reader, L"test.xml"); - if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40)) - todo_wine - ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); - else - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); DeleteFileA(testXmlA); /* try BSTR input with no BOM or '<?xml' instruction */ @@ -4025,7 +3519,6 @@ static struct msxmlsupported_data_t mxwriter_support_data[] = { { &CLSID_MXXMLWriter, "MXXMLWriter" }, { &CLSID_MXXMLWriter30, "MXXMLWriter30" }, - { &CLSID_MXXMLWriter40, "MXXMLWriter40" }, { NULL } }; @@ -4033,7 +3526,6 @@ static struct msxmlsupported_data_t mxattributes_support_data[] = { { &CLSID_SAXAttributes, "SAXAttributes" }, { &CLSID_SAXAttributes30, "SAXAttributes30" }, - { &CLSID_SAXAttributes40, "SAXAttributes40" }, { NULL } }; @@ -4052,7 +3544,6 @@ static const struct mxwriter_props_t mxwriter_default_props[] = { { &CLSID_MXXMLWriter, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, L"UTF-16" }, { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, L"UTF-16" }, - { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, L"UTF-16" }, { NULL } }; @@ -4567,78 +4058,58 @@ static const struct writer_startendelement_t writer_startendelement[] = { { &CLSID_MXXMLWriter, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, StartElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, StartElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, StartElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, StartElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, StartElement, L"uri", L"local", L"qname", L"<qname>", S_OK }, { &CLSID_MXXMLWriter30, StartElement, L"uri", L"local", L"qname", L"<qname>", S_OK }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"qname", L"<qname>", S_OK }, { &CLSID_MXXMLWriter, StartElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, StartElement, L"uri", L"local", L"uri:local", L"<uri:local>", S_OK }, { &CLSID_MXXMLWriter30, StartElement, L"uri", L"local", L"uri:local", L"<uri:local>", S_OK }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local", L"<uri:local>", S_OK }, { &CLSID_MXXMLWriter, StartElement, L"uri", L"local", L"uri:local2", L"<uri:local2>", S_OK }, { &CLSID_MXXMLWriter30, StartElement, L"uri", L"local", L"uri:local2", L"<uri:local2>", S_OK }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local2", L"<uri:local2>", S_OK }, /* endElement tests */ { &CLSID_MXXMLWriter, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, EndElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, EndElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, EndElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, EndElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, EndElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, EndElement, L"uri", L"local", L"qname", L"</qname>", S_OK }, { &CLSID_MXXMLWriter30, EndElement, L"uri", L"local", L"qname", L"</qname>", S_OK }, - { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"qname", L"</qname>", S_OK }, { &CLSID_MXXMLWriter, EndElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, - { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter, EndElement, L"uri", L"local", L"uri:local", L"</uri:local>", S_OK }, { &CLSID_MXXMLWriter30, EndElement, L"uri", L"local", L"uri:local", L"</uri:local>", S_OK }, - { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"uri:local", L"</uri:local>", S_OK }, { &CLSID_MXXMLWriter, EndElement, L"uri", L"local", L"uri:local2", L"</uri:local2>", S_OK }, { &CLSID_MXXMLWriter30, EndElement, L"uri", L"local", L"uri:local2", L"</uri:local2>", S_OK }, - { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"uri:local2", L"</uri:local2>", S_OK }, /* with attributes */ { &CLSID_MXXMLWriter, StartElement, L"uri", L"local", L"uri:local", startelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter30, StartElement, L"uri", L"local", L"uri:local", startelement_xml, S_OK, &saxattributes }, - { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local", startelement_xml, S_OK, &saxattributes }, /* empty elements */ { &CLSID_MXXMLWriter, StartEndElement, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter30, StartEndElement, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, - { &CLSID_MXXMLWriter40, StartEndElement, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter, StartEndElement, L"", L"", L"", L"</>", S_OK }, { &CLSID_MXXMLWriter30, StartEndElement, L"", L"", L"", L"</>", S_OK }, - { &CLSID_MXXMLWriter40, StartEndElement, L"", L"", L"", L"</>", S_OK }, /* with disabled output escaping */ { &CLSID_MXXMLWriter, StartEndElement | DisableEscaping, L"uri", L"local", L"uri:local", startendelement_noescape_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, L"uri", L"local", L"uri:local", startendelement_noescape_xml, S_OK, &saxattributes }, - { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, { NULL } }; @@ -4750,11 +4221,9 @@ struct writer_startendelement2_t { static const struct writer_startendelement2_t writer_startendelement2[] = { { &CLSID_MXXMLWriter, L"a", -1, L"b", -1, L"<a/>", S_OK }, { &CLSID_MXXMLWriter30, L"a", -1, L"b", -1, L"<a/>", S_OK }, - { &CLSID_MXXMLWriter40, L"a", -1, L"b", -1, L"<a/>", S_OK }, { &CLSID_MXXMLWriter, L"a", 1, L"b", 1, L"<a/>", S_OK }, { &CLSID_MXXMLWriter30, L"a", 1, L"b", 1, L"<a/>", S_OK }, - { &CLSID_MXXMLWriter40, L"a", 1, L"b", 1, L"<a/>", S_OK }, { NULL } }; @@ -5031,7 +4500,6 @@ static const struct writer_characters_t writer_characters[] = { { &CLSID_MXXMLWriter, L"< > & \" \'", L"< > & \" \'" }, { &CLSID_MXXMLWriter30, L"< > & \" \'", L"< > & \" \'" }, - { &CLSID_MXXMLWriter40, L"< > & \" \'", L"< > & \" \'" }, { NULL } }; @@ -6281,19 +5749,15 @@ typedef struct { static const addattribute_test_t addattribute_data[] = { { &CLSID_SAXAttributes, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, - { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, - { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, - { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", "type", "value", S_OK }, { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK }, - { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK }, { NULL } }; @@ -6325,13 +5789,8 @@ static void test_mxattr_addAttribute(void) hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - /* SAXAttributes40 crash on this test */ - if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || - IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) - { - hr = ISAXAttributes_getLength(saxattr, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - } + hr = ISAXAttributes_getLength(saxattr, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); len = -1; hr = ISAXAttributes_getLength(saxattr, &len); @@ -6368,28 +5827,23 @@ static void test_mxattr_addAttribute(void) if (hr == S_OK) { - /* SAXAttributes40 crash on this test */ - if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || - IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) - { - hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - } + hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); len = -1; hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); @@ -6447,63 +5901,40 @@ static void test_mxattr_addAttribute(void) ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); ok(index == -1, "%d: got wrong index %d\n", i, index); - if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40)) - { - hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); - ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); - } - else - { - hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - /* versions 4 and 6 crash */ - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + /* versions 4 and 6 crash */ + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local), - strlen(table->local), NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - } + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local), + strlen(table->local), NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -6654,7 +6085,6 @@ static struct msxmlsupported_data_t saxattr_support_data[] = { { &CLSID_SAXAttributes, "SAXAttributes" }, { &CLSID_SAXAttributes30, "SAXAttributes30" }, - { &CLSID_SAXAttributes40, "SAXAttributes40" }, { NULL } }; diff --git a/dlls/msxml4/tests/Makefile.in b/dlls/msxml4/tests/Makefile.in index 4b790466411..5c92b1fbe5c 100644 --- a/dlls/msxml4/tests/Makefile.in +++ b/dlls/msxml4/tests/Makefile.in @@ -3,4 +3,5 @@ IMPORTS = oleaut32 ole32 SOURCES = \ domdoc.c \ + saxreader.c \ schema.c diff --git a/dlls/msxml4/tests/saxreader.c b/dlls/msxml4/tests/saxreader.c new file mode 100644 index 00000000000..0d9a199f029 --- /dev/null +++ b/dlls/msxml4/tests/saxreader.c @@ -0,0 +1,5802 @@ +/* + * SAXReader/MXWriter tests + * + * Copyright 2008 Piotr Caban + * Copyright 2011 Thomas Mullaly + * Copyright 2012 Nikolay Sivov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#define CONST_VTABLE + +#include <stdio.h> +#include <assert.h> + +#include "windows.h" +#include "ole2.h" +#include "msxml2.h" +#include "msxml2did.h" +#include "ocidl.h" +#include "initguid.h" +#include "dispex.h" + +#include "wine/test.h" + +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + +struct sink +{ + IVBSAXContentHandler IVBSAXContentHandler_iface; + LONG refcount; +}; + +static struct sink *impl_from_IVBSAXContentHandler(IVBSAXContentHandler *iface) +{ + return CONTAINING_RECORD(iface, struct sink, IVBSAXContentHandler_iface); +} + +static HRESULT WINAPI vb_content_handler_QueryInterface(IVBSAXContentHandler *iface, + REFIID riid, void **obj) +{ + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IDispatch) + || IsEqualGUID(riid, &IID_IVBSAXContentHandler)) + { + *obj = iface; + IVBSAXContentHandler_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI vb_content_handler_AddRef(IVBSAXContentHandler *iface) +{ + struct sink *sink = impl_from_IVBSAXContentHandler(iface); + return InterlockedIncrement(&sink->refcount); +} + +static ULONG WINAPI vb_content_handler_Release(IVBSAXContentHandler *iface) +{ + struct sink *sink = impl_from_IVBSAXContentHandler(iface); + LONG refcount = InterlockedDecrement(&sink->refcount); + + if (!refcount) + free(sink); + + return refcount; +} + +static HRESULT WINAPI vb_content_handler_GetTypeInfoCount(IVBSAXContentHandler *iface, UINT *count) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI vb_content_handler_GetTypeInfo(IVBSAXContentHandler *iface, UINT index, + LCID lcid, ITypeInfo **ti) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI vb_content_handler_GetIDsOfNames(IVBSAXContentHandler *iface, REFIID riid, + LPOLESTR *names, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI vb_content_handler_Invoke(IVBSAXContentHandler *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI vb_content_handler_putref_documentLocator(IVBSAXContentHandler *iface, IVBSAXLocator *locator) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_startDocument(IVBSAXContentHandler *iface) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_endDocument(IVBSAXContentHandler *iface) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_startPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix, BSTR *uri) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_endPrefixMapping(IVBSAXContentHandler *iface, BSTR *prefix) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_startElement(IVBSAXContentHandler *iface, BSTR *uri, BSTR *localName, + BSTR *qname, IVBSAXAttributes * oAttributes) +{ + ok(uri != NULL && *uri != NULL, "Unexpected pointer.\n"); + ok(localName != NULL && *localName != NULL, "Unexpected pointer.\n"); + ok(qname != NULL && *qname != NULL, "Unexpected pointer.\n"); + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_endElement(IVBSAXContentHandler *iface, BSTR *uri, BSTR *localName, + BSTR * strQName) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_characters(IVBSAXContentHandler *iface, BSTR * chars) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_ignorableWhitespace(IVBSAXContentHandler *iface, BSTR * chars) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_processingInstruction(IVBSAXContentHandler *iface, BSTR * target, BSTR * data) +{ + return S_OK; +} + +static HRESULT WINAPI vb_content_handler_skippedEntity(IVBSAXContentHandler *iface, BSTR * name) +{ + return S_OK; +} + +static const IVBSAXContentHandlerVtbl vbcontenthandlervtbl = +{ + vb_content_handler_QueryInterface, + vb_content_handler_AddRef, + vb_content_handler_Release, + vb_content_handler_GetTypeInfoCount, + vb_content_handler_GetTypeInfo, + vb_content_handler_GetIDsOfNames, + vb_content_handler_Invoke, + vb_content_handler_putref_documentLocator, + vb_content_handler_startDocument, + vb_content_handler_endDocument, + vb_content_handler_startPrefixMapping, + vb_content_handler_endPrefixMapping, + vb_content_handler_startElement, + vb_content_handler_endElement, + vb_content_handler_characters, + vb_content_handler_ignorableWhitespace, + vb_content_handler_processingInstruction, + vb_content_handler_skippedEntity, +}; + +static struct sink *create_test_sink(void) +{ + struct sink *sink = calloc(1, sizeof(*sink)); + + sink->IVBSAXContentHandler_iface.lpVtbl = &vbcontenthandlervtbl; + sink->refcount = 1; + + return sink; +} + +#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) +static void _expect_ref(IUnknown* obj, ULONG ref, int line) +{ + ULONG rc; + IUnknown_AddRef(obj); + rc = IUnknown_Release(obj); + ok_(__FILE__, line)(rc == ref, "expected refcount %ld, got %ld.\n", ref, rc); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static LONG get_refcount(void *iface) +{ + IUnknown *unk = iface; + LONG ref; + + ref = IUnknown_AddRef(unk); + IUnknown_Release(unk); + return ref-1; +} + +struct msxmlsupported_data_t +{ + const GUID *clsid; + const char *name; + BOOL supported; +}; + +static BSTR alloc_str_from_narrow(const char *str) +{ + int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + BSTR ret = SysAllocStringLen(NULL, len - 1); /* NUL character added automatically */ + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + return ret; +} + +static BSTR alloced_bstrs[512]; +static int alloced_bstrs_count; + +static BSTR _bstr_(const char *str) +{ + assert(alloced_bstrs_count < ARRAY_SIZE(alloced_bstrs)); + alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str); + return alloced_bstrs[alloced_bstrs_count++]; +} + +static void free_bstrs(void) +{ + int i; + for (i = 0; i < alloced_bstrs_count; i++) + SysFreeString(alloced_bstrs[i]); + alloced_bstrs_count = 0; +} + +static void test_saxstr(const char *file, unsigned line, BSTR str, const WCHAR *expected, BOOL todo, int *failcount) +{ + int len, lenexp, cmp; + + len = SysStringLen(str); + + if (!expected) { + if (str && todo) + { + (*failcount)++; + todo_wine + ok_(file, line) (!str, "got %p, expected null str\n", str); + } + else + ok_(file, line) (!str, "got %p, expected null str\n", str); + + if (len && todo) + { + (*failcount)++; + todo_wine + ok_(file, line) (len == 0, "got len %d, expected 0\n", len); + } + else + ok_(file, line) (len == 0, "got len %d, expected 0\n", len); + return; + } + + lenexp = lstrlenW(expected); + if (lenexp != len && todo) + { + (*failcount)++; + todo_wine + ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), + lenexp, wine_dbgstr_w(expected)); + } + else + ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), + lenexp, wine_dbgstr_w(expected)); + + /* exit earlier on length mismatch */ + if (lenexp != len) return; + + cmp = lstrcmpW(str, expected); + if (cmp && todo) + { + (*failcount)++; + todo_wine + ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", wine_dbgstr_wn(str, len), wine_dbgstr_w(expected)); + } + else + ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", wine_dbgstr_wn(str, len), wine_dbgstr_w(expected)); +} + +typedef enum _CH { + CH_ENDTEST, + CH_PUTDOCUMENTLOCATOR, + CH_STARTDOCUMENT, + CH_ENDDOCUMENT, + CH_STARTPREFIXMAPPING, + CH_ENDPREFIXMAPPING, + CH_STARTELEMENT, + CH_ENDELEMENT, + CH_CHARACTERS, + CH_IGNORABLEWHITESPACE, + CH_PROCESSINGINSTRUCTION, + CH_SKIPPEDENTITY, + LH_STARTCDATA, + LH_ENDCDATA, + LH_COMMENT, + EH_ERROR, + EH_FATALERROR, + EH_IGNORABLEWARNING, + EVENT_LAST +} CH; + +static const char *event_names[EVENT_LAST] = { + "endtest", + "putDocumentLocator", + "startDocument", + "endDocument", + "startPrefixMapping", + "endPrefixMapping", + "startElement", + "endElement", + "characters", + "ignorableWhitespace", + "processingInstruction", + "skippedEntity", + "startCDATA", + "endCDATA", + "comment", + "error", + "fatalError", + "ignorableWarning" +}; + +struct attribute_entry +{ + const WCHAR *uri; + const WCHAR *local; + const WCHAR *qname; + const WCHAR *value; + + /* used for actual call data only, null for expected call data */ + BSTR uriW; + BSTR localW; + BSTR qnameW; + BSTR valueW; +}; + +struct call_entry +{ + CH id; + int line; + int column; + HRESULT ret; + const WCHAR *arg1; + const WCHAR *arg2; + const WCHAR *arg3; + + /* allocated once at startElement callback */ + struct attribute_entry *attributes; + int attr_count; + + /* used for actual call data only, null for expected call data */ + BSTR arg1W; + BSTR arg2W; + BSTR arg3W; +}; + +struct call_sequence +{ + int count; + int size; + struct call_entry *sequence; +}; + +#define CONTENT_HANDLER_INDEX 0 +#define NUM_CALL_SEQUENCES 1 +static struct call_sequence *sequences[NUM_CALL_SEQUENCES]; + +static void init_call_entry(ISAXLocator *locator, struct call_entry *call) +{ + memset(call, 0, sizeof(*call)); + ISAXLocator_getLineNumber(locator, &call->line); + ISAXLocator_getColumnNumber(locator, &call->column); +} + +static void add_call(struct call_sequence **seq, int sequence_index, + const struct call_entry *call) +{ + struct call_sequence *call_seq = seq[sequence_index]; + + if (!call_seq->sequence) + { + call_seq->size = 10; + call_seq->sequence = malloc(call_seq->size * sizeof (struct call_entry)); + } + + if (call_seq->count == call_seq->size) + { + call_seq->size *= 2; + call_seq->sequence = realloc(call_seq->sequence, call_seq->size * sizeof (struct call_entry)); + } + + assert(call_seq->sequence); + + call_seq->sequence[call_seq->count].id = call->id; + call_seq->sequence[call_seq->count].line = call->line; + call_seq->sequence[call_seq->count].column = call->column; + call_seq->sequence[call_seq->count].arg1W = call->arg1W; + call_seq->sequence[call_seq->count].arg2W = call->arg2W; + call_seq->sequence[call_seq->count].arg3W = call->arg3W; + call_seq->sequence[call_seq->count].ret = call->ret; + call_seq->sequence[call_seq->count].attr_count = call->attr_count; + call_seq->sequence[call_seq->count].attributes = call->attributes; + + call_seq->count++; +} + +static inline void flush_sequence(struct call_sequence **seg, int sequence_index) +{ + int i; + + struct call_sequence *call_seq = seg[sequence_index]; + + for (i = 0; i < call_seq->count; i++) + { + int j; + + for (j = 0; j < call_seq->sequence[i].attr_count; j++) + { + SysFreeString(call_seq->sequence[i].attributes[j].uriW); + SysFreeString(call_seq->sequence[i].attributes[j].localW); + SysFreeString(call_seq->sequence[i].attributes[j].qnameW); + SysFreeString(call_seq->sequence[i].attributes[j].valueW); + } + free(call_seq->sequence[i].attributes); + call_seq->sequence[i].attr_count = 0; + + SysFreeString(call_seq->sequence[i].arg1W); + SysFreeString(call_seq->sequence[i].arg2W); + SysFreeString(call_seq->sequence[i].arg3W); + } + + free(call_seq->sequence); + call_seq->sequence = NULL; + call_seq->count = call_seq->size = 0; +} + +static const char *get_event_name(CH event) +{ + return event_names[event]; +} + +static void compare_attributes(const struct call_entry *actual, const struct call_entry *expected, const char *context, + BOOL todo, const char *file, int line, int *failcount) +{ + int i, lenexp = 0; + + /* attribute count is not stored for expected data */ + if (expected->attributes) + { + struct attribute_entry *ptr = expected->attributes; + while (ptr->uri) { lenexp++; ptr++; }; + } + + /* check count first and exit earlier */ + if (actual->attr_count != lenexp && todo) + { + (*failcount)++; + todo_wine + ok_(file, line) (FALSE, "%s: in event %s expecting attr count %d got %d\n", + context, get_event_name(actual->id), lenexp, actual->attr_count); + } + else + ok_(file, line) (actual->attr_count == lenexp, "%s: in event %s expecting attr count %d got %d\n", + context, get_event_name(actual->id), lenexp, actual->attr_count); + + if (actual->attr_count != lenexp) return; + + /* now compare all attributes strings */ + for (i = 0; i < actual->attr_count; i++) + { + test_saxstr(file, line, actual->attributes[i].uriW, expected->attributes[i].uri, todo, failcount); + test_saxstr(file, line, actual->attributes[i].localW, expected->attributes[i].local, todo, failcount); + test_saxstr(file, line, actual->attributes[i].qnameW, expected->attributes[i].qname, todo, failcount); + test_saxstr(file, line, actual->attributes[i].valueW, expected->attributes[i].value, todo, failcount); + } +} + +static void ok_sequence_(struct call_sequence **seq, int sequence_index, + const struct call_entry *expected, const char *context, BOOL todo, + const char *file, int line) +{ + struct call_sequence *call_seq = seq[sequence_index]; + static const struct call_entry end_of_sequence = { CH_ENDTEST }; + const struct call_entry *actual, *sequence; + int failcount = 0; + + add_call(seq, sequence_index, &end_of_sequence); + + sequence = call_seq->sequence; + actual = sequence; + + while (expected->id != CH_ENDTEST && actual->id != CH_ENDTEST) + { + if (expected->id == actual->id) + { + if (expected->line != -1) + { + /* always test position data */ + if (expected->line != actual->line && todo) + { + todo_wine + { + failcount++; + ok_(file, line) (FALSE, + "%s: in event %s expecting line %d got %d\n", + context, get_event_name(actual->id), expected->line, actual->line); + } + } + else + { + ok_(file, line) (expected->line == actual->line, + "%s: in event %s expecting line %d got %d\n", + context, get_event_name(actual->id), expected->line, actual->line); + } + } + + + if (expected->column != -1) + { + if (expected->column != actual->column && todo) + { + todo_wine + { + failcount++; + ok_(file, line) (FALSE, + "%s: in event %s expecting column %d got %d\n", + context, get_event_name(actual->id), expected->column, actual->column); + } + } + else + { + ok_(file, line) (expected->column == actual->column, + "%s: in event %s expecting column %d got %d\n", + context, get_event_name(actual->id), expected->column, actual->column); + } + } + + switch (actual->id) + { + case CH_PUTDOCUMENTLOCATOR: + case CH_STARTDOCUMENT: + case CH_ENDDOCUMENT: + case LH_STARTCDATA: + case LH_ENDCDATA: + break; + case CH_STARTPREFIXMAPPING: + /* prefix, uri */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); + break; + case CH_ENDPREFIXMAPPING: + /* prefix */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + break; + case CH_STARTELEMENT: + /* compare attributes */ + compare_attributes(actual, expected, context, todo, file, line, &failcount); + /* fallthrough */ + case CH_ENDELEMENT: + /* uri, localname, qname */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); + test_saxstr(file, line, actual->arg3W, expected->arg3, todo, &failcount); + break; + case CH_CHARACTERS: + case CH_IGNORABLEWHITESPACE: + case LH_COMMENT: + /* char data */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + break; + case CH_PROCESSINGINSTRUCTION: + /* target, data */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); + break; + case CH_SKIPPEDENTITY: + /* name */ + test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); + break; + case EH_FATALERROR: + /* test return value only */ + if (expected->ret != actual->ret && todo) + { + failcount++; + ok_(file, line) (FALSE, + "%s: in event %s expecting ret %#lx got %#lx\n", + context, get_event_name(actual->id), expected->ret, actual->ret); + } + else + ok_(file, line) (expected->ret == actual->ret, + "%s: in event %s expecting ret %#lx got %#lx\n", + context, get_event_name(actual->id), expected->ret, actual->ret); + break; + case EH_ERROR: + case EH_IGNORABLEWARNING: + default: + ok(0, "%s: callback not handled, %s\n", context, get_event_name(actual->id)); + } + expected++; + actual++; + } + else if (todo) + { + failcount++; + todo_wine + { + ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n", + context, get_event_name(expected->id), get_event_name(actual->id)); + } + + flush_sequence(seq, sequence_index); + return; + } + else + { + ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n", + context, get_event_name(expected->id), get_event_name(actual->id)); + expected++; + actual++; + } + } + + if (todo) + { + todo_wine + { + if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST) + { + failcount++; + ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n", + context, get_event_name(expected->id), get_event_name(actual->id)); + } + } + } + else if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST) + { + ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n", + context, get_event_name(expected->id), get_event_name(actual->id)); + } + + if (todo && !failcount) /* succeeded yet marked todo */ + { + todo_wine + { + ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context); + } + } + + flush_sequence(seq, sequence_index); +} + +#define ok_sequence(seq, index, exp, contx, todo) \ + ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) + +static void init_call_sequences(struct call_sequence **seq, int n) +{ + int i; + + for (i = 0; i < n; i++) + seq[i] = calloc(1, sizeof(**seq)); +} + +static const WCHAR szSimpleXML[] = + +L"<?xml version=\"1.0\" ?>\n" +"<BankAccount>\n" +" <Number>1234</Number>\n" +" <Name>Captain Ahab</Name>\n" +"</BankAccount>\n"; + +static const WCHAR carriage_ret_test[] = + +L"<?xml version=\"1.0\"?>\r\n" +"<BankAccount>\r\n\t<Number>1234</Number>\r\n\t" +"<Name>Captain Ahab</Name>\r\n" +"</BankAccount>\r\n"; + +static const char testXML[] = +"<?xml version=\"1.0\" ?>\n" +"<BankAccount>\n" +" <Number>1234</Number>\n" +" <Name>Captain Ahab</Name>\n" +"</BankAccount>\n"; + +static const char test_attributes[] = +"<?xml version=\"1.0\" ?>\n" +"<document xmlns:test=\"prefix_test\" xmlns=\"prefix\" test:arg1=\"arg1\" arg2=\"arg2\" test:ar3=\"arg3\">\n" +"<node1 xmlns:p=\"test\" />" +"</document>\n"; + +static const char test_cdata_xml[] = +"<?xml version=\"1.0\" ?>" +"<a><![CDATA[Some \r\ntext\n\r\ndata\n\n]]></a>"; + +static const char test2_cdata_xml[] = +"<?xml version=\"1.0\" ?>" +"<a><![CDATA[\n\r\nSome \r\ntext\n\r\ndata\n\n]]></a>"; + +static const char test3_cdata_xml[] = +"<?xml version=\"1.0\" ?><a><![CDATA[Some text data]]></a>"; + +static const char test_pi_xml[] = +"<?xml version=\"1.0\" ?><a><?t some text ?></a>"; + +static const char test_chardata_xml[] = +"<?xml version=\"1.0\" ?><a>\nabc<b>de\nf</b>gh\n</a>"; + +static const char test_chardata_xml2[] = +"<?xml version=\"1.0\" ?><a>\rabc<b>de\rf</b>gh\r</a>"; + +static struct call_entry content_handler_test1_alternate[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 4, S_OK, L"\n " }, + { CH_STARTELEMENT, 3, 11, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 16, S_OK, L"1234" }, + { CH_ENDELEMENT, 3, 24, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 4, S_OK, L"\n " }, + { CH_STARTELEMENT, 4, 9, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 22, S_OK, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 28, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 1, S_OK, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_ENDDOCUMENT, 6, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry content_handler_test2_alternate[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 21, S_OK }, + { CH_STARTELEMENT, 2, 13, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 2, S_OK, L"\t" }, + { CH_STARTELEMENT, 3, 9, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 14, S_OK, L"1234" }, + { CH_ENDELEMENT, 3, 22, S_OK, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 4, 2, S_OK, L"\t" }, + { CH_STARTELEMENT, 4, 7, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 20, S_OK, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 26, S_OK, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 0, S_OK, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_OK, L"", L"BankAccount", L"BankAccount" }, + { CH_ENDDOCUMENT, 6, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry content_handler_testerror_alternate[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL }, + { EH_FATALERROR, 1, 0, E_FAIL }, + { CH_ENDTEST } +}; + +static struct call_entry content_handler_test_callback_rets_alt[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE }, + { CH_STARTDOCUMENT, 1, 22, S_FALSE }, + { CH_STARTELEMENT, 2, 13, S_FALSE, L"", L"BankAccount", L"BankAccount" }, + { CH_CHARACTERS, 3, 4, S_FALSE, L"\n " }, + { CH_STARTELEMENT, 3, 11, S_FALSE, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 3, 16, S_FALSE, L"1234" }, + { CH_ENDELEMENT, 3, 24, S_FALSE, L"", L"Number", L"Number" }, + { CH_CHARACTERS, 4, 4, S_FALSE, L"\n " }, + { CH_STARTELEMENT, 4, 9, S_FALSE, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 4, 22, S_FALSE, L"Captain Ahab" }, + { CH_ENDELEMENT, 4, 28, S_FALSE, L"", L"Name", L"Name" }, + { CH_CHARACTERS, 5, 1, S_FALSE, L"\n" }, + { CH_ENDELEMENT, 5, 14, S_FALSE, L"", L"BankAccount", L"BankAccount" }, + { CH_ENDDOCUMENT, 6, 0, S_FALSE }, + { CH_ENDTEST } +}; + +static struct attribute_entry ch_attributes2[] = { + { L"", L"", L"xmlns:p", L"test" }, + { NULL } +}; + +static struct attribute_entry ch_attributes_alt_4[] = +{ + { L"prefix_test", L"arg1", L"test:arg1", L"arg1" }, + { L"", L"arg2", L"arg2", L"arg2" }, + { L"prefix_test", L"ar3", L"test:ar3", L"arg3" }, + { L"", L"", L"xmlns:test", L"prefix_test" }, + { L"", L"", L"xmlns", L"prefix" }, + { NULL } +}; + +static struct call_entry content_handler_test_attributes_alternate_4[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, + { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_alt_4 }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", ch_attributes2 }, + { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, + { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, + { CH_ENDDOCUMENT, 4, 0, S_OK }, + { CH_ENDTEST } +}; + +/* 'namespace' feature switched off */ +static struct attribute_entry ch_attributes_alt_no_ns[] = { + { L"", L"", L"xmlns:test", L"prefix_test" }, + { L"", L"", L"xmlns", L"prefix" }, + { L"", L"", L"test:arg1", L"arg1" }, + { L"", L"", L"arg2", L"arg2" }, + { L"", L"", L"test:ar3", L"arg3" }, + { NULL } +}; + +static struct call_entry content_handler_test_attributes_alt_no_ns[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 2, 95, S_OK, L"", L"", L"document", ch_attributes_alt_no_ns }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"", L"", L"node1", ch_attributes2 }, + { CH_ENDELEMENT, 3, 24, S_OK, L"", L"", L"node1" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"", L"", L"document" }, + { CH_ENDDOCUMENT, 4, 0, S_OK }, + { CH_ENDTEST } +}; + +/* 'namespaces' is on, 'namespace-prefixes' if off */ +static struct attribute_entry ch_attributes_no_prefix[] = +{ + { L"prefix_test", L"arg1", L"test:arg1", L"arg1" }, + { L"", L"arg2", L"arg2", L"arg2" }, + { L"prefix_test", L"ar3", L"test:ar3", L"arg3" }, + { NULL } +}; + +static struct call_entry content_handler_test_attributes_alt_no_prefix[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"test", L"prefix_test" }, + { CH_STARTPREFIXMAPPING, 2, 95, S_OK, L"", L"prefix" }, + { CH_STARTELEMENT, 2, 95, S_OK, L"prefix", L"document", L"document", ch_attributes_no_prefix }, + { CH_CHARACTERS, 3, 1, S_OK, L"\n" }, + { CH_STARTPREFIXMAPPING, 3, 24, S_OK, L"p", L"test" }, + { CH_STARTELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1", NULL }, + { CH_ENDELEMENT, 3, 24, S_OK, L"prefix", L"node1", L"node1" }, + { CH_ENDPREFIXMAPPING, 3, 24, S_OK, L"p" }, + { CH_ENDELEMENT, 3, 35, S_OK, L"prefix", L"document", L"document" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"test" }, + { CH_ENDPREFIXMAPPING, 3, 35, S_OK, L"" }, + { CH_ENDDOCUMENT, 4, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct attribute_entry xmlspace_attrs[] = +{ + { L"http://www.w3.org/XML/1998/namespace", L"space", L"xml:space", L"preserve" }, + { NULL } +}; + +static struct call_entry xmlspaceattr_test_alternate[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 39, S_OK }, + { CH_STARTELEMENT, 1, 63, S_OK, L"", L"a", L"a", xmlspace_attrs }, + { CH_CHARACTERS, 1, 80, S_OK, L" Some text data " }, + { CH_ENDELEMENT, 1, 83, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 83, S_OK }, + { CH_ENDTEST } +}; + +/* attribute value normalization test */ +static const char attribute_normalize[] = + "<?xml version=\"1.0\" ?>\n" + "<a attr1=\" \r \n \tattr_value A & &\t \r \n\r\n \n\"/>\n"; + +static struct attribute_entry attribute_norm_attrs[] = +{ + { L"", L"attr1", L"attr1", L" attr_value A & & " }, + { NULL } +}; + +static struct call_entry attribute_norm_alt[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 8, 3, S_OK, L"", L"a", L"a", attribute_norm_attrs }, + { CH_ENDELEMENT, 8, 3, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 9, 0, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry pi_test_v4[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_PROCESSINGINSTRUCTION, 1, 41, S_OK, L"t", L"some text " }, + { CH_ENDELEMENT, 1, 45, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 45, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test_v4[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 4, S_OK, L"\nabc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 2, S_OK, L"de\nf" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 4, 1, S_OK, L"gh\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry chardata_test2_v4[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 2, 4, S_OK, L"abc" }, + { CH_STARTELEMENT, 2, 6, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 2, 9, S_OK, L"de" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 2, S_OK, L"f" }, + { CH_ENDELEMENT, 3, 5, S_OK, L"", L"b", L"b" }, + { CH_CHARACTERS, 3, 8, S_OK, L"gh" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_ENDELEMENT, 4, 4, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 4, 4, S_OK }, + { CH_ENDTEST } +}; + +/* this is what MSXML6 does */ +static struct call_entry cdata_test_alt[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { LH_STARTCDATA, 1, 34, S_OK }, + { CH_CHARACTERS, 1, 40, S_OK, L"Some " }, + { CH_CHARACTERS, 2, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 1, S_OK, L"text\n" }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 6, 3, S_OK, L"data\n\n" }, + { LH_ENDCDATA, 6, 3, S_OK }, + { CH_ENDELEMENT, 6, 7, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 6, 7, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry cdata_test2_alt[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { LH_STARTCDATA, 1, 34, S_OK }, + { CH_CHARACTERS, 2, 1, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 3, 6, S_OK, L"Some " }, + { CH_CHARACTERS, 4, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 5, 1, S_OK, L"text\n" }, + { CH_CHARACTERS, 6, 0, S_OK, L"\n" }, + { CH_CHARACTERS, 8, 3, S_OK, L"data\n\n" }, + { LH_ENDCDATA, 8, 3, S_OK }, + { CH_ENDELEMENT, 8, 7, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 8, 7, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry cdata_test3_alt[] = { + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 22, S_OK }, + { CH_STARTELEMENT, 1, 25, S_OK, L"", L"a", L"a" }, + { LH_STARTCDATA, 1, 34, S_OK }, + { CH_CHARACTERS, 1, 51, S_OK, L"Some text data" }, + { LH_ENDCDATA, 1, 51, S_OK }, + { CH_ENDELEMENT, 1, 55, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 55, S_OK }, + { CH_ENDTEST } +}; + +static struct attribute_entry read_test_attrs[] = +{ + { L"", L"attr", L"attr", L"val" }, + { NULL } +}; + +static struct call_entry read_test_seq[] = { + { CH_PUTDOCUMENTLOCATOR, -1, 0, S_OK }, + { CH_STARTDOCUMENT, -1, -1, S_OK }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"rootelem", L"rootelem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_STARTELEMENT, -1, -1, S_OK, L"", L"elem", L"elem", read_test_attrs }, + { CH_CHARACTERS, -1, -1, S_OK, L"text" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"elem", L"elem" }, + { CH_ENDELEMENT, -1, -1, S_OK, L"", L"rootelem", L"rootelem" }, + { CH_ENDDOCUMENT, -1, -1, S_OK}, + { CH_ENDTEST } +}; + +static const char xmlspace_attr[] = + "<?xml version=\"1.0\" encoding=\"UTF-16\"?>" + "<a xml:space=\"preserve\"> Some text data </a>"; + +static struct call_entry *expectCall; +static ISAXLocator *locator; +static ISAXXMLReader *g_reader; + +static void set_expected_seq(struct call_entry *expected) +{ + expectCall = expected; +} + +/* to be called once on each tested callback return */ +static HRESULT get_expected_ret(void) +{ + HRESULT hr = expectCall->ret; + if (expectCall->id != CH_ENDTEST) expectCall++; + return hr; +} + +static HRESULT WINAPI contentHandler_QueryInterface( + ISAXContentHandler* iface, + REFIID riid, + void **ppvObject) +{ + *ppvObject = NULL; + + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler)) + { + *ppvObject = iface; + } + else + { + return E_NOINTERFACE; + } + + return S_OK; +} + +static ULONG WINAPI contentHandler_AddRef( + ISAXContentHandler* iface) +{ + return 2; +} + +static ULONG WINAPI contentHandler_Release( + ISAXContentHandler* iface) +{ + return 1; +} + +static HRESULT WINAPI contentHandler_putDocumentLocator( + ISAXContentHandler* iface, + ISAXLocator *pLocator) +{ + struct call_entry call; + IUnknown *unk; + HRESULT hr; + + locator = pLocator; + + init_call_entry(locator, &call); + call.id = CH_PUTDOCUMENTLOCATOR; + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXLocator, (void**)&unk); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + + return get_expected_ret(); +} + +static ISAXAttributes *test_attr_ptr; +static HRESULT WINAPI contentHandler_startDocument( + ISAXContentHandler* iface) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = CH_STARTDOCUMENT; + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + test_attr_ptr = NULL; + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_endDocument( + ISAXContentHandler* iface) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = CH_ENDDOCUMENT; + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_startPrefixMapping( + ISAXContentHandler* iface, + const WCHAR *prefix, int prefix_len, + const WCHAR *uri, int uri_len) +{ + struct call_entry call; + + ok(prefix != NULL, "prefix == NULL\n"); + ok(uri != NULL, "uri == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_STARTPREFIXMAPPING; + call.arg1W = SysAllocStringLen(prefix, prefix_len); + call.arg2W = SysAllocStringLen(uri, uri_len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_endPrefixMapping( + ISAXContentHandler* iface, + const WCHAR *prefix, int len) +{ + struct call_entry call; + + ok(prefix != NULL, "prefix == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_ENDPREFIXMAPPING; + call.arg1W = SysAllocStringLen(prefix, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_startElement( + ISAXContentHandler* iface, + const WCHAR *uri, int uri_len, + const WCHAR *localname, int local_len, + const WCHAR *qname, int qname_len, + ISAXAttributes *saxattr) +{ + struct call_entry call; + HRESULT hr; + int len; + + ok(uri != NULL, "uri == NULL\n"); + ok(localname != NULL, "localname == NULL\n"); + ok(qname != NULL, "qname == NULL\n"); + ok(!!saxattr, "Unexpected pointer.\n"); + + check_interface(saxattr, &IID_ISAXAttributes, TRUE); + check_interface(saxattr, &IID_ISAXLocator, TRUE); + + check_interface(saxattr, &IID_IMXAttributes, FALSE); + check_interface(saxattr, &IID_IVBSAXAttributes, FALSE); + check_interface(saxattr, &IID_IVBSAXLocator, FALSE); + check_interface(saxattr, &IID_IDispatch, FALSE); + check_interface(saxattr, &IID_IDispatchEx, FALSE); + todo_wine + check_interface(saxattr, &IID_ISAXXMLReader, TRUE); + check_interface(saxattr, &IID_IVBSAXXMLReader, FALSE); + + init_call_entry(locator, &call); + call.id = CH_STARTELEMENT; + call.arg1W = SysAllocStringLen(uri, uri_len); + call.arg2W = SysAllocStringLen(localname, local_len); + call.arg3W = SysAllocStringLen(qname, qname_len); + + if(!test_attr_ptr) + test_attr_ptr = saxattr; + ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr); + + hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* store actual attributes */ + len = 0; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (len) + { + VARIANT_BOOL v; + int i; + + struct attribute_entry *attr; + attr = calloc(len, sizeof(*attr)); + + v = VARIANT_TRUE; + hr = ISAXXMLReader_getFeature(g_reader, L"http://xml.org/sax/features/namespaces", &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < len; i++) + { + const WCHAR *value; + int value_len; + + hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len, + &localname, &local_len, &qname, &qname_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* if 'namespaces' switched off uri and local name contains garbage */ + if (v == VARIANT_FALSE) + { + attr[i].uriW = SysAllocStringLen(NULL, 0); + attr[i].localW = SysAllocStringLen(NULL, 0); + } + else + { + attr[i].uriW = SysAllocStringLen(uri, uri_len); + attr[i].localW = SysAllocStringLen(localname, local_len); + } + + attr[i].qnameW = SysAllocStringLen(qname, qname_len); + attr[i].valueW = SysAllocStringLen(value, value_len); + } + + call.attributes = attr; + call.attr_count = len; + } + + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_endElement( + ISAXContentHandler* iface, + const WCHAR *uri, int uri_len, + const WCHAR *localname, int local_len, + const WCHAR *qname, int qname_len) +{ + struct call_entry call; + + ok(uri != NULL, "uri == NULL\n"); + ok(localname != NULL, "localname == NULL\n"); + ok(qname != NULL, "qname == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_ENDELEMENT; + call.arg1W = SysAllocStringLen(uri, uri_len); + call.arg2W = SysAllocStringLen(localname, local_len); + call.arg3W = SysAllocStringLen(qname, qname_len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_characters( + ISAXContentHandler* iface, + const WCHAR *chars, + int len) +{ + struct call_entry call; + + ok(chars != NULL, "chars == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_CHARACTERS; + call.arg1W = SysAllocStringLen(chars, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_ignorableWhitespace( + ISAXContentHandler* iface, + const WCHAR *chars, int len) +{ + struct call_entry call; + + ok(chars != NULL, "chars == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_IGNORABLEWHITESPACE; + call.arg1W = SysAllocStringLen(chars, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_processingInstruction( + ISAXContentHandler* iface, + const WCHAR *target, int target_len, + const WCHAR *data, int data_len) +{ + struct call_entry call; + + ok(target != NULL, "target == NULL\n"); + ok(data != NULL, "data == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_PROCESSINGINSTRUCTION; + call.arg1W = SysAllocStringLen(target, target_len); + call.arg2W = SysAllocStringLen(data, data_len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI contentHandler_skippedEntity( + ISAXContentHandler* iface, + const WCHAR *name, int len) +{ + struct call_entry call; + + ok(name != NULL, "name == NULL\n"); + + init_call_entry(locator, &call); + call.id = CH_SKIPPEDENTITY; + call.arg1W = SysAllocStringLen(name, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static const ISAXContentHandlerVtbl contentHandlerVtbl = +{ + contentHandler_QueryInterface, + contentHandler_AddRef, + contentHandler_Release, + contentHandler_putDocumentLocator, + contentHandler_startDocument, + contentHandler_endDocument, + contentHandler_startPrefixMapping, + contentHandler_endPrefixMapping, + contentHandler_startElement, + contentHandler_endElement, + contentHandler_characters, + contentHandler_ignorableWhitespace, + contentHandler_processingInstruction, + contentHandler_skippedEntity +}; + +static ISAXContentHandler contentHandler = { &contentHandlerVtbl }; + +static HRESULT WINAPI isaxerrorHandler_QueryInterface( + ISAXErrorHandler* iface, + REFIID riid, + void **ppvObject) +{ + *ppvObject = NULL; + + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler)) + { + *ppvObject = iface; + } + else + { + return E_NOINTERFACE; + } + + return S_OK; +} + +static ULONG WINAPI isaxerrorHandler_AddRef( + ISAXErrorHandler* iface) +{ + return 2; +} + +static ULONG WINAPI isaxerrorHandler_Release( + ISAXErrorHandler* iface) +{ + return 1; +} + +static HRESULT WINAPI isaxerrorHandler_error( + ISAXErrorHandler* iface, + ISAXLocator *pLocator, + const WCHAR *pErrorMessage, + HRESULT hrErrorCode) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static HRESULT WINAPI isaxerrorHandler_fatalError( + ISAXErrorHandler* iface, + ISAXLocator *pLocator, + const WCHAR *message, + HRESULT hr) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = EH_FATALERROR; + call.ret = hr; + + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + get_expected_ret(); + return S_OK; +} + +static HRESULT WINAPI isaxerrorHandler_ignorableWarning( + ISAXErrorHandler* iface, + ISAXLocator *pLocator, + const WCHAR *pErrorMessage, + HRESULT hrErrorCode) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static const ISAXErrorHandlerVtbl errorHandlerVtbl = +{ + isaxerrorHandler_QueryInterface, + isaxerrorHandler_AddRef, + isaxerrorHandler_Release, + isaxerrorHandler_error, + isaxerrorHandler_fatalError, + isaxerrorHandler_ignorableWarning +}; + +static ISAXErrorHandler errorHandler = { &errorHandlerVtbl }; + +static HRESULT WINAPI isaxattributes_QueryInterface( + ISAXAttributes* iface, + REFIID riid, + void **ppvObject) +{ + *ppvObject = NULL; + + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes)) + { + *ppvObject = iface; + } + else + { + return E_NOINTERFACE; + } + + return S_OK; +} + +static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface) +{ + return 2; +} + +static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface) +{ + return 1; +} + +static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length) +{ + *length = 4; + return S_OK; +} + +static HRESULT WINAPI isaxattributes_getURI( + ISAXAttributes* iface, + int nIndex, + const WCHAR **pUrl, + int *pUriSize) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getLocalName( + ISAXAttributes* iface, + int nIndex, + const WCHAR **pLocalName, + int *pLocalNameLength) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getQName( + ISAXAttributes* iface, + int index, + const WCHAR **QName, + int *QNameLength) +{ + static const WCHAR attrqnamesW[][15] = {L"a:attr1junk", + L"attr2junk", + L"attr3", + L"attr4"}; + static const int attrqnamelen[] = {7, 5, 5, 5}; + + ok(index >= 0 && index <= 3, "invalid index received %d\n", index); + + if (index >= 0 && index <= 3) + { + *QName = attrqnamesW[index]; + *QNameLength = attrqnamelen[index]; + } + else + { + *QName = NULL; + *QNameLength = 0; + } + + return S_OK; +} + +static HRESULT WINAPI isaxattributes_getName( + ISAXAttributes* iface, + int nIndex, + const WCHAR **pUri, + int * pUriLength, + const WCHAR ** pLocalName, + int * pLocalNameSize, + const WCHAR ** pQName, + int * pQNameLength) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getIndexFromName( + ISAXAttributes* iface, + const WCHAR * pUri, + int cUriLength, + const WCHAR * pLocalName, + int cocalNameLength, + int * index) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getIndexFromQName( + ISAXAttributes* iface, + const WCHAR * pQName, + int nQNameLength, + int * index) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getType( + ISAXAttributes* iface, + int nIndex, + const WCHAR ** pType, + int * pTypeLength) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getTypeFromName( + ISAXAttributes* iface, + const WCHAR * pUri, + int nUri, + const WCHAR * pLocalName, + int nLocalName, + const WCHAR ** pType, + int * nType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getTypeFromQName( + ISAXAttributes* iface, + const WCHAR * pQName, + int nQName, + const WCHAR ** pType, + int * nType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index, + const WCHAR **value, int *nValue) +{ + static const WCHAR attrvaluesW[][10] = {L"a1junk", + L"a2junk", + L"<&\">'", + L"a\rb\nc\r\n"}; + static const int attrvalueslen[] = {2, 2, 5, 7}; + + ok(index >= 0 && index <= 3, "invalid index received %d\n", index); + + if (index >= 0 && index <= 3) + { + *value = attrvaluesW[index]; + *nValue = attrvalueslen[index]; + } + else + { + *value = NULL; + *nValue = 0; + } + + return S_OK; +} + +static HRESULT WINAPI isaxattributes_getValueFromName( + ISAXAttributes* iface, + const WCHAR * pUri, + int nUri, + const WCHAR * pLocalName, + int nLocalName, + const WCHAR ** pValue, + int * nValue) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxattributes_getValueFromQName( + ISAXAttributes* iface, + const WCHAR * pQName, + int nQName, + const WCHAR ** pValue, + int * nValue) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const ISAXAttributesVtbl SAXAttributesVtbl = +{ + isaxattributes_QueryInterface, + isaxattributes_AddRef, + isaxattributes_Release, + isaxattributes_getLength, + isaxattributes_getURI, + isaxattributes_getLocalName, + isaxattributes_getQName, + isaxattributes_getName, + isaxattributes_getIndexFromName, + isaxattributes_getIndexFromQName, + isaxattributes_getType, + isaxattributes_getTypeFromName, + isaxattributes_getTypeFromQName, + isaxattributes_getValue, + isaxattributes_getValueFromName, + isaxattributes_getValueFromQName +}; + +static ISAXAttributes saxattributes = { &SAXAttributesVtbl }; + +struct saxlexicalhandler +{ + ISAXLexicalHandler ISAXLexicalHandler_iface; + LONG ref; + + HRESULT qi_hr; /* ret value for QueryInterface for handler riid */ +}; + +static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface ) +{ + return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface); +} + +static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out) +{ + struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); + + *out = NULL; + + if (IsEqualGUID(riid, &IID_IUnknown)) + { + *out = iface; + ok(0, "got unexpected IID_IUnknown query\n"); + } + else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler)) + { + if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr; + *out = iface; + } + + if (*out) + ISAXLexicalHandler_AddRef(iface); + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface) +{ + struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); + return InterlockedIncrement(&handler->ref); +} + +static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface) +{ + struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); + return InterlockedDecrement(&handler->ref); +} + +static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface, + const WCHAR * pName, int nName, const WCHAR * pPublicId, + int nPublicId, const WCHAR * pSystemId, int nSystemId) +{ + return S_OK; +} + +static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface) +{ + return S_OK; +} + +static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface, + const WCHAR * pName, int nName) +{ + ok(0, "call not expected\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface, + const WCHAR * pName, int nName) +{ + ok(0, "call not expected\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = LH_STARTCDATA; + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = LH_ENDCDATA; + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface, + const WCHAR *chars, int len) +{ + struct call_entry call; + + init_call_entry(locator, &call); + call.id = LH_COMMENT; + call.arg1W = SysAllocStringLen(chars, len); + add_call(sequences, CONTENT_HANDLER_INDEX, &call); + + return get_expected_ret(); +} + +static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl = +{ + isaxlexical_QueryInterface, + isaxlexical_AddRef, + isaxlexical_Release, + isaxlexical_startDTD, + isaxlexical_endDTD, + isaxlexical_startEntity, + isaxlexical_endEntity, + isaxlexical_startCDATA, + isaxlexical_endCDATA, + isaxlexical_comment +}; + +static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr) +{ + handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl; + handler->ref = 1; + handler->qi_hr = hr; +} + +struct saxdeclhandler +{ + ISAXDeclHandler ISAXDeclHandler_iface; + LONG ref; + + HRESULT qi_hr; /* ret value for QueryInterface for handler riid */ +}; + +static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface ) +{ + return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface); +} + +static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out) +{ + struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); + + *out = NULL; + + if (IsEqualGUID(riid, &IID_IUnknown)) + { + *out = iface; + ok(0, "got unexpected IID_IUnknown query\n"); + } + else if (IsEqualGUID(riid, &IID_ISAXDeclHandler)) + { + if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr; + *out = iface; + } + + if (*out) + ISAXDeclHandler_AddRef(iface); + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface) +{ + struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); + return InterlockedIncrement(&handler->ref); +} + +static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface) +{ + struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); + return InterlockedDecrement(&handler->ref); +} + +static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface, + const WCHAR * pName, int nName, const WCHAR * pModel, int nModel) +{ + return S_OK; +} + +static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface, + const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName, + int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault, + int nValueDefault, const WCHAR * pValue, int nValue) +{ + return S_OK; +} + +static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface, + const WCHAR * pName, int nName, const WCHAR * pValue, int nValue) +{ + return S_OK; +} + +static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface, + const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId, + const WCHAR * pSystemId, int nSystemId) +{ + return S_OK; +} + +static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = +{ + isaxdecl_QueryInterface, + isaxdecl_AddRef, + isaxdecl_Release, + isaxdecl_elementDecl, + isaxdecl_attributeDecl, + isaxdecl_internalEntityDecl, + isaxdecl_externalEntityDecl +}; + +static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr) +{ + handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl; + handler->ref = 1; + handler->qi_hr = hr; +} + +static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject) +{ + *ppvObject = NULL; + + ok(!IsEqualGUID(riid, &IID_IPersistStream), "Did not expect QI for IPersistStream\n"); + + if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown)) + *ppvObject = iface; + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI istream_AddRef(IStream *iface) +{ + return 2; +} + +static ULONG WINAPI istream_Release(IStream *iface) +{ + return 1; +} + +static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_Revert(IStream *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static int read_cnt; + +static HRESULT WINAPI instream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) +{ + static const char *ret_str; + + if(!read_cnt) + ret_str = "<?xml version=\"1.0\" ?>\n<rootelem>"; + else if(read_cnt < 5) + ret_str = "<elem attr=\"val\">text</elem>"; + else if(read_cnt == 5) + ret_str = "</rootelem>\n"; + else + ret_str = ""; + + read_cnt++; + strcpy(pv, ret_str); + *pcbRead = strlen(ret_str); + return S_OK; +} + +static const IStreamVtbl instreamVtbl = { + istream_QueryInterface, + istream_AddRef, + istream_Release, + instream_Read, + istream_Write, + istream_Seek, + istream_SetSize, + istream_CopyTo, + istream_Commit, + istream_Revert, + istream_LockRegion, + istream_UnlockRegion, + istream_Stat, + istream_Clone +}; + +static IStream instream = { &instreamVtbl }; + +static struct saxlexicalhandler lexicalhandler; +static struct saxdeclhandler declhandler; + +static IStream *create_test_stream(const char *data, int len) +{ + ULARGE_INTEGER size; + LARGE_INTEGER pos; + IStream *stream; + ULONG written; + + if (len == -1) len = strlen(data); + CreateStreamOnHGlobal(NULL, TRUE, &stream); + size.QuadPart = len; + IStream_SetSize(stream, size); + IStream_Write(stream, data, len, &written); + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + return stream; +} + +static void test_saxreader(void) +{ + HRESULT hr; + ISAXXMLReader *reader = NULL; + VARIANT var; + ISAXContentHandler *content; + ISAXErrorHandler *lpErrorHandler; + SAFEARRAY *sa; + SAFEARRAYBOUND SADim[1]; + char *ptr = NULL; + IStream *stream; + ULONG written; + HANDLE file; + static const CHAR testXmlA[] = "test.xml"; + IXMLDOMDocument *doc; + VARIANT_BOOL v; + struct call_entry *test_seq; + ISAXEntityResolver *resolver; + BSTR str; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + check_interface(reader, &IID_IUnknown, TRUE); + check_interface(reader, &IID_ISAXXMLReader, TRUE); + todo_wine + check_interface(reader, &IID_ISAXLocator, TRUE); + todo_wine + check_interface(reader, &IID_IDispatch, FALSE); + todo_wine + check_interface(reader, &IID_IDispatchEx, FALSE); + todo_wine + check_interface(reader, &IID_IVBSAXXMLReader, FALSE); + check_interface(reader, &IID_IVBSAXLocator, FALSE); + + hr = ISAXXMLReader_getContentHandler(reader, &content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(content == NULL, "Expected %p, got %p\n", NULL, content); + + hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler); + + hr = ISAXXMLReader_putContentHandler(reader, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_getContentHandler(reader, &content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(szSimpleXML); + + test_seq = content_handler_test1_alternate; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE); + + VariantClear(&var); + + SADim[0].lLbound = 0; + SADim[0].cElements = sizeof(testXML)-1; + sa = SafeArrayCreate(VT_UI1, 1, SADim); + SafeArrayAccessData(sa, (void**)&ptr); + memcpy(ptr, testXML, sizeof(testXML)-1); + SafeArrayUnaccessData(sa); + V_VT(&var) = VT_ARRAY|VT_UI1; + V_ARRAY(&var) = sa; + + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE); + + SafeArrayDestroy(sa); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = NULL; + hr = ISAXXMLReader_parse(reader, var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = NULL; + hr = ISAXXMLReader_parse(reader, var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + stream = create_test_stream(testXML, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE); + + IStream_Release(stream); + + stream = create_test_stream(test_attributes, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + test_seq = content_handler_test_attributes_alternate_4; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); + IStream_Release(stream); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)&instream; + + test_seq = read_test_seq; + read_cnt = 0; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(read_cnt == 7, "read_cnt = %d\n", read_cnt); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(carriage_ret_test); + + test_seq = content_handler_test2_alternate; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE); + + VariantClear(&var); + + /* from file url */ + file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "Could not create file: %lu\n", GetLastError()); + WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL); + CloseHandle(file); + + test_seq = content_handler_test1_alternate; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE); + + /* error handler */ + test_seq = content_handler_testerror_alternate; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE); + + /* callback ret values */ + test_seq = content_handler_test_callback_rets_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE); + + DeleteFileA(testXmlA); + + /* parse from IXMLDOMDocument */ + hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, + &IID_IXMLDOMDocument, (void**)&doc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = SysAllocString(szSimpleXML); + hr = IXMLDOMDocument_loadXML(doc, str, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)doc; + + test_seq = content_handler_test2_alternate; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE); + IXMLDOMDocument_Release(doc); + + /* xml:space test */ + test_seq = xmlspaceattr_test_alternate; + set_expected_seq(test_seq); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_(xmlspace_attr); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE); + + /* switch off 'namespaces' feature */ + hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream = create_test_stream(test_attributes, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + test_seq = content_handler_test_attributes_alt_no_ns; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); + IStream_Release(stream); + hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespaces", VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* switch off 'namespace-prefixes' feature */ + hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespace-prefixes", VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream = create_test_stream(test_attributes, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + test_seq = content_handler_test_attributes_alt_no_prefix; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); + IStream_Release(stream); + + hr = ISAXXMLReader_putFeature(reader, L"http://xml.org/sax/features/namespace-prefixes", VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* attribute normalization */ + stream = create_test_stream(attribute_normalize, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + test_seq = attribute_norm_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE); + IStream_Release(stream); + + resolver = (void*)0xdeadbeef; + hr = ISAXXMLReader_getEntityResolver(reader, &resolver); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(resolver == NULL, "got %p\n", resolver); + + hr = ISAXXMLReader_putEntityResolver(reader, NULL); + ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_saxreader_cdata(void) +{ + struct call_entry *test_seq; + ISAXXMLReader *reader; + IStream *stream; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* CDATA sections */ + init_saxlexicalhandler(&lexicalhandler, S_OK); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)&lexicalhandler.ISAXLexicalHandler_iface; + hr = ISAXXMLReader_putProperty(reader, L"http://xml.org/sax/properties/lexical-handler", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream = create_test_stream(test_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + + test_seq = cdata_test_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test", TRUE); + + IStream_Release(stream); + + /* 2. CDATA sections */ + stream = create_test_stream(test2_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)stream; + + test_seq = cdata_test2_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 2", TRUE); + + IStream_Release(stream); + + /* 3. CDATA sections */ + stream = create_test_stream(test3_cdata_xml, -1); + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + + test_seq = cdata_test3_alt; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "cdata test 3", FALSE); + + IStream_Release(stream); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_saxreader_pi(void) +{ + struct call_entry *test_seq; + ISAXXMLReader *reader; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* PI */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_pi_xml, -1); + + test_seq = pi_test_v4; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "pi test", FALSE); + VariantClear(&var); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_saxreader_characters(void) +{ + struct call_entry *test_seq; + ISAXXMLReader *reader; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + g_reader = reader; + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Character data. */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml, -1); + + test_seq = chardata_test_v4; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "char data test", FALSE); + VariantClear(&var); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)create_test_stream(test_chardata_xml2, -1); + + test_seq = chardata_test2_v4; + set_expected_seq(test_seq); + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "char data test 2", TRUE); + VariantClear(&var); + + ISAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_saxreader_properties(void) +{ + static const struct saxreader_props_test_t + { + const WCHAR *prop_name; + IUnknown *iface; + } + props_test_data[] = + { + { L"http://xml.org/sax/properties/lexical-handler", (IUnknown *)&lexicalhandler.ISAXLexicalHandler_iface }, + { L"http://xml.org/sax/properties/declaration-handler", (IUnknown *)&declhandler.ISAXDeclHandler_iface }, + { 0 } + }; + + const struct saxreader_props_test_t *ptr = props_test_data; + IVBSAXXMLReader *vb_reader; + ISAXXMLReader *reader; + HRESULT hr; + VARIANT v; + BSTR str; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + while (ptr->prop_name) + { + VARIANT varref; + LONG ref; + + init_saxlexicalhandler(&lexicalhandler, S_OK); + init_saxdeclhandler(&declhandler, S_OK); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); + + /* VT_UNKNOWN */ + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = ptr->iface; + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ref < get_refcount(ptr->iface), "Unexpected refcount.\n"); + + /* VT_DISPATCH */ + V_VT(&v) = VT_DISPATCH; + V_UNKNOWN(&v) = ptr->iface; + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ref == get_refcount(ptr->iface), "got wrong refcount %ld, expected %ld\n", get_refcount(ptr->iface), ref); + + /* VT_VARIANT|VT_BYREF with VT_UNKNOWN in referenced variant */ + V_VT(&varref) = VT_UNKNOWN; + V_UNKNOWN(&varref) = ptr->iface; + + V_VT(&v) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&v) = &varref; + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ref == get_refcount(ptr->iface), "got wrong refcount %ld, expected %ld\n", get_refcount(ptr->iface), ref); + + /* VT_VARIANT|VT_BYREF with VT_DISPATCH in referenced variant */ + V_VT(&varref) = VT_DISPATCH; + V_UNKNOWN(&varref) = ptr->iface; + + V_VT(&v) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&v) = &varref; + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ref == get_refcount(ptr->iface), "got wrong refcount %ld, expected %ld\n", get_refcount(ptr->iface), ref); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v)); + ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n"); + VariantClear(&v); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); + + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = ptr->iface; + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* only VT_EMPTY seems to be valid to reset property */ + V_VT(&v) = VT_I4; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v)); + VariantClear(&v); + + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = NULL; + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); + + /* block QueryInterface on handler riid */ + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = ptr->iface; + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE); + init_saxdeclhandler(&declhandler, E_NOINTERFACE); + + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = ptr->iface; + EXPECT_REF(ptr->iface, 1); + ref = get_refcount(ptr->iface); + hr = ISAXXMLReader_putProperty(reader, ptr->prop_name, v); + ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(ptr->iface, 1); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, ptr->prop_name, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); + ok(V_UNKNOWN(&v) != NULL, "got %p\n", V_UNKNOWN(&v)); + + ptr++; + free_bstrs(); + } + + ISAXXMLReader_Release(reader); + + /* Getters with VB interface */ + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_IVBSAXXMLReader, (void **)&vb_reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_DISPATCH(&v) = (IDispatch *)0xdeadbeef; + hr = IVBSAXXMLReader_getProperty(vb_reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_DISPATCH, "Unexpected type %d.\n", V_VT(&v)); + ok(!V_DISPATCH(&v), "Unexpected value %p.\n", V_UNKNOWN(&v)); + + V_VT(&v) = VT_EMPTY; + V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; + hr = IVBSAXXMLReader_getProperty(vb_reader, _bstr_("http://xml.org/sax/properties/declaration-handler"), &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_DISPATCH, "Unexpected type %d.\n", V_VT(&v)); + ok(!V_DISPATCH(&v), "Unexpected value %p.\n", V_UNKNOWN(&v)); + + IVBSAXXMLReader_Release(vb_reader); + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, + &IID_ISAXXMLReader, (void**)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* xmldecl-version property */ + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + + /* stream without declaration */ + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = _bstr_("<element></element>"); + hr = ISAXXMLReader_parse(reader, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"xmldecl-encoding", &v); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + todo_wine + ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"charset", &v); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + todo_wine + ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + + /* stream with declaration */ + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = _bstr_("<?xml version=\"1.0\"?><element></element>"); + hr = ISAXXMLReader_parse(reader, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* VT_BSTR|VT_BYREF input type */ + str = _bstr_("<?xml version=\"1.0\"?><element></element>"); + V_VT(&v) = VT_BSTR|VT_BYREF; + V_BSTRREF(&v) = &str; + hr = ISAXXMLReader_parse(reader, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"xmldecl-version", &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L"1.0"), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + + /* Encoding specified */ + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = _bstr_("<?xml version=\"1.0\" encoding=\"uTf-16\"?><element></element>"); + hr = ISAXXMLReader_parse(reader, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"xmldecl-encoding", &v); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + ok(!wcscmp(V_BSTR(&v), L"uTf-16"), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + } + + V_VT(&v) = VT_EMPTY; + V_BSTR(&v) = (void*)0xdeadbeef; + hr = ISAXXMLReader_getProperty(reader, L"charset", &v); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); + todo_wine + ok(!V_BSTR(&v), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + + ISAXXMLReader_Release(reader); + free_bstrs(); +} + +static void test_saxreader_max_xml_size(void) +{ + static const char test_text[] = + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa" + "aaaaaaaaaa"; + ISAXXMLReader *reader; + LARGE_INTEGER pos; + IStream *stream; + DWORD written; + VARIANT var; + HRESULT hr; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IStream_Write(stream, "<a>", 3, &written); + for (int i = 0; i < 20; ++i) + IStream_Write(stream, test_text, sizeof(test_text)-1, &written); + IStream_Write(stream, "</a>", 4, &written); + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 123; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(!V_I4(&var), "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_R4; + V_R4(&var) = 10.0; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("abc"); + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_EMPTY; + V_I4(&var) = 0; + hr = ISAXXMLReader_getProperty(reader, L"max-xml-size", &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_I4, "Unexpected type %d.\n", V_VT(&var)); + ok(V_I4(&var) == 10, "Unexpected value %ld.\n", V_I4(&var)); + + V_VT(&var) = VT_I4; + V_I4(&var) = -123; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194304; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 4194305; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Limit to 1K */ + V_VT(&var) = VT_I4; + V_I4(&var) = 1; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_I4; + V_I4(&var) = 3; + hr = ISAXXMLReader_putProperty(reader, L"max-xml-size", var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown *)stream; + hr = ISAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + + free_bstrs(); + IStream_Release(stream); +} + +static void test_saxreader_normalize_line_breaks(void) +{ + ISAXXMLReader *reader; + VARIANT_BOOL v; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + v = 123; + hr = ISAXXMLReader_getFeature(reader, L"normalize-line-breaks", &v); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(v == 123, "Unexpected value %d.\n", v); + + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_FALSE); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putFeature(reader, L"normalize-line-breaks", VARIANT_TRUE); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); +} + +static void test_saxreader_features(void) +{ + static const WCHAR *feature_names[] = + { + L"http://xml.org/sax/features/namespaces", + L"http://xml.org/sax/features/namespace-prefixes", + NULL + }; + + ISAXXMLReader *reader; + VARIANT_BOOL value; + const WCHAR **name; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + value = VARIANT_TRUE; + hr = ISAXXMLReader_getFeature(reader, L"exhaustive-errors", &value); + ok(hr == S_OK, "Failed to get feature value, hr %#lx.\n", hr); + ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); + hr = ISAXXMLReader_putFeature(reader, L"exhaustive-errors", VARIANT_FALSE); + ok(hr == S_OK, "Failed to put feature value, hr %#lx.\n", hr); + + value = VARIANT_TRUE; + hr = ISAXXMLReader_getFeature(reader, L"schema-validation", &value); + ok(hr == S_OK, "Failed to get feature value, hr %#lx.\n", hr); + ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); + hr = ISAXXMLReader_putFeature(reader, L"exhaustive-errors", VARIANT_FALSE); + ok(hr == S_OK, "Failed to put feature value, hr %#lx.\n", hr); + + name = feature_names; + while (*name) + { + value = 0xc; + hr = ISAXXMLReader_getFeature(reader, *name, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value == VARIANT_TRUE, "Unexpected value %#x.\n", value); + + value = 0xc; + hr = ISAXXMLReader_putFeature(reader, *name, value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + value = 0xd; + hr = ISAXXMLReader_getFeature(reader, *name, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value == VARIANT_TRUE, "Unexpected value %#x.\n", value); + + hr = ISAXXMLReader_putFeature(reader, *name, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + value = 0xd; + hr = ISAXXMLReader_getFeature(reader, *name, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value == VARIANT_FALSE, "Unexpected value %#x.\n", value); + + hr = ISAXXMLReader_putFeature(reader, *name, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + value = 0xd; + hr = ISAXXMLReader_getFeature(reader, *name, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value == VARIANT_TRUE, "Unexpected value %#x.\n", value); + + name++; + } + + ISAXXMLReader_Release(reader); +} + +/* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */ +static const CHAR UTF8BOMTest[] = +"\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n" +"<a></a>\n"; + +static void create_test_file(const char *name, const void *data, DWORD size) +{ + DWORD written; + HANDLE file; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "Could not create file: %lu\n", GetLastError()); + WriteFile(file, data, size, &written, NULL); + CloseHandle(file); +} + +static const WCHAR _80[] = {0x0080,0}; +static struct call_entry xml_iso_8859_1_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 44, S_OK }, + { CH_STARTELEMENT, 1, 47, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 49, S_OK, _80 }, + { CH_ENDELEMENT, 1, 52, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 52, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry xml_win1252_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 46, S_OK }, + { CH_STARTELEMENT, 1, 49, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 52, S_OK, L"\u00c1\u20ac" }, + { CH_ENDELEMENT, 1, 55, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 55, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry xml_win1253_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 46, S_OK }, + { CH_STARTELEMENT, 1, 49, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 51, S_OK, L"\u0391" }, + { CH_ENDELEMENT, 1, 54, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 54, S_OK }, + { CH_ENDTEST } +}; + +static struct call_entry xml_us_ascii_seq[] = +{ + { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, + { CH_STARTDOCUMENT, 1, 42, S_OK }, + { CH_STARTELEMENT, 1, 45, S_OK, L"", L"a", L"a" }, + { CH_CHARACTERS, 1, 47, S_OK, L"A" }, + { CH_ENDELEMENT, 1, 50, S_OK, L"", L"a", L"a" }, + { CH_ENDDOCUMENT, 1, 50, S_OK }, + { CH_ENDTEST } +}; + +static void test_saxreader_encoding(void) +{ + static const DWORD ucs4_le_test[] = + { + '<','a','>','t','e','x','t','<','/','a','>', + }; + + static const char xml_win1252_test[] = + "<?xml version=\"1.0\" encoding=\"windows-1252\" ?><a>" "\xc1" "\x80" "</a>"; + + static const char xml_win1253_test[] = + "<?xml version=\"1.0\" encoding=\"windows-1253\" ?><a>" "\xc1" "</a>"; + + static const char xml_us_ascii_test[] = + "<?xml version=\"1.0\" encoding=\"us-ascii\" ?><a>" "\xc1" "</a>"; + + static const char xml_iso_8859_1_test[] = + "<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?><a>" "\x80" "</a>"; + + static const CHAR testXmlA[] = "test.xml"; + DWORD ucs4_be_test[ARRAYSIZE(ucs4_le_test)]; + ISAXXMLReader *reader; + VARIANT input; + HRESULT hr; + + for (int i = 0; i < ARRAYSIZE(ucs4_le_test); ++i) + ucs4_be_test[i] = (ucs4_le_test[i] & 0xff) << 24; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* UTF-8 with BOM */ + create_test_file(testXmlA, UTF8BOMTest, sizeof(UTF8BOMTest)-1); + + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + DeleteFileA(testXmlA); + + /* UCS-4 LE */ + create_test_file(testXmlA, ucs4_le_test, sizeof(ucs4_le_test)); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + todo_wine + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + DeleteFileA(testXmlA); + + /* UCS-4 BE */ + create_test_file(testXmlA, ucs4_be_test, sizeof(ucs4_be_test)); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + todo_wine + ok(FAILED(hr), "Unexpected hr %#lx.\n", hr); + DeleteFileA(testXmlA); + + /* try BSTR input with no BOM or '<?xml' instruction */ + V_VT(&input) = VT_BSTR; + V_BSTR(&input) = _bstr_("<element></element>"); + hr = ISAXXMLReader_parse(reader, input); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXXMLReader_Release(reader); + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + create_test_file(testXmlA, xml_win1252_test, sizeof(xml_win1252_test) - 1); + set_expected_seq(xml_win1252_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1252_seq, "Content test with windows-1252", FALSE); + DeleteFileA(testXmlA); + + create_test_file(testXmlA, xml_win1253_test, sizeof(xml_win1253_test) - 1); + set_expected_seq(xml_win1253_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_win1253_seq, "Content test with windows-1253", FALSE); + DeleteFileA(testXmlA); + + create_test_file(testXmlA, xml_us_ascii_test, sizeof(xml_us_ascii_test) - 1); + set_expected_seq(xml_us_ascii_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_us_ascii_seq, "Content test with us-ascii", FALSE); + DeleteFileA(testXmlA); + + create_test_file(testXmlA, xml_iso_8859_1_test, sizeof(xml_iso_8859_1_test) - 1); + set_expected_seq(xml_iso_8859_1_seq); + hr = ISAXXMLReader_parseURL(reader, L"test.xml"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_sequence(sequences, CONTENT_HANDLER_INDEX, xml_iso_8859_1_seq, "Content test with iso-8859-1", FALSE); + DeleteFileA(testXmlA); + + ISAXXMLReader_Release(reader); + free_bstrs(); +} + +static void test_mxwriter_handlers(void) +{ + IMXWriter *writer; + HRESULT hr; + int i; + + static REFIID riids[] = + { + &IID_ISAXContentHandler, + &IID_ISAXLexicalHandler, + &IID_ISAXDeclHandler, + &IID_ISAXDTDHandler, + &IID_ISAXErrorHandler, + &IID_IVBSAXDeclHandler, + &IID_IVBSAXLexicalHandler, + &IID_IVBSAXContentHandler, + &IID_IVBSAXDTDHandler, + &IID_IVBSAXErrorHandler + }; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + EXPECT_REF(writer, 1); + + for (i = 0; i < ARRAY_SIZE(riids); i++) + { + IUnknown *handler; + IMXWriter *writer2; + + /* handler from IMXWriter */ + hr = IMXWriter_QueryInterface(writer, riids[i], (void**)&handler); + ok(hr == S_OK, "%s, unexpected hr %#lx.\n", wine_dbgstr_guid(riids[i]), hr); + + /* IMXWriter from a handler */ + hr = IUnknown_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2); + ok(hr == S_OK, "%s, unexpected hr %#lx.\n", wine_dbgstr_guid(riids[i]), hr); + ok(writer2 == writer, "got %p, expected %p\n", writer2, writer); + + IMXWriter_Release(writer2); + IUnknown_Release(handler); + } + + IMXWriter_Release(writer); +} + +static void test_mxwriter_default_properties(void) +{ + IMXWriter *writer; + VARIANT_BOOL b; + BSTR encoding; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + b = VARIANT_FALSE; + hr = IMXWriter_get_byteOrderMark(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "Unexpected value %#x.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_disableOutputEscaping(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %#x.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_indent(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %#x.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_omitXMLDeclaration(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %#x.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_standalone(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %#x.\n", b); + + hr = IMXWriter_get_encoding(writer, &encoding); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(encoding, L"UTF-16"), "Unexpected encoding %s.\n", wine_dbgstr_w(encoding)); + SysFreeString(encoding); + + IMXWriter_Release(writer); +} + +static void test_mxwriter_properties(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT_BOOL b; + HRESULT hr; + BSTR str, str2; + VARIANT dest; + + test_mxwriter_default_properties(); + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_disableOutputEscaping(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_byteOrderMark(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_indent(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_omitXMLDeclaration(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_standalone(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* set and check */ + hr = IMXWriter_put_standalone(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + b = VARIANT_FALSE; + hr = IMXWriter_get_standalone(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "got %d\n", b); + + hr = IMXWriter_get_encoding(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* UTF-16 is a default setting apparently */ + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"UTF-16"), "Unexpected string %s.\n", wine_dbgstr_w(str)); + + str2 = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(str != str2, "expected newly allocated, got same %p\n", str); + + SysFreeString(str2); + SysFreeString(str); + + /* put empty string */ + str = SysAllocString(L""); + hr = IMXWriter_put_encoding(writer, str); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"UTF-16"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* invalid encoding name */ + str = SysAllocString(L"test"); + hr = IMXWriter_put_encoding(writer, str); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + /* test case sensitivity */ + hr = IMXWriter_put_encoding(writer, _bstr_("utf-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"utf-8"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"uTf-16"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* how it affects document creation */ + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<?xml version=\"1.0\" standalone=\"yes\"?>\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + ISAXContentHandler_Release(content); + + hr = IMXWriter_get_version(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + /* default version is 'surprisingly' 1.0 */ + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"1.0"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* store version string as is */ + hr = IMXWriter_put_version(writer, NULL); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_version(writer, _bstr_("1.0")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_version(writer, _bstr_("")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!lstrcmpW(str, L"1.0"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_version(writer, _bstr_("a.b")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"a.b"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_version(writer, _bstr_("2.0")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"2.0"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + IMXWriter_Release(writer); + free_bstrs(); +} + +static void test_mxwriter_flush(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + LARGE_INTEGER pos; + ULARGE_INTEGER pos2; + IStream *stream; + VARIANT dest; + HRESULT hr; + char *buff; + LONG ref; + int len; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(stream, 1); + + /* detach when nothing was attached */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* attach stream */ + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine EXPECT_REF(stream, 3); + + /* detach setting VT_EMPTY destination */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(stream, 1); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* flush() doesn't detach a stream */ + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine EXPECT_REF(stream, 3); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "expected stream beginning\n"); + + /* already started */ + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* flushed on endDocument() */ + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "expected stream position moved\n"); + + IStream_Release(stream); + + /* auto-flush feature */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(stream, 1); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* internal buffer is flushed automatically on certain threshold */ + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + len = 2048; + buff = malloc(len + 1); + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "unexpected stream beginning\n"); + + hr = IMXWriter_get_output(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + ref = get_refcount(stream); + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest)); + ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest)); + ok(ref+1 == get_refcount(stream), "expected increased refcount\n"); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IStream_Release(stream); + + /* test char count lower than threshold */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(stream, 1); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test auto-flush function when stream is not set */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + len += strlen("<a>"); + ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len); + VariantClear(&dest); + + free(buff); + ISAXContentHandler_Release(content); + IStream_Release(stream); + IMXWriter_Release(writer); + free_bstrs(); +} + +static void test_mxwriter_startenddocument(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* now try another startDocument */ + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* and get duplicated prolog */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n" + "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n", V_BSTR(&dest)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + /* now with omitted declaration */ + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + free_bstrs(); +} + +enum startendtype +{ + StartElement = 0x001, + EndElement = 0x010, + StartEndElement = 0x011, + DisableEscaping = 0x100 +}; + +struct writer_startendelement_t { + const GUID *clsid; + enum startendtype type; + const WCHAR *uri; + const WCHAR *local_name; + const WCHAR *qname; + const WCHAR *output; + HRESULT hr; + ISAXAttributes *attr; +}; + +static const WCHAR startelement_xml[] = L"<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&">\'\" attr4=\"a b c \">"; +static const WCHAR startendelement_xml[] = L"<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&">\'\" attr4=\"a b c \"/>"; + +static const struct writer_startendelement_t writer_startendelement[] = +{ + { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, StartElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, StartElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"qname", L"<qname>", S_OK }, + { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local", L"<uri:local>", S_OK }, + { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local2", L"<uri:local2>", S_OK }, + + /* endElement tests */ + { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, EndElement, L"uri", NULL, NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, EndElement, NULL, L"local", NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, L"qname", NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"qname", L"</qname>", S_OK }, + { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", NULL, NULL, E_INVALIDARG }, + { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"uri:local", L"</uri:local>", S_OK }, + { &CLSID_MXXMLWriter40, EndElement, L"uri", L"local", L"uri:local2", L"</uri:local2>", S_OK }, + + /* with attributes */ + { &CLSID_MXXMLWriter40, StartElement, L"uri", L"local", L"uri:local", startelement_xml, S_OK, &saxattributes }, + /* empty elements */ + { &CLSID_MXXMLWriter40, StartEndElement, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, + { &CLSID_MXXMLWriter40, StartEndElement, L"", L"", L"", L"</>", S_OK }, + + /* with disabled output escaping */ + { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, L"uri", L"local", L"uri:local", startendelement_xml, S_OK, &saxattributes }, + + { NULL } +}; + +struct class_support +{ + const GUID *clsid; + const char *name; + const IID *iid; + BOOL supported; +}; + +static struct class_support class_support[] = +{ + { &CLSID_MXXMLWriter40, "MXXMLWriter40", &IID_IMXWriter }, + { &CLSID_SAXAttributes40, "SAXAttributes40", &IID_IMXAttributes }, + { &CLSID_SAXXMLReader40, "SAXXMLReader40", &IID_ISAXXMLReader }, + { NULL } +}; + +static void get_class_support_data(void) +{ + struct class_support *table = class_support; + + while (table->clsid) + { + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, table->iid, (void **)&unk); + if (hr == S_OK) IUnknown_Release(unk); + + table->supported = hr == S_OK; + if (hr != S_OK) win_skip("class %s not supported\n", table->name); + + table++; + } +} + +static BOOL is_clsid_supported(const GUID *clsid) +{ + const struct class_support *table = class_support; + while (table->clsid) + { + if (table->clsid == clsid) return table->supported; + table++; + } + return FALSE; +} + +static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table) +{ + int i = 0; + + while (table->clsid) + { + ISAXContentHandler *content; + IMXWriter *writer; + HRESULT hr; + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, + &IID_IMXWriter, (void**)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (table->type & DisableEscaping) + { + hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + if (table->type & StartElement) + { + hr = ISAXContentHandler_startElement(content, table->uri, table->uri ? lstrlenW(table->uri) : 0, + table->local_name, table->local_name ? lstrlenW(table->local_name) : 0, table->qname, + table->qname ? lstrlenW(table->qname) : 0, table->attr); + ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr); + } + + if (table->type & EndElement) + { + hr = ISAXContentHandler_endElement(content, table->uri, table->uri ? lstrlenW(table->uri) : 0, + table->local_name, table->local_name ? lstrlenW(table->local_name) : 0, table->qname, + table->qname ? lstrlenW(table->qname) : 0); + ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr); + } + + /* test output */ + if (hr == S_OK) + { + VARIANT dest; + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine_if(i == 16 || i == 17 || i == 19) + ok(!lstrcmpW(table->output, V_BSTR(&dest)), + "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output)); + VariantClear(&dest); + } + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + table++; + i++; + } + + free_bstrs(); +} + +/* point of these test is to start/end element with different names and name lengths */ +struct writer_startendelement2_t { + const GUID *clsid; + const WCHAR *qnamestart; + int qnamestart_len; + const WCHAR *qnameend; + int qnameend_len; + const WCHAR *output; + HRESULT hr; +}; + +static const struct writer_startendelement2_t writer_startendelement2[] = { + { &CLSID_MXXMLWriter40, L"a", -1, L"b", -1, L"<a/>", S_OK }, + { &CLSID_MXXMLWriter40, L"a", 1, L"b", 1, L"<a/>", S_OK }, + { NULL } +}; + +static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table) +{ + int i = 0; + + while (table->clsid) + { + ISAXContentHandler *content; + IMXWriter *writer; + HRESULT hr; + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, + &IID_IMXWriter, (void**)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, table->qnamestart, table->qnamestart_len, NULL); + ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, table->qnameend, table->qnameend_len); + ok(hr == table->hr, "test %d: got %#lx, expected %#lx\n", i, hr, table->hr); + + /* test output */ + if (hr == S_OK) + { + VARIANT dest; + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(table->output, V_BSTR(&dest)), + "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output)); + VariantClear(&dest); + } + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + table++; + i++; + + free_bstrs(); + } +} + + +static void test_mxwriter_startendelement(void) +{ + ISAXContentHandler *content; + IVBSAXContentHandler *vb_content; + IMXWriter *writer; + VARIANT dest; + BSTR bstr_null = NULL, bstr_empty, bstr_a, bstr_b, bstr_ab; + HRESULT hr; + + test_mxwriter_startendelement_batch(writer_startendelement); + test_mxwriter_startendelement_batch2(writer_startendelement2); + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + bstr_empty = SysAllocString(L""); + bstr_a = SysAllocString(L"a"); + bstr_b = SysAllocString(L"b"); + bstr_ab = SysAllocString(L"a:b"); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_null, &bstr_empty, &bstr_b, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_b, &bstr_empty, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<><b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_null, &bstr_null, &bstr_b); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_null, &bstr_a, &bstr_b); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_a, &bstr_b, &bstr_null); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_null, &bstr_b); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_b, &bstr_null); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<><b></b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + SysFreeString(bstr_empty); + SysFreeString(bstr_a); + SysFreeString(bstr_b); + SysFreeString(bstr_ab); + + hr = IVBSAXContentHandler_endDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* all string pointers should be not null */ + hr = ISAXContentHandler_startElement(content, L"", 0, L"b", 1, L"", 0, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"b", 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<><b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, L"a:b", 3); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, NULL, 0, L"b", 1, L"a:b", 3); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* only local name is an error too */ + hr = ISAXContentHandler_endElement(content, NULL, 0, L"b", 1, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"b", 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<><b></b>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"abcdef", 3, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<abc>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMXWriter_flush(writer); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"abdcdef", 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<abc></abd>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* length -1 */ + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<a>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + free_bstrs(); +} + +struct writer_characters_t { + const GUID *clsid; + const WCHAR *data; + const WCHAR *output; +}; + +static const struct writer_characters_t writer_characters[] = +{ + { &CLSID_MXXMLWriter40, L"< > & \" \'", L"< > & \" \'" }, + { NULL } +}; + +static void test_mxwriter_characters(void) +{ + static const WCHAR embedded_nullbytes[] = L"a\0b\0\0\0c"; + const struct writer_characters_t *table = writer_characters; + IVBSAXContentHandler *vb_content; + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + BSTR str; + HRESULT hr; + int i = 0; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void **)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = _bstr_("VbChars"); + hr = IVBSAXContentHandler_characters(vb_content, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 14); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"VbCharsTESTCHARDATA .", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXContentHandler_Release(content); + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + /* try empty characters data to see if element is closed */ + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<a></a>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + /* test embedded null bytes */ + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(SysStringLen(V_BSTR(&dest)) == ARRAY_SIZE(embedded_nullbytes), "unexpected len %d\n", SysStringLen(V_BSTR(&dest))); + ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void **)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = SysAllocStringLen(embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)); + hr = IVBSAXContentHandler_characters(vb_content, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(SysStringLen(V_BSTR(&dest)) == 1, "Unexpected length %d.\n", SysStringLen(V_BSTR(&dest))); + ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + /* Newlines in consecutive calls */ + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"ab\r", 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"\ncd", 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest)); + ok(!lstrcmpW(L"ab\r\n\r\ncd", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"\nab\rc\r\n", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest)); + ok(!lstrcmpW(L"\r\nab\r\nc\r\n", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + while (table->clsid) + { + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, + &IID_IMXWriter, (void**)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, table->data, lstrlenW(table->data)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test output */ + if (hr == S_OK) + { + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(table->output, V_BSTR(&dest)), + "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output)); + VariantClear(&dest); + } + + /* with disabled escaping */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, table->data, lstrlenW(table->data)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test output */ + if (hr == S_OK) + { + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(table->data, V_BSTR(&dest)), + "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->data)); + VariantClear(&dest); + } + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + table++; + i++; + } + + free_bstrs(); +} + +static void test_mxwriter_stream(void) +{ + IMXWriter *writer; + ISAXContentHandler *content; + HRESULT hr; + VARIANT dest; + IStream *stream; + LARGE_INTEGER pos; + ULARGE_INTEGER pos2; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Setting output of the mxwriter causes the current output to be flushed, + * and the writer to start over. + */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "expected stream position moved\n"); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<?xml version=\"1.0\" standalone=\"no\"?>\r\n", V_BSTR(&dest)), + "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* test when BOM is written to output stream */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 2, "got wrong position\n"); + + IStream_Release(stream); + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + free_bstrs(); +} + +static const char *encoding_names[] = { + "iso-8859-1", + "iso-8859-2", + "iso-8859-3", + "iso-8859-4", + "iso-8859-5", + "iso-8859-7", + "iso-8859-9", + "iso-8859-13", + "iso-8859-15", + NULL +}; + +static void test_mxwriter_encoding(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + IStream *stream; + const char *enc; + VARIANT dest; + HRESULT hr; + HGLOBAL g; + char *ptr; + BSTR s; + int i; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* The content is always re-encoded to UTF-16 when the output is + * retrieved as a BSTR. + */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<?xml version=\"1.0\" standalone=\"no\"?>\r\n", V_BSTR(&dest)), + "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* switch encoding when something is written already */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* write empty element */ + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* switch */ + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = GetHGlobalFromStream(stream, &g); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ptr = GlobalLock(g); + ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]); + GlobalUnlock(g); + + /* so output is unaffected, encoding name is stored however */ + hr = IMXWriter_get_encoding(writer, &s); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(s, L"UTF-16"), "got %s\n", wine_dbgstr_w(s)); + SysFreeString(s); + + IStream_Release(stream); + + i = 0; + enc = encoding_names[i]; + while (enc) + { + char expectedA[200]; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_(enc)); + ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */, + "%s: encoding not accepted\n", enc); + if (hr != S_OK) + { + enc = encoding_names[++i]; + IStream_Release(stream); + continue; + } + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* prepare expected string */ + *expectedA = 0; + strcat(expectedA, "<?xml version=\"1.0\" encoding=\""); + strcat(expectedA, enc); + strcat(expectedA, "\" standalone=\"no\"?>\r\n"); + + hr = GetHGlobalFromStream(stream, &g); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ptr = GlobalLock(g); + ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA); + GlobalUnlock(g); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IStream_Release(stream); + + enc = encoding_names[++i]; + } + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + free_bstrs(); +} + +static void test_obj_dispex(IUnknown *obj) +{ + DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE; + IDispatchEx *dispex; + IUnknown *unk; + DWORD props; + UINT ticnt; + HRESULT hr; + BSTR name; + DISPID did; + + hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) return; + + ticnt = 0; + hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ticnt == 1, "ticnt=%u\n", ticnt); + + name = SysAllocString(L"*"); + hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + props = 0; + hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(props == 0, "Unexpected value %ld.\n", props); + + hr = IDispatchEx_GetMemberName(dispex, dispid, &name); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) SysFreeString(name); + + hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + unk = (IUnknown*)0xdeadbeef; + hr = IDispatchEx_GetNameSpaceParent(dispex, &unk); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk); + + name = SysAllocString(L"testprop"); + hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &did); + ok(hr == DISP_E_UNKNOWNNAME, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + IDispatchEx_Release(dispex); +} + +static void test_saxreader_dispex(void) +{ + IVBSAXXMLReader *vbreader; + ISAXXMLReader *reader; + DISPPARAMS dispparams; + DISPID dispid; + IUnknown *unk; + VARIANT arg; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_interface(reader, &IID_IUnknown, TRUE); + check_interface(reader, &IID_ISAXXMLReader, TRUE); + todo_wine + check_interface(reader, &IID_IVBSAXXMLReader, FALSE); + todo_wine + check_interface(reader, &IID_IDispatch, FALSE); + todo_wine + check_interface(reader, &IID_IDispatchEx, FALSE); + + ISAXXMLReader_Release(reader); + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_IVBSAXXMLReader, (void **)&vbreader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_interface(vbreader, &IID_IUnknown, TRUE); + check_interface(vbreader, &IID_ISAXXMLReader, TRUE); + check_interface(vbreader, &IID_IVBSAXXMLReader, TRUE); + check_interface(vbreader, &IID_IDispatch, TRUE); + check_interface(vbreader, &IID_IDispatchEx, TRUE); + + hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_obj_dispex(unk); + IUnknown_Release(unk); + + dispid = DISPID_PROPERTYPUT; + dispparams.cArgs = 1; + dispparams.cNamedArgs = 1; + dispparams.rgdispidNamedArgs = &dispid; + dispparams.rgvarg = &arg; + + V_VT(&arg) = VT_DISPATCH; + V_DISPATCH(&arg) = NULL; + + /* propputref is callable as PROPERTYPUT and PROPERTYPUTREF */ + hr = IVBSAXXMLReader_Invoke(vbreader, + DISPID_SAX_XMLREADER_CONTENTHANDLER, + &IID_NULL, + 0, + DISPATCH_PROPERTYPUT, + &dispparams, + NULL, + NULL, + NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXXMLReader_Invoke(vbreader, + DISPID_SAX_XMLREADER_CONTENTHANDLER, + &IID_NULL, + 0, + DISPATCH_PROPERTYPUTREF, + &dispparams, + NULL, + NULL, + NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IVBSAXXMLReader_Release(vbreader); +} + +static void test_mxwriter_dispex(void) +{ + IDispatchEx *dispex; + IMXWriter *writer; + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void **)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void **)&unk); + test_obj_dispex(unk); + IUnknown_Release(unk); + IDispatchEx_Release(dispex); + IMXWriter_Release(writer); +} + +static void test_mxwriter_comment(void) +{ + IVBSAXLexicalHandler *vblexical; + ISAXContentHandler *content; + ISAXLexicalHandler *lexical; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void **)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void **)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_comment(lexical, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + if (0) /* crashes */ + hr = IVBSAXLexicalHandler_comment(vblexical, NULL); + + hr = ISAXLexicalHandler_comment(lexical, L"comment", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!---->\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXLexicalHandler_comment(lexical, L"comment", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!---->\r\n<!--comment-->\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + ISAXLexicalHandler_Release(lexical); + IVBSAXLexicalHandler_Release(vblexical); + IMXWriter_Release(writer); + free_bstrs(); +} + +static void test_mxwriter_cdata(void) +{ + IVBSAXLexicalHandler *vblexical; + ISAXContentHandler *content; + ISAXLexicalHandler *lexical; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void **)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void **)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startCDATA(lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<![CDATA[", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXLexicalHandler_startCDATA(vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* all these are escaped for text nodes */ + hr = ISAXContentHandler_characters(content, L"< > & \"", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"\na\rb\r\n", 6); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_endCDATA(lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<![CDATA[<![CDATA[< > & \"\r\na\r\nb\r\n]]>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + ISAXLexicalHandler_Release(lexical); + IVBSAXLexicalHandler_Release(vblexical); + IMXWriter_Release(writer); + free_bstrs(); +} + +static void test_mxwriter_pi(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 0, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 6, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?\?>\r\n<?target?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 4, L"data", 4); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?\?>\r\n<?target?>\r\n<?targ data?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 6, L"data", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?target?>\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); +} + +static void test_mxwriter_ignorablespaces(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void **)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 4); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"datad", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); +} + +static void test_mxwriter_dtd(void) +{ + IVBSAXLexicalHandler *vblexical; + ISAXContentHandler *content; + ISAXLexicalHandler *lexical; + IVBSAXDeclHandler *vbdecl; + ISAXDeclHandler *decl; + ISAXDTDHandler *dtd; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXDeclHandler, (void**)&vbdecl); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + if (0) /* crashes */ + hr = IVBSAXLexicalHandler_startDTD(vblexical, NULL, NULL, NULL); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, L"pub", 3, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, L"sys", 3); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, L"pub", 3, L"sys", 3); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, NULL, 0, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!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, L"name", 4, L"pub", 3, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, L"pub", 3, L"sys", 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXLexicalHandler_endDTD(vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!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); + + /* element declaration */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + if (0) /* crashes */ + hr = IVBSAXDeclHandler_elementDecl(vbdecl, NULL, NULL); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!ELEMENT name content>\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!ELEMENT name >\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* attribute declaration */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_attributeDecl(decl, L"element", 7, L"attribute", 9, L"CDATA", 5, + L"#REQUIRED", 9, L"value", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<!ATTLIST element attribute CDATA #REQUIRED>\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXDeclHandler_attributeDecl(decl, L"element", 7, L"attribute2", 10, L"CDATA", 5, + L"#REQUIRED", 9, L"value2", 6); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_attributeDecl(decl, L"element2", 8, L"attribute3", 10, L"CDATA", 5, + L"#REQUIRED", 9, L"value3", 6); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<!ATTLIST element attribute CDATA #REQUIRED>\r\n" + "<!ATTLIST element attribute2 CDATA #REQUIRED>\r\n" + "<!ATTLIST element2 attribute3 CDATA #REQUIRED>\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* internal entities */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + if (0) /* crashes */ + hr = IVBSAXDeclHandler_internalEntityDecl(vbdecl, NULL, NULL); + + hr = ISAXDeclHandler_internalEntityDecl(decl, L"name", -1, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_internalEntityDecl(decl, L"name", 4, L"value", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!ENTITY name \"value\">\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* external entities */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, NULL, 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + if (0) /* crashes */ + hr = IVBSAXDeclHandler_externalEntityDecl(vbdecl, NULL, NULL, NULL); + + hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", -1, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", 4, L"pubid", 5, L"sysid", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", 4, NULL, 0, L"sysid", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, L"name", 4, L"pubid", 5, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n<!ENTITY name SYSTEM \"sysid\">\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + + VariantClear(&dest); + + /* notation declaration */ + hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, NULL, 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, L"name", 4, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, L"name", 4, L"pubid", 5, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, L"name", 4, L"pubid", 5, L"sysid", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, L"name", 4, NULL, 0, L"sysid", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW( + L"<!NOTATION name" + "<!NOTATION name PUBLIC \"pubid\">\r\n" + "<!NOTATION name PUBLIC \"pubid\" \"sysid\">\r\n" + "<!NOTATION name SYSTEM \"sysid\">\r\n", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + + VariantClear(&dest); + + ISAXDTDHandler_Release(dtd); + + ISAXContentHandler_Release(content); + ISAXLexicalHandler_Release(lexical); + IVBSAXLexicalHandler_Release(vblexical); + IVBSAXDeclHandler_Release(vbdecl); + ISAXDeclHandler_Release(decl); + IMXWriter_Release(writer); + free_bstrs(); +} + +typedef struct { + const CLSID *clsid; + const char *uri; + const char *local; + const char *qname; + const char *type; + const char *value; + HRESULT hr; +} addattribute_test_t; + +static const addattribute_test_t addattribute_data[] = { + { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, + { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, + { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, + { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK }, + + { NULL } +}; + +static void test_mxattr_addAttribute(void) +{ + const addattribute_test_t *table = addattribute_data; + int i = 0; + + while (table->clsid) + { + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + const WCHAR *value; + int len, index; + HRESULT hr; + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, + &IID_IMXAttributes, (void**)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 0, "got %d\n", len); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local), + _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value)); + ok(hr == table->hr, "%d: got %#lx, expected %#lx.\n", i, hr, table->hr); + + if (hr == S_OK) + { + /* SAXAttributes40 crash on this test */ + if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || + IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) + { + hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + } + + len = -1; + hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), + table->value); + ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); + + len = -1; + value = (void*)0xdeadbeef; + hr = ISAXAttributes_getType(saxattr, 0, &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (table->type) + { + ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), + table->type); + ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len); + } + else + { + ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value)); + ok(len == 0, "%d: got wrong type value length %d\n", i, len); + } + + hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL); + if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || + IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) + { + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + } + else + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, L"nonexistent", 11, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(index == 0, "%d: got wrong index %d\n", i, index); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40)) + { + hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + } + else + { + hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* versions 4 and 6 crash */ + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local), + strlen(table->local), NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + } + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), + table->value); + ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); + + if (table->uri) { + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), + _bstr_(table->local), strlen(table->local), &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), + table->value); + ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); + } + } + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (table->hr == S_OK) + ok(len == 1, "%d: got %d length, expected 1\n", i, len); + else + ok(len == 0, "%d: got %d length, expected 0\n", i, len); + + ISAXAttributes_Release(saxattr); + IMXAttributes_Release(mxattr); + + table++; + i++; + } + + free_bstrs(); +} + +static void test_mxattr_clear(void) +{ + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + const WCHAR *ptr; + HRESULT hr; + int len; + + hr = CoCreateInstance(&CLSID_SAXAttributes40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_clear(mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"), + _bstr_("qname"), _bstr_("type"), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 1, "got %d\n", len); + + if (0) /* crashes */ + { + hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len); + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL); + } + + len = 0; + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 5, "got %d\n", len); + ok(!lstrcmpW(ptr, L"qname"), "got %s\n", wine_dbgstr_w(ptr)); + + hr = IMXAttributes_clear(mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 0, "got %d\n", len); + + len = -1; + ptr = (void*)0xdeadbeef; + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(len == -1, "got %d\n", len); + ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr); + + IMXAttributes_Release(mxattr); + ISAXAttributes_Release(saxattr); + free_bstrs(); +} + +static void test_mxattr_dispex(void) +{ + IMXAttributes *mxattr; + IDispatchEx *dispex; + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXAttributes40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk); + test_obj_dispex(unk); + IUnknown_Release(unk); + IDispatchEx_Release(dispex); + + IMXAttributes_Release(mxattr); +} + +static void test_mxattr_qi(void) +{ + static const GUID *classes[] = { &CLSID_SAXAttributes40 }; + IMXAttributes *mxattr; + HRESULT hr; + + for (int i = 0; i < ARRAYSIZE(classes); ++i) + { + hr = CoCreateInstance(classes[i], NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_interface(mxattr, &IID_IMXAttributes, TRUE); + check_interface(mxattr, &IID_ISAXAttributes, TRUE); + check_interface(mxattr, &IID_IVBSAXAttributes, TRUE); + check_interface(mxattr, &IID_IDispatch, TRUE); + check_interface(mxattr, &IID_IDispatchEx, TRUE); + + IMXAttributes_Release(mxattr); + } +} + +static void test_mxattr_localname(void) +{ + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + HRESULT hr; + int index; + + hr = CoCreateInstance(&CLSID_SAXAttributes40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* add some ambiguous attribute names */ + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), + _bstr_("a:localname"), _bstr_(""), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), + _bstr_("b:localname"), _bstr_(""), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname", 9, &index); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(index == 0, "Unexpected index %d\n", index); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri1", 4, L"localname", 9, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "Unexpected index %d\n", index); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname1", 10, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "Unexpected index %d\n", index); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname1", 10, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, L"localname1", 10, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + ISAXAttributes_Release(saxattr); + IMXAttributes_Release(mxattr); + free_bstrs(); +} + +static void test_mxwriter_indent(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_indent(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"a", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"b", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"c", -1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"c", -1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"b", -1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"a", -1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>", V_BSTR(&dest)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + free_bstrs(); +} + +static void test_saxreader_vb_content_handler(void) +{ + IVBSAXXMLReader *reader; + struct sink *sink; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_IVBSAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + sink = create_test_sink(); + + hr = IVBSAXXMLReader_putref_contentHandler(reader, &sink->IVBSAXContentHandler_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<a>text</a>"); + hr = IVBSAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IVBSAXContentHandler_Release(&sink->IVBSAXContentHandler_iface); + IVBSAXXMLReader_Release(reader); + + free_bstrs(); +} + +static void test_mxwriter_from_reader(void) +{ + IVBSAXContentHandler *content_handler; + IVBSAXXMLReader *reader; + IMXWriter *writer; + VARIANT var; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_IVBSAXXMLReader, (void **)&reader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_MXXMLWriter40, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void **)&content_handler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXXMLReader_putref_contentHandler(reader, content_handler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = _bstr_("<a>text</a>"); + hr = IVBSAXXMLReader_parse(reader, var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + VariantClear(&var); + + V_VT(&var) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&var) == VT_BSTR, "Unexpected output type %d.\n", V_VT(&var)); + ok(!lstrcmpW(L"<a>text</a>", V_BSTR(&var)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + IMXWriter_Release(writer); + IVBSAXXMLReader_Release(reader); + IVBSAXContentHandler_Release(content_handler); +} + +START_TEST(saxreader) +{ + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "failed to init com\n"); + + init_call_sequences(sequences, NUM_CALL_SEQUENCES); + + get_class_support_data(); + + if (is_clsid_supported(&CLSID_SAXXMLReader40)) + { + test_saxreader(); + test_saxreader_properties(); + test_saxreader_max_xml_size(); + test_saxreader_normalize_line_breaks(); + test_saxreader_features(); + test_saxreader_encoding(); + test_saxreader_dispex(); + test_saxreader_vb_content_handler(); + test_saxreader_cdata(); + test_saxreader_pi(); + test_saxreader_characters(); + } + + if (is_clsid_supported(&CLSID_MXXMLWriter40)) + { + test_mxwriter_handlers(); + test_mxwriter_startenddocument(); + test_mxwriter_startendelement(); + test_mxwriter_characters(); + test_mxwriter_comment(); + test_mxwriter_cdata(); + test_mxwriter_pi(); + test_mxwriter_ignorablespaces(); + test_mxwriter_dtd(); + test_mxwriter_properties(); + test_mxwriter_flush(); + test_mxwriter_stream(); + test_mxwriter_encoding(); + test_mxwriter_dispex(); + test_mxwriter_indent(); + test_mxwriter_from_reader(); + } + + if (is_clsid_supported(&CLSID_SAXAttributes40)) + { + test_mxattr_qi(); + test_mxattr_addAttribute(); + test_mxattr_clear(); + test_mxattr_localname(); + test_mxattr_dispex(); + } + + CoUninitialize(); +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/mxwriter.c | 16 ++++++++-------- dlls/msxml4/tests/saxreader.c | 1 - dlls/msxml6/tests/saxreader.c | 2 -- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c index f766aa85955..0024d375934 100644 --- a/dlls/msxml3/mxwriter.c +++ b/dlls/msxml3/mxwriter.c @@ -489,9 +489,9 @@ static void close_output_buffer(mxwriter *writer) */ -static void write_crlf(mxwriter *writer) +static void write_crlf(mxwriter *writer, escape_mode mode) { - bool use_charref_lf = writer->class_version == MSXML6; + bool use_charref_lf = writer->class_version >= MSXML4 && mode == EscapeValue; if (use_charref_lf) write_output_buffer(writer, L" ", 5); @@ -514,19 +514,19 @@ static void write_escaped_string(mxwriter *writer, const WCHAR *str, int len, es else if (*p == '"' && mode == EscapeValue) write_output_buffer(writer, L""", 6); else if (*p == '\n') - write_crlf(writer); + write_crlf(writer, mode); else if (*p == '\r') { if (len > 0 && p[1] == '\n') { - write_crlf(writer); + write_crlf(writer, mode); ++p; --len; } else { - write_crlf(writer); + write_crlf(writer, mode); } } else @@ -543,19 +543,19 @@ static void write_string_with_crlf(mxwriter *writer, const WCHAR *str, int len) while (len-- > 0) { if (*p == '\n') - write_crlf(writer); + write_crlf(writer, EscapeText); else if (*p == '\r') { if (len > 0 && p[1] == '\n') { - write_crlf(writer); + write_crlf(writer, EscapeText); ++p; --len; } else { - write_crlf(writer); + write_crlf(writer, EscapeText); } } else diff --git a/dlls/msxml4/tests/saxreader.c b/dlls/msxml4/tests/saxreader.c index 0d9a199f029..76cda76b99e 100644 --- a/dlls/msxml4/tests/saxreader.c +++ b/dlls/msxml4/tests/saxreader.c @@ -3783,7 +3783,6 @@ static void test_mxwriter_startendelement_batch(const struct writer_startendelem hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); - todo_wine_if(i == 16 || i == 17 || i == 19) ok(!lstrcmpW(table->output, V_BSTR(&dest)), "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), wine_dbgstr_w(table->output)); VariantClear(&dest); diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c index 1522589e622..dd99d936f57 100644 --- a/dlls/msxml6/tests/saxreader.c +++ b/dlls/msxml6/tests/saxreader.c @@ -2222,7 +2222,6 @@ static void test_mxwriter_characters(void) hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest)); - todo_wine ok(!lstrcmpW(L"ab\r\n\r\ncd", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); @@ -2237,7 +2236,6 @@ static void test_mxwriter_characters(void) hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(V_VT(&dest) == VT_BSTR, "Unexpected type %d.\n", V_VT(&dest)); - todo_wine ok(!lstrcmpW(L"\r\nab\r\nc\r\n", V_BSTR(&dest)), "Unexpected content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10172
participants (2)
-
Nikolay Sivov -
Nikolay Sivov (@nsivov)