| /* | |
| * SAX Reader implementation | |
| * | |
| * 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 | |
| * 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 | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "winnls.h" | |
| #include "ole2.h" | |
| #include "msxml6.h" | |
| #include "wininet.h" | |
| #include "urlmon.h" | |
| #include "winreg.h" | |
| #include "shlwapi.h" | |
| #include "winternl.h" | |
| #include "wine/debug.h" | |
| #include "msxml_private.h" | |
| #include "initguid.h" | |
| #include "saxreader_extensions.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(msxml); | |
| 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, | |
| }; | |
| 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 attribute_type | |
| { | |
| ATTR_TYPE_INVALID = 0, | |
| ATTR_TYPE_CDATA, | |
| ATTR_TYPE_ID, | |
| ATTR_TYPE_IDREF, | |
| ATTR_TYPE_IDREFS, | |
| ATTR_TYPE_ENTITY, | |
| ATTR_TYPE_ENTITIES, | |
| ATTR_TYPE_NMTOKEN, | |
| ATTR_TYPE_NMTOKENS, | |
| ATTR_TYPE_NOTATION, | |
| ATTR_TYPE_ENUMERATION, | |
| }; | |
| enum attribute_default_type | |
| { | |
| ATTR_DEF_TYPE_NONE = 0, | |
| ATTR_DEF_TYPE_REQUIRED, | |
| ATTR_DEF_TYPE_IMPLIED, | |
| ATTR_DEF_TYPE_FIXED, | |
| }; | |
| struct attribute | |
| { | |
| BSTR uri; | |
| BSTR local; | |
| BSTR qname; | |
| BSTR value; | |
| BSTR prefix; | |
| bool nsdef; | |
| }; | |
| struct enumeration | |
| { | |
| BSTR *values; | |
| size_t count; | |
| size_t capacity; | |
| }; | |
| struct attlist_attr | |
| { | |
| struct parsed_name name; | |
| enum attribute_type type; | |
| struct enumeration valuelist; | |
| enum attribute_default_type valuetype; | |
| BSTR value; | |
| }; | |
| struct attlist_decl | |
| { | |
| struct list entry; | |
| BSTR name; | |
| struct attlist_attr *attributes; | |
| size_t count; | |
| size_t capacity; | |
| }; | |
| enum element_type | |
| { | |
| ELEMENT_TYPE_UNDEFINED = 0, | |
| ELEMENT_TYPE_EMPTY, | |
| ELEMENT_TYPE_ANY, | |
| ELEMENT_TYPE_MIXED, | |
| ELEMENT_TYPE_ELEMENT, | |
| }; | |
| enum element_content_type | |
| { | |
| ELEMENT_CONTENT_PCDATA = 1, | |
| ELEMENT_CONTENT_ELEMENT, | |
| ELEMENT_CONTENT_SEQ, | |
| ELEMENT_CONTENT_OR, | |
| }; | |
| enum element_content_occurrence | |
| { | |
| ELEMENT_CONTENT_ONCE = 1, | |
| ELEMENT_CONTENT_OPT, | |
| ELEMENT_CONTENT_MULT, | |
| ELEMENT_CONTENT_PLUS, | |
| }; | |
| struct element_content | |
| { | |
| BSTR name; | |
| enum element_content_type type; | |
| enum element_content_occurrence occurrence; | |
| struct element_content *left; | |
| struct element_content *right; | |
| struct element_content *parent; | |
| }; | |
| struct element_decl | |
| { | |
| struct list entry; | |
| BSTR name; | |
| enum element_type type; | |
| struct element_content *content; | |
| }; | |
| 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, | |
| NormalizeLineBreaks = 0x00008000, | |
| NormalizeAttributeValues = 0x00010000, | |
| FeatureForceDWord = 0xffffffff, | |
| }; | |
| struct saxreader_feature_pair | |
| { | |
| enum saxreader_feature feature; | |
| const WCHAR *name; | |
| }; | |
| static const struct saxreader_feature_pair saxreader_feature_map[] = { | |
| { ExhaustiveErrors, L"exhaustive-errors" }, | |
| { ExternalGeneralEntities, L"http://xml.org/sax/features/external-general-entities" }, | |
| { ExternalParameterEntities, L"http://xml.org/sax/features/external-parameter-entities" }, | |
| { LexicalHandlerParEntities, L"http://xml.org/sax/features/lexical-handler/parameter-entities" }, | |
| { NamespacePrefixes, L"http://xml.org/sax/features/namespace-prefixes" }, | |
| { Namespaces, L"http://xml.org/sax/features/namespaces" }, | |
| { NormalizeAttributeValues, L"normalize-attribute-values" }, | |
| { NormalizeLineBreaks, L"normalize-line-breaks" }, | |
| { ProhibitDTD, L"prohibit-dtd" }, | |
| { SchemaValidation, L"schema-validation" }, | |
| }; | |
| static enum saxreader_feature get_saxreader_feature(const WCHAR *name) | |
| { | |
| int min, max, n, c; | |
| min = 0; | |
| max = ARRAY_SIZE(saxreader_feature_map) - 1; | |
| while (min <= max) | |
| { | |
| n = (min+max)/2; | |
| c = wcscmp(saxreader_feature_map[n].name, name); | |
| if (!c) | |
| return saxreader_feature_map[n].feature; | |
| if (c > 0) | |
| max = n-1; | |
| else | |
| min = n+1; | |
| } | |
| return FeatureUnknown; | |
| } | |
| static const WCHAR empty_str; | |
| typedef struct | |
| { | |
| BSTR prefix; | |
| BSTR uri; | |
| } ns; | |
| typedef struct | |
| { | |
| struct list entry; | |
| BSTR prefix; | |
| BSTR local; | |
| BSTR qname; | |
| ns *ns; /* namespaces defined in this particular element */ | |
| int ns_count; | |
| } element_entry; | |
| enum saxhandler_type | |
| { | |
| SAXContentHandler = 0, | |
| SAXDeclHandler, | |
| SAXDTDHandler, | |
| SAXEntityResolver, | |
| SAXErrorHandler, | |
| SAXLexicalHandler, | |
| SAXExtensionHandler, | |
| SAXHandler_Last | |
| }; | |
| struct saxanyhandler_iface | |
| { | |
| IUnknown *handler; | |
| IUnknown *vbhandler; | |
| }; | |
| struct saxcontenthandler_iface | |
| { | |
| ISAXContentHandler *handler; | |
| IVBSAXContentHandler *vbhandler; | |
| }; | |
| struct saxextensionhandler_iface | |
| { | |
| ISAXExtensionHandler *handler; | |
| IDispatch *vbhandler; | |
| }; | |
| struct saxerrorhandler_iface | |
| { | |
| ISAXErrorHandler *handler; | |
| IVBSAXErrorHandler *vbhandler; | |
| }; | |
| struct saxlexicalhandler_iface | |
| { | |
| ISAXLexicalHandler *handler; | |
| IVBSAXLexicalHandler *vbhandler; | |
| }; | |
| struct saxdtdhandler_iface | |
| { | |
| ISAXDTDHandler *handler; | |
| IVBSAXDTDHandler *vbhandler; | |
| }; | |
| struct saxdeclhandler_iface | |
| { | |
| ISAXDeclHandler *handler; | |
| IVBSAXDeclHandler *vbhandler; | |
| }; | |
| struct saxentityresolver_iface | |
| { | |
| ISAXEntityResolver *handler; | |
| IVBSAXEntityResolver *vbhandler; | |
| }; | |
| struct saxhandler_iface | |
| { | |
| 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 saxextensionhandler_iface extension; | |
| struct saxanyhandler_iface anyhandler; | |
| } u; | |
| }; | |
| struct saxreader | |
| { | |
| DispatchEx dispex; | |
| IVBSAXXMLReader IVBSAXXMLReader_iface; | |
| ISAXXMLReader ISAXXMLReader_iface; | |
| ISAXXMLReaderExtension ISAXXMLReaderExtension_iface; | |
| LONG refcount; | |
| struct saxhandler_iface saxhandlers[SAXHandler_Last]; | |
| BOOL isParsing; | |
| enum saxreader_feature features; | |
| BSTR xmldecl_version; | |
| BSTR xmldecl_standalone; | |
| BSTR xmldecl_encoding; | |
| int max_xml_size; | |
| int max_element_depth; | |
| BSTR empty_bstr; | |
| MSXML_VERSION version; | |
| }; | |
| 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; | |
| if (unk) | |
| IUnknown_AddRef(unk); | |
| if ((vb && iface->vbhandler) || (!vb && iface->handler)) | |
| IUnknown_Release(vb ? iface->vbhandler : iface->handler); | |
| if (vb) | |
| iface->vbhandler = unk; | |
| else | |
| iface->handler = unk; | |
| return S_OK; | |
| } | |
| 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; | |
| if (!ret) return E_POINTER; | |
| if ((vb && iface->vbhandler) || (!vb && iface->handler)) | |
| { | |
| if (vb) | |
| IUnknown_AddRef(iface->vbhandler); | |
| else | |
| IUnknown_AddRef(iface->handler); | |
| } | |
| *ret = vb ? iface->vbhandler : iface->handler; | |
| return S_OK; | |
| } | |
| static struct saxcontenthandler_iface *saxreader_get_contenthandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXContentHandler].u.content; | |
| } | |
| static struct saxextensionhandler_iface *saxreader_get_extensionhandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXExtensionHandler].u.extension; | |
| } | |
| static struct saxerrorhandler_iface *saxreader_get_errorhandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXErrorHandler].u.error; | |
| } | |
| static struct saxlexicalhandler_iface *saxreader_get_lexicalhandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXLexicalHandler].u.lexical; | |
| } | |
| static struct saxdtdhandler_iface *saxreader_get_dtdhandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXDTDHandler].u.dtd; | |
| } | |
| static struct saxdeclhandler_iface *saxreader_get_declhandler(struct saxreader *reader) | |
| { | |
| return &reader->saxhandlers[SAXDeclHandler].u.decl; | |
| } | |
| struct encoded_buffer | |
| { | |
| char *data; | |
| size_t cur; | |
| size_t allocated; | |
| size_t written; | |
| }; | |
| static void encoded_buffer_cleanup(struct encoded_buffer *buffer) | |
| { | |
| free(buffer->data); | |
| memset(buffer, 0, sizeof(*buffer)); | |
| } | |
| enum xmlencoding | |
| { | |
| 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 const char *saxreader_get_encoding_name(enum xmlencoding encoding) | |
| { | |
| 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 bool saxreader_get_encoding_codepage(const WCHAR *name, UINT *codepage) | |
| { | |
| static const struct | |
| { | |
| const WCHAR *name; | |
| UINT codepage; | |
| } | |
| encodings[] = | |
| { | |
| { L"shift_jis", 932 }, | |
| { L"shift-jis", 932 }, | |
| { 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 bool saxreader_can_ignore_encoding(const WCHAR *encoding) | |
| { | |
| static const WCHAR *encodings[] = | |
| { | |
| L"utf-16", L"utf-8" | |
| }; | |
| for (int i = 0; i < ARRAYSIZE(encodings); ++i) | |
| { | |
| if (!wcsicmp(encoding, encodings[i])) | |
| return true; | |
| } | |
| return false; | |
| } | |
| typedef int (*p_converter)(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length); | |
| static int convert_mbtowc(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) | |
| { | |
| return MultiByteToWideChar(cp, 0, src, src_size, buffer, buffer_length); | |
| } | |
| static int convert_utf16be(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) | |
| { | |
| size_t size = min(src_size / 2, buffer_length); | |
| if (!buffer) return src_size / 2; | |
| for (int i = 0; i < size; ++i) | |
| buffer[i] = src[i + 1] | src[i]; | |
| return size; | |
| } | |
| static unsigned int saxreader_convert_u32_to_u16(UINT32 ch, WCHAR *out) | |
| { | |
| if (ch < 0x10000) | |
| { | |
| out[0] = ch; | |
| out[1] = 0; | |
| out[2] = 0; | |
| return 1; | |
| } | |
| else | |
| { | |
| out[0] = ((ch - 0x10000) >> 10) + 0xd800; | |
| out[1] = ((ch - 0x10000) & 0x3ff) + 0xdc00; | |
| out[2] = 0; | |
| return 2; | |
| } | |
| } | |
| static int convert_ucs4le(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) | |
| { | |
| const UINT32 *u = (const UINT32 *)src; | |
| int size = 0, len; | |
| WCHAR *p = buffer; | |
| if (src_size % 4) return 0; | |
| if (!buffer) | |
| { | |
| for (int i = 0; i < src_size / 4; ++i) | |
| { | |
| ++size; | |
| if (u[i] > 0xffff) ++size; | |
| } | |
| return size; | |
| } | |
| while (buffer_length && src_size) | |
| { | |
| if (*u > 0xffff && buffer_length < 2) break; | |
| len = saxreader_convert_u32_to_u16(*u, buffer); | |
| buffer_length -= len; | |
| buffer += len; | |
| ++u; | |
| } | |
| return buffer - p; | |
| } | |
| static int convert_ucs4be(UINT cp, const char *src, int src_size, WCHAR *buffer, int buffer_length) | |
| { | |
| const UINT32 *u = (const UINT32 *)src; | |
| int size = 0, len; | |
| WCHAR *p = buffer; | |
| UINT32 _u; | |
| if (src_size % 4) return 0; | |
| if (!buffer) | |
| { | |
| for (int i = 0; i < src_size / 4; ++i) | |
| { | |
| ++size; | |
| if (u[i] & 0xffff) ++size; | |
| } | |
| return size; | |
| } | |
| 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; | |
| } | |
| return buffer - p; | |
| } | |
| static p_converter convert_get_converter(enum xmlencoding encoding) | |
| { | |
| switch (encoding) | |
| { | |
| 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: | |
| ; | |
| } | |
| return NULL; | |
| } | |
| static UINT convert_get_codepage(enum xmlencoding encoding) | |
| { | |
| switch (encoding) | |
| { | |
| case XML_ENCODING_UTF16LE: | |
| return 1200; | |
| case XML_ENCODING_UTF8: | |
| return CP_UTF8; | |
| default: | |
| ; | |
| } | |
| return 0; | |
| } | |
| /* 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) | |
| { | |
| switch (encoding) | |
| { | |
| 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; | |
| } | |
| } | |
| struct input_buffer | |
| { | |
| 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; | |
| size_t raw_size; | |
| bool last_cr; | |
| unsigned int chunk_size; | |
| struct list entities; | |
| }; | |
| static bool is_mb_codepage(UINT codepage) | |
| { | |
| return codepage == 932 || codepage == 936; | |
| } | |
| static size_t convert_get_raw_length(struct input_buffer *buffer) | |
| { | |
| const struct encoded_buffer *raw = &buffer->raw; | |
| size_t size = raw->written; | |
| if (buffer->encoding == XML_ENCODING_UTF8) | |
| { | |
| if (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)) | |
| ; | |
| } | |
| } | |
| else if (is_mb_codepage(buffer->code_page)) | |
| { | |
| /* Attempt to skip incomplete character */ | |
| if (size > 2 | |
| && IsDBCSLeadByteEx(buffer->code_page, raw->data[size-1]) | |
| && !IsDBCSLeadByteEx(buffer->code_page, raw->data[size-2])) | |
| { | |
| --size; | |
| } | |
| } | |
| } | |
| return size - raw->cur; | |
| } | |
| enum saxreader_state | |
| { | |
| 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, | |
| }; | |
| struct string_buffer | |
| { | |
| WCHAR *data; | |
| size_t count; | |
| size_t capacity; | |
| }; | |
| struct dtd | |
| { | |
| unsigned int refcount; | |
| struct list attr_decls; | |
| struct list elements; | |
| struct list entities; | |
| }; | |
| 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; | |
| int depth; | |
| ISequentialStream *stream; | |
| bool eos; | |
| struct | |
| { | |
| struct attribute *entries; | |
| size_t count; | |
| size_t capacity; | |
| size_t *map; | |
| } attributes; | |
| struct dtd *dtd; | |
| struct | |
| { | |
| BSTR xmlns_uri; | |
| BSTR xml_uri; | |
| BSTR null_uri; | |
| BSTR typenames[ATTR_TYPE_NMTOKENS + 1]; | |
| BSTR valuenames[ATTR_DEF_TYPE_FIXED + 1]; | |
| } strings; | |
| bool collect; | |
| struct string_buffer scratch; | |
| struct input_buffer buffer; | |
| struct text_position start_document_position; | |
| HRESULT status; | |
| }; | |
| static inline struct saxreader *impl_from_IVBSAXXMLReader(IVBSAXXMLReader *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxreader, IVBSAXXMLReader_iface); | |
| } | |
| static inline struct saxreader *impl_from_ISAXXMLReader(ISAXXMLReader *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxreader, ISAXXMLReader_iface); | |
| } | |
| static inline struct saxreader *impl_from_ISAXXMLReaderExtension(ISAXXMLReaderExtension *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxreader, ISAXXMLReaderExtension_iface); | |
| } | |
| static inline struct saxlocator *impl_from_IVBSAXLocator(IVBSAXLocator *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxlocator, IVBSAXLocator_iface); | |
| } | |
| static inline struct saxlocator *impl_from_ISAXLocator(ISAXLocator *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxlocator, ISAXLocator_iface); | |
| } | |
| static inline struct saxlocator *impl_from_IVBSAXAttributes(IVBSAXAttributes *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxlocator, IVBSAXAttributes_iface); | |
| } | |
| static inline struct saxlocator *impl_from_ISAXAttributes(ISAXAttributes *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct saxlocator, ISAXAttributes_iface); | |
| } | |
| 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 inline HRESULT set_feature_value(struct saxreader *reader, enum saxreader_feature feature, VARIANT_BOOL value) | |
| { | |
| /* 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; | |
| } | |
| static inline HRESULT get_feature_value(const struct saxreader *reader, enum saxreader_feature feature, VARIANT_BOOL *value) | |
| { | |
| *value = reader->features & feature ? VARIANT_TRUE : VARIANT_FALSE; | |
| return S_OK; | |
| } | |
| static void saxreader_set_error(struct saxlocator *locator, HRESULT status) | |
| { | |
| if (locator->status == S_OK) | |
| locator->status = status; | |
| } | |
| static struct dtd * saxreader_new_dtd(struct saxlocator *locator) | |
| { | |
| struct dtd *dtd; | |
| if (!(dtd = calloc(1, sizeof(*dtd)))) | |
| { | |
| saxreader_set_error(locator, E_OUTOFMEMORY); | |
| return NULL; | |
| } | |
| dtd->refcount = 1; | |
| list_init(&dtd->elements); | |
| list_init(&dtd->attr_decls); | |
| list_init(&dtd->entities); | |
| return dtd; | |
| } | |
| static void saxreader_clear_enumeration(struct enumeration *list) | |
| { | |
| for (size_t i = 0; i < list->count; ++i) | |
| SysFreeString(list->values[i]); | |
| free(list->values); | |
| } | |
| static void saxreader_free_name(struct parsed_name *name) | |
| { | |
| SysFreeString(name->prefix); | |
| SysFreeString(name->local); | |
| SysFreeString(name->qname); | |
| memset(name, 0, sizeof(*name)); | |
| } | |
| static void saxreader_free_attlist(struct attlist_decl *decl) | |
| { | |
| for (size_t i = 0; i < decl->count; ++i) | |
| { | |
| struct attlist_attr *attr = &decl->attributes[i]; | |
| saxreader_free_name(&attr->name); | |
| saxreader_clear_enumeration(&attr->valuelist); | |
| SysFreeString(attr->value); | |
| } | |
| SysFreeString(decl->name); | |
| free(decl->attributes); | |
| free(decl); | |
| } | |
| static void saxreader_free_entity(struct entity_decl *entity) | |
| { | |
| SysFreeString(entity->name); | |
| SysFreeString(entity->sysid); | |
| SysFreeString(entity->value); | |
| free(entity); | |
| } | |
| static struct element_content * saxreader_free_element_content(struct element_content *c) | |
| { | |
| int depth = 0; | |
| while (true) | |
| { | |
| struct element_content *parent; | |
| while (c->left || c->right) | |
| { | |
| c = c->left ? c->left : c->right; | |
| ++depth; | |
| } | |
| SysFreeString(c->name); | |
| parent = c->parent; | |
| if (depth == 0 || !parent) | |
| { | |
| free(c); | |
| break; | |
| } | |
| if (c == parent->left) | |
| parent->left = NULL; | |
| else | |
| parent->right = NULL; | |
| free(c); | |
| if (parent->right) | |
| { | |
| c = parent->right; | |
| } | |
| else | |
| { | |
| --depth; | |
| c = parent; | |
| } | |
| } | |
| return NULL; | |
| } | |
| static void saxreader_free_element_decl(struct element_decl *decl) | |
| { | |
| if (decl->content) | |
| saxreader_free_element_content(decl->content); | |
| SysFreeString(decl->name); | |
| free(decl); | |
| } | |
| struct dtd *parser_dtd_addref(struct dtd *dtd) | |
| { | |
| if (dtd) | |
| ++dtd->refcount; | |
| return dtd; | |
| } | |
| void parser_dtd_release(struct dtd *dtd) | |
| { | |
| struct element_decl *element, *element_next; | |
| struct entity_decl *entity, *entity_next; | |
| struct attlist_decl *attr, *attr_next; | |
| if (!dtd || --dtd->refcount) | |
| return; | |
| LIST_FOR_EACH_ENTRY_SAFE(entity, entity_next, &dtd->entities, struct entity_decl, entry) | |
| { | |
| list_remove(&entity->entry); | |
| saxreader_free_entity(entity); | |
| } | |
| LIST_FOR_EACH_ENTRY_SAFE(attr, attr_next, &dtd->attr_decls, struct attlist_decl, entry) | |
| { | |
| list_remove(&attr->entry); | |
| saxreader_free_attlist(attr); | |
| } | |
| LIST_FOR_EACH_ENTRY_SAFE(element, element_next, &dtd->elements, struct element_decl, entry) | |
| { | |
| list_remove(&element->entry); | |
| saxreader_free_element_decl(element); | |
| } | |
| free(dtd); | |
| } | |
| static BSTR saxreader_alloc_string(struct saxlocator *locator, const WCHAR *str) | |
| { | |
| BSTR ret; | |
| if (!str) | |
| return NULL; | |
| if (!(ret = SysAllocString(str))) | |
| saxreader_set_error(locator, E_OUTOFMEMORY); | |
| return ret; | |
| } | |
| static BSTR saxreader_alloc_stringlen(struct saxlocator *locator, const WCHAR *str, UINT len) | |
| { | |
| BSTR ret; | |
| if (!(ret = SysAllocStringLen(str, len))) | |
| saxreader_set_error(locator, E_OUTOFMEMORY); | |
| return ret; | |
| } | |
| static void saxreader_ensure_dtd_typenames(struct saxlocator *locator) | |
| { | |
| if (locator->strings.typenames[ATTR_TYPE_CDATA]) | |
| return; | |
| locator->strings.typenames[ATTR_TYPE_CDATA] = saxreader_alloc_string(locator, L"CDATA"); | |
| locator->strings.typenames[ATTR_TYPE_ID] = saxreader_alloc_string(locator, L"ID"); | |
| locator->strings.typenames[ATTR_TYPE_IDREF] = saxreader_alloc_string(locator, L"IDREF"); | |
| locator->strings.typenames[ATTR_TYPE_IDREFS] = saxreader_alloc_string(locator, L"IDREFS"); | |
| locator->strings.typenames[ATTR_TYPE_ENTITY] = saxreader_alloc_string(locator, L"ENTITY"); | |
| locator->strings.typenames[ATTR_TYPE_ENTITIES] = saxreader_alloc_string(locator, L"ENTITIES"); | |
| locator->strings.typenames[ATTR_TYPE_NMTOKEN] = saxreader_alloc_string(locator, L"NMTOKEN"); | |
| locator->strings.typenames[ATTR_TYPE_NMTOKENS] = saxreader_alloc_string(locator, L"NMTOKENS"); | |
| locator->strings.valuenames[ATTR_DEF_TYPE_REQUIRED] = saxreader_alloc_string(locator, L"#REQUIRED"); | |
| locator->strings.valuenames[ATTR_DEF_TYPE_IMPLIED] = saxreader_alloc_string(locator, L"#IMPLIED"); | |
| locator->strings.valuenames[ATTR_DEF_TYPE_FIXED] = saxreader_alloc_string(locator, L"#FIXED"); | |
| } | |
| 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++) | |
| { | |
| SysFreeString(element->ns[i].prefix); | |
| SysFreeString(element->ns[i].uri); | |
| } | |
| SysFreeString(element->prefix); | |
| SysFreeString(element->local); | |
| SysFreeString(element->qname); | |
| free(element->ns); | |
| free(element); | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_QueryInterface(IVBSAXAttributes *iface, REFIID riid, void **obj) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| return IVBSAXLocator_QueryInterface(&locator->IVBSAXLocator_iface, riid, obj); | |
| } | |
| static ULONG WINAPI ivbsaxattributes_AddRef(IVBSAXAttributes* iface) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| return IVBSAXLocator_AddRef(&locator->IVBSAXLocator_iface); | |
| } | |
| static ULONG WINAPI ivbsaxattributes_Release(IVBSAXAttributes* iface) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| return IVBSAXLocator_Release(&locator->IVBSAXLocator_iface); | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_GetTypeInfoCount(IVBSAXAttributes *iface, UINT *count) | |
| { | |
| TRACE("%p, %p.\n", iface, count); | |
| *count = 1; | |
| return S_OK; | |
| } | |
| 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) | |
| { | |
| ITypeInfo *typeinfo; | |
| HRESULT hr; | |
| TRACE("%p, %s, %p, %u, %lx %p.\n", iface, debugstr_guid(riid), rgszNames, cNames, | |
| lcid, rgDispId); | |
| if(!rgszNames || cNames == 0 || !rgDispId) | |
| return E_INVALIDARG; | |
| hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo); | |
| if(SUCCEEDED(hr)) | |
| { | |
| hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); | |
| ITypeInfo_Release(typeinfo); | |
| } | |
| return hr; | |
| } | |
| 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, flags, pDispParams, pVarResult, ei, arg_err); | |
| hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo); | |
| if (SUCCEEDED(hr)) | |
| { | |
| hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, flags, pDispParams, pVarResult, ei, arg_err); | |
| ITypeInfo_Release(typeinfo); | |
| } | |
| return hr; | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_get_length(IVBSAXAttributes *iface, int *length) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| TRACE("%p, %p.\n", iface, length); | |
| *length = locator->attributes.count; | |
| return S_OK; | |
| } | |
| static const struct attribute *saxlocator_get_attribute(const struct saxlocator *locator, int index) | |
| { | |
| 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; | |
| } | |
| 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; | |
| 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 index, BSTR *name) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p.\n", iface, index, name); | |
| if (!name) | |
| return E_POINTER; | |
| *name = NULL; | |
| 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 index, BSTR *name) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p.\n", iface, index, name); | |
| if (!name) | |
| return E_POINTER; | |
| *name = NULL; | |
| if (!(attr = saxlocator_get_attribute(locator, index))) | |
| return E_INVALIDARG; | |
| return return_bstrn(attr->qname, SysStringLen(attr->qname), name); | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_getIndexFromName(IVBSAXAttributes *iface, BSTR uri, | |
| BSTR name, int *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 name, int *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 index, BSTR *type) | |
| { | |
| FIXME("%p, %d, %p stub\n", iface, index, type); | |
| if (!type) | |
| return E_POINTER; | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_getTypeFromName(IVBSAXAttributes *iface, BSTR uri, BSTR name, BSTR *type) | |
| { | |
| FIXME("%p, %s, %s, %p stub\n", iface, debugstr_w(uri), debugstr_w(name), type); | |
| if (!type) | |
| return E_POINTER; | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_getTypeFromQName(IVBSAXAttributes *iface, BSTR name, BSTR *type) | |
| { | |
| FIXME("%p, %s, %p stub\n", iface, debugstr_w(name), type); | |
| if (!type) | |
| return E_POINTER; | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI ivbsaxattributes_getValue(IVBSAXAttributes *iface, int index, BSTR *value) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p.\n", iface, index, value); | |
| if (!value) | |
| return E_POINTER; | |
| *value = NULL; | |
| 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 name, BSTR *value) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| const struct attribute *attr; | |
| int index; | |
| TRACE("%p, %s, %s, %p.\n", iface, debugstr_w(uri), debugstr_w(name), value); | |
| if (!value) | |
| return E_POINTER; | |
| *value = NULL; | |
| 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 name, BSTR *value) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXAttributes(iface); | |
| const struct attribute *attr; | |
| int index; | |
| TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); | |
| if (!value) | |
| return E_POINTER; | |
| *value = NULL; | |
| 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 = | |
| { | |
| ivbsaxattributes_QueryInterface, | |
| ivbsaxattributes_AddRef, | |
| ivbsaxattributes_Release, | |
| ivbsaxattributes_GetTypeInfoCount, | |
| ivbsaxattributes_GetTypeInfo, | |
| ivbsaxattributes_GetIDsOfNames, | |
| ivbsaxattributes_Invoke, | |
| ivbsaxattributes_get_length, | |
| ivbsaxattributes_getURI, | |
| ivbsaxattributes_getLocalName, | |
| ivbsaxattributes_getQName, | |
| ivbsaxattributes_getIndexFromName, | |
| ivbsaxattributes_getIndexFromQName, | |
| ivbsaxattributes_getType, | |
| ivbsaxattributes_getTypeFromName, | |
| ivbsaxattributes_getTypeFromQName, | |
| ivbsaxattributes_getValue, | |
| ivbsaxattributes_getValueFromName, | |
| ivbsaxattributes_getValueFromQName | |
| }; | |
| static HRESULT WINAPI isaxattributes_QueryInterface(ISAXAttributes *iface, REFIID riid, void **obj) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| return ISAXLocator_QueryInterface(&locator->ISAXLocator_iface, riid, obj); | |
| } | |
| static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes *iface) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| return ISAXLocator_AddRef(&locator->ISAXLocator_iface); | |
| } | |
| static ULONG WINAPI isaxattributes_Release(ISAXAttributes *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) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| TRACE("%p, %p.\n", iface, length); | |
| *length = locator->attributes.count; | |
| return S_OK; | |
| } | |
| static HRESULT saxlocator_return_string(BSTR str, const WCHAR **p, int *length) | |
| { | |
| if (!p || !length) | |
| return E_POINTER; | |
| *length = SysStringLen(str); | |
| *p = str; | |
| TRACE("%s:%d\n", debugstr_w(str), *length); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI isaxattributes_getURI(ISAXAttributes *iface, int index, | |
| const WCHAR **url, int *length) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p, %p.\n", iface, index, url, length); | |
| if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; | |
| return saxlocator_return_string(attr->uri, url, length); | |
| } | |
| static HRESULT WINAPI isaxattributes_getLocalName(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->local, name, length); | |
| } | |
| 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 *uri_length, const WCHAR **local, int *local_length, | |
| const WCHAR **qname, int *qname_length) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p, %p, %p, %p, %p, %p.\n", iface, index, uri, uri_length, local, local_length, | |
| qname, qname_length); | |
| if (!(attr = saxlocator_get_attribute(locator, index))) return E_INVALIDARG; | |
| if (!uri || !uri_length || !local || !local_length || !qname || !qname_length) return E_POINTER; | |
| 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 *uri, | |
| int uri_length, const WCHAR *local, int local_length, int *index) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %s, %d, %s, %d, %p.\n", iface, debugstr_w(uri), uri_length, debugstr_w(local), local_length, index); | |
| if (!uri || !local || !index) | |
| return E_POINTER; | |
| attr = saxlocator_get_attribute_by_name(locator, uri, uri_length, local, local_length, index); | |
| return attr ? S_OK : E_INVALIDARG; | |
| } | |
| static HRESULT WINAPI isaxattributes_getIndexFromQName(ISAXAttributes *iface, const WCHAR *name, int length, int *index) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %s, %d, %p.\n", iface, debugstr_w(name), length, index); | |
| if (!name || !index) return E_POINTER; | |
| if (!length) return E_INVALIDARG; | |
| attr = saxlocator_get_attribute_by_qname(locator, name, length, index); | |
| return attr ? S_OK : E_INVALIDARG; | |
| } | |
| static HRESULT WINAPI isaxattributes_getType(ISAXAttributes *iface, int index, const WCHAR **type, | |
| int *length) | |
| { | |
| FIXME("%p, %d, %p, %p stub\n", iface, index, type, length); | |
| return E_NOTIMPL; | |
| } | |
| 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) | |
| { | |
| FIXME("%p, %s, %d, %s, %d, %p, %p stub\n", iface, debugstr_w(uri), uri_length, debugstr_w(name), | |
| name_length, type, type_length); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxattributes_getTypeFromQName(ISAXAttributes *iface, const WCHAR *name, | |
| int name_length, const WCHAR **type, int *type_length) | |
| { | |
| FIXME("%p, %s, %d, %p, %p stub\n", iface, debugstr_w(name), name_length, type, type_length); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes *iface, int index, const WCHAR **value, | |
| int *length) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| TRACE("%p, %d, %p, %p.\n", iface, index, value, length); | |
| 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 *uri, | |
| int uri_length, const WCHAR *name, int name_length, const WCHAR **value, int *length) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| int index; | |
| TRACE("%p, %s, %d, %s, %d, %p, %p.\n", iface, debugstr_w(uri), uri_length, | |
| debugstr_w(name), name_length, value, length); | |
| 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 *name, int name_length, const WCHAR **value, int *length) | |
| { | |
| struct saxlocator *locator = impl_from_ISAXAttributes(iface); | |
| const struct attribute *attr; | |
| int index; | |
| TRACE("%p, %s, %d, %p, %p.\n", iface, debugstr_w(name), name_length, value, length); | |
| 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 = | |
| { | |
| 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 void saxreader_free_bstr(BSTR *str) | |
| { | |
| if (str) | |
| { | |
| SysFreeString(*str); | |
| *str = NULL; | |
| } | |
| } | |
| static void saxreader_clear_attributes(struct saxlocator *locator) | |
| { | |
| for (size_t i = 0; i < locator->attributes.count; ++i) | |
| { | |
| struct attribute *attr = &locator->attributes.entries[i]; | |
| 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 WINAPI ivbsaxlocator_QueryInterface(IVBSAXLocator *iface, REFIID riid, void **obj) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXLocator(iface); | |
| TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); | |
| *obj = NULL; | |
| if (IsEqualGUID(riid, &IID_IUnknown) || | |
| IsEqualGUID(riid, &IID_IDispatch) || | |
| IsEqualGUID(riid, &IID_IVBSAXLocator)) | |
| { | |
| *obj = iface; | |
| } | |
| else if (IsEqualGUID(riid, &IID_IVBSAXAttributes)) | |
| { | |
| *obj = &locator->IVBSAXAttributes_iface; | |
| } | |
| else | |
| { | |
| FIXME("interface %s not implemented\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| IVBSAXLocator_AddRef(iface); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI ivbsaxlocator_AddRef(IVBSAXLocator *iface) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXLocator(iface); | |
| return ISAXLocator_AddRef(&locator->ISAXLocator_iface); | |
| } | |
| static ULONG WINAPI ivbsaxlocator_Release(IVBSAXLocator *iface) | |
| { | |
| struct saxlocator *locator = impl_from_IVBSAXLocator(iface); | |
| return ISAXLocator_Release(&locator->ISAXLocator_iface); | |
| } | |
| static HRESULT WINAPI ivbsaxlocator_GetTypeInfoCount(IVBSAXLocator *iface, UINT *count) | |
| { | |
| TRACE("%p, %p.\n", iface, count); | |
| *count = 1; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI ivbsaxlocator_GetTypeInfo(IVBSAXLocator *iface, UINT index, LCID lcid, ITypeInfo **ti) | |
| { | |
| TRACE("%p, %u, %lx, %p.\n", iface, index, lcid, ti); | |
| return get_typeinfo(IVBSAXLocator_tid, ti); | |
| } | |
| static HRESULT WINAPI ivbsaxlocator_GetIDsOfNames(IVBSAXLocator *iface, REFIID riid, LPOLESTR *names, | |
| UINT count, LCID lcid, DISPID *dispid) | |
| { | |
| ITypeInfo *typeinfo; | |
| HRESULT hr; | |
| 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 void saxreader_clear_strings(struct saxlocator *locator) | |
| { | |
| for (int i = 0; i < ARRAYSIZE(locator->strings.typenames); ++i) | |
| SysFreeString(locator->strings.typenames[i]); | |
| for (int i = 0; i < ARRAYSIZE(locator->strings.valuenames); ++i) | |
| SysFreeString(locator->strings.valuenames[i]); | |
| SysFreeString(locator->strings.xmlns_uri); | |
| SysFreeString(locator->strings.xml_uri); | |
| SysFreeString(locator->strings.null_uri); | |
| } | |
| 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; | |
| saxreader_clear_attributes(locator); | |
| free(locator->attributes.entries); | |
| saxreader_clear_strings(locator); | |
| if (locator->dtd) | |
| parser_dtd_release(locator->dtd); | |
| locator->dtd = NULL; | |
| /* 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->strings.xml_uri = saxreader_alloc_string(locator, L"http://www.w3.org/XML/1998/namespace"); | |
| locator->strings.null_uri = saxreader_alloc_stringlen(locator, NULL, 0); | |
| if (locator->saxreader->version >= MSXML6) | |
| locator->strings.xmlns_uri = saxreader_alloc_string(locator, L"http://www.w3.org/2000/xmlns/"); | |
| else | |
| locator->strings.xmlns_uri = saxreader_alloc_stringlen(locator, NULL, 0); | |
| list_init(&locator->elements); | |
| 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 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; | |
| } | |
| static bool saxreader_limit_xml_size(struct saxlocator *locator, ULONG read) | |
| { | |
| if (locator->saxreader && 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; | |
| 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; | |
| } | |
| if (!saxreader_limit_xml_size(locator, *read)) | |
| 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_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_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 (locator->collect) | |
| saxreader_string_append(locator, &locator->scratch, &ch, 1); | |
| 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 | FORMAT_MESSAGE_FROM_HMODULE, MSXML_hInstance, locator->status, 0, | |
| msg, ARRAY_SIZE(msg), NULL)) | |
| { | |
| *msg = 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 parsed_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_xmldecl(struct saxlocator *locator) | |
| { | |
| struct saxextensionhandler_iface *handler = saxreader_get_extensionhandler(locator->saxreader); | |
| HRESULT hr = S_OK; | |
| if (locator->status != S_OK) | |
| return; | |
| if (!locator->saxreader->xmldecl_version) | |
| return; | |
| if (saxreader_has_handler(locator, SAXExtensionHandler)) | |
| { | |
| if (!locator->vbInterface) | |
| hr = ISAXExtensionHandler_xmldecl(handler->handler, | |
| locator->saxreader->xmldecl_version, | |
| locator->saxreader->xmldecl_encoding, | |
| locator->saxreader->xmldecl_standalone); | |
| } | |
| locator->status = saxlocator_callback_result(locator, 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); | |
| int max_depth = locator->saxreader->max_element_depth; | |
| MSXML_VERSION version = locator->saxreader->version; | |
| BSTR uri = NULL, local = NULL; | |
| HRESULT hr; | |
| if (locator->status != S_OK) | |
| return; | |
| ++locator->depth; | |
| if (max_depth && locator->depth > max_depth) | |
| return saxreader_set_error(locator, version < MSXML6 ? E_ABORT : E_SAX_MAX_ELEMENT_DEPTH); | |
| if (!saxreader_has_handler(locator, SAXContentHandler)) | |
| return; | |
| locator->line = position->line; | |
| locator->column = position->column; | |
| /* Point to the closing '>' */ | |
| if (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->depth; | |
| 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 saxreader_element_decl_occurence_stringify(struct saxlocator *locator, struct string_buffer *buffer, | |
| struct element_content *c) | |
| { | |
| if (c->occurrence == ELEMENT_CONTENT_OPT) | |
| saxreader_string_append(locator, buffer, L"?", 1); | |
| else if (c->occurrence == ELEMENT_CONTENT_MULT) | |
| saxreader_string_append(locator, buffer, L"*", 1); | |
| else if (c->occurrence == ELEMENT_CONTENT_PLUS) | |
| saxreader_string_append(locator, buffer, L"+", 1); | |
| } | |
| static void saxreader_element_decl_content_stringify(struct saxlocator *locator, struct string_buffer *buffer, | |
| struct element_decl *decl) | |
| { | |
| struct element_content *cur, *content; | |
| saxreader_string_append(locator, buffer, L"(", 1); | |
| cur = content = decl->content; | |
| do | |
| { | |
| if (!cur) return; | |
| switch (cur->type) | |
| { | |
| case ELEMENT_CONTENT_PCDATA: | |
| saxreader_string_append(locator, buffer, L"#PCDATA", 7); | |
| break; | |
| case ELEMENT_CONTENT_ELEMENT: | |
| saxreader_string_append(locator, buffer, cur->name, SysStringLen(cur->name)); | |
| break; | |
| case ELEMENT_CONTENT_SEQ: | |
| case ELEMENT_CONTENT_OR: | |
| if ((cur != decl->content) && cur->parent && | |
| ((cur->type != cur->parent->type) || (cur->occurrence != ELEMENT_CONTENT_ONCE))) | |
| { | |
| saxreader_string_append(locator, buffer, L"(", 1); | |
| } | |
| cur = cur->left; | |
| continue; | |
| default: | |
| ; | |
| } | |
| while (cur != content) | |
| { | |
| struct element_content *parent = cur->parent; | |
| if (!parent) return; | |
| if (((cur->type == ELEMENT_CONTENT_OR) || | |
| (cur->type == ELEMENT_CONTENT_SEQ)) && | |
| ((cur->type != parent->type) || | |
| (cur->occurrence != ELEMENT_CONTENT_ONCE))) | |
| { | |
| saxreader_string_append(locator, buffer, L")", 1); | |
| } | |
| saxreader_element_decl_occurence_stringify(locator, buffer, cur); | |
| if (cur == parent->left) | |
| { | |
| if (parent->type == ELEMENT_CONTENT_SEQ) | |
| saxreader_string_append(locator, buffer, L",", 1); | |
| else if (parent->type == ELEMENT_CONTENT_OR) | |
| saxreader_string_append(locator, buffer, L"|", 1); | |
| cur = parent->right; | |
| break; | |
| } | |
| cur = parent; | |
| } | |
| } | |
| while (cur != content); | |
| saxreader_string_append(locator, buffer, L")", 1); | |
| saxreader_element_decl_occurence_stringify(locator, buffer, content); | |
| } | |
| static BSTR saxreader_element_decl_model_stringify(struct saxlocator *locator, struct element_decl *decl) | |
| { | |
| struct string_buffer buffer = { 0 }; | |
| if (decl->type == ELEMENT_TYPE_EMPTY) | |
| saxreader_string_append(locator, &buffer, L"EMPTY", 5); | |
| else if (decl->type == ELEMENT_TYPE_ANY) | |
| saxreader_string_append(locator, &buffer, L"ANY", 3); | |
| else | |
| saxreader_element_decl_content_stringify(locator, &buffer, decl); | |
| return saxreader_string_to_bstr(locator, &buffer); | |
| } | |
| static void saxlocator_elementdecl(struct saxlocator *locator, struct element_decl *decl) | |
| { | |
| struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); | |
| HRESULT hr; | |
| BSTR model; | |
| 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; | |
| model = saxreader_element_decl_model_stringify(locator, decl); | |
| if (locator->vbInterface) | |
| hr = IVBSAXDeclHandler_elementDecl(handler->vbhandler, &decl->name, &model); | |
| else | |
| hr = ISAXDeclHandler_elementDecl(handler->handler, decl->name, SysStringLen(decl->name), model, SysStringLen(model)); | |
| SysFreeString(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 BSTR saxreader_attribute_type_stringify(struct saxlocator *locator, struct attlist_attr *attr) | |
| { | |
| struct string_buffer buffer = { 0 }; | |
| bool separate = false; | |
| if (attr->type == ATTR_TYPE_NOTATION) | |
| saxreader_string_append(locator, &buffer, L"NOTATION ", 9); | |
| saxreader_string_append(locator, &buffer, L"(", 1); | |
| for (size_t i = 0; i < attr->valuelist.count; ++i) | |
| { | |
| if (separate) | |
| saxreader_string_append(locator, &buffer, L"|", 1); | |
| saxreader_string_append(locator, &buffer, attr->valuelist.values[i], SysStringLen(attr->valuelist.values[i])); | |
| separate = true; | |
| } | |
| saxreader_string_append(locator, &buffer, L")", 1); | |
| return saxreader_string_to_bstr(locator, &buffer); | |
| } | |
| static void saxlocator_attribute_decl(struct saxlocator *locator, struct attlist_decl *decl) | |
| { | |
| struct saxdeclhandler_iface *handler = saxreader_get_declhandler(locator->saxreader); | |
| BSTR typename, tofree, valuetype; | |
| 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; | |
| saxreader_ensure_dtd_typenames(locator); | |
| if (locator->status != S_OK) | |
| return; | |
| for (size_t i = 0; i < decl->count && locator->status == S_OK; ++i) | |
| { | |
| tofree = NULL; | |
| if (decl->attributes[i].type == ATTR_TYPE_NOTATION | |
| || decl->attributes[i].type == ATTR_TYPE_ENUMERATION) | |
| { | |
| typename = tofree = saxreader_attribute_type_stringify(locator, &decl->attributes[i]); | |
| } | |
| else | |
| { | |
| typename = locator->strings.typenames[decl->attributes[i].type]; | |
| } | |
| valuetype = locator->strings.valuenames[decl->attributes[i].valuetype]; | |
| if (locator->vbInterface) | |
| hr = IVBSAXDeclHandler_attributeDecl(handler->vbhandler, &decl->name, | |
| &decl->attributes[i].name.qname, &typename, &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), | |
| typename, SysStringLen(typename), valuetype, SysStringLen(valuetype), | |
| decl->attributes[i].value, SysStringLen(decl->attributes[i].value)); | |
| SysFreeString(tofree); | |
| 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_extension_dtd(struct saxlocator *locator) | |
| { | |
| struct saxextensionhandler_iface *handler = saxreader_get_extensionhandler(locator->saxreader); | |
| HRESULT hr = S_OK; | |
| BSTR str; | |
| if (locator->status != S_OK) | |
| return; | |
| str = saxreader_string_to_bstr(locator, &locator->scratch); | |
| if (saxreader_has_handler(locator, SAXExtensionHandler)) | |
| { | |
| if (!locator->vbInterface) | |
| hr = ISAXExtensionHandler_dtd(handler->handler, str, locator->dtd); | |
| } | |
| SysFreeString(str); | |
| locator->collect = false; | |
| 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); | |
| } | |
| 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; | |
| } | |
| /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ | |
| static bool saxreader_is_space(WCHAR ch) | |
| { | |
| return xml_is_space(ch); | |
| } | |
| /* [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; | |
| } | |
| 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 '"') */ | |
| /* [26] VersionNum ::= '1.' [0-9]+ */ | |
| static void saxreader_parse_versioninfo(struct saxlocator *locator, const struct parsed_name *name, BSTR value) | |
| { | |
| if (wcscmp(name->qname, L"version")) | |
| return saxreader_set_error(locator, E_SAX_UNEXPECTED_ATTRIBUTE); | |
| if (wcscmp(value, L"1.0")) | |
| return saxreader_set_error(locator, E_SAX_INVALID_VERSION); | |
| SysFreeString(locator->saxreader->xmldecl_version); | |
| locator->saxreader->xmldecl_version = saxreader_alloc_string(locator, L"1.0"); | |
| } | |
| 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, const struct parsed_name *name, BSTR value) | |
| { | |
| UINT codepage; | |
| if (wcscmp(name->qname, L"encoding")) | |
| return saxreader_set_error(locator, E_SAX_UNEXPECTED_ATTRIBUTE); | |
| locator->saxreader->xmldecl_encoding = saxreader_alloc_string(locator, value); | |
| 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)); | |
| saxreader_set_error(locator, E_SAX_INVALIDENCODING); | |
| } | |
| } | |
| } | |
| /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */ | |
| static void saxreader_parse_sddecl(struct saxlocator *locator, const struct parsed_name *name, BSTR value) | |
| { | |
| if (wcscmp(name->qname, L"standalone")) | |
| return saxreader_set_error(locator, E_SAX_UNEXPECTED_ATTRIBUTE); | |
| if (!wcscmp(value, L"yes") || !wcscmp(value, L"no")) | |
| locator->saxreader->xmldecl_standalone = saxreader_alloc_string(locator, value); | |
| else | |
| saxreader_set_error(locator, E_SAX_INVALID_STANDALONE); | |
| } | |
| /* | |
| static bool 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 false; | |
| saxreader_skip(locator, 5); | |
| saxreader_skipspaces(locator); | |
| saxreader_parse_versioninfo(locator); | |
| if (saxreader_cmp(locator, L"?>")) | |
| return true; | |
| saxreader_parse_encdecl(locator); | |
| if (locator->saxreader->xmldecl_encoding) | |
| { | |
| if (saxreader_cmp(locator, L"?>")) | |
| return true; | |
| 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); | |
| return true; | |
| } | |
| */ | |
| static BSTR saxreader_parse_xmldecl_attribute(struct saxlocator *locator, struct parsed_name *name); | |
| /* [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 parsed_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 const WCHAR * validate_ncname(const WCHAR *name) | |
| { | |
| const WCHAR *p = name; | |
| if (!saxreader_is_ncname_startchar(*p++)) | |
| return NULL; | |
| while (*p && *p != ':') | |
| { | |
| if (!saxreader_is_ncnamechar(*p)) return NULL; | |
| ++p; | |
| } | |
| return p; | |
| } | |
| bool parser_is_valid_qualified_name(const WCHAR *name) | |
| { | |
| const WCHAR *p; | |
| p = validate_ncname(name); | |
| if (!p) | |
| return false; | |
| if (*p == ':') | |
| p = validate_ncname(++p); | |
| return p && !*p; | |
| } | |
| static struct element *saxreader_new_element(struct saxlocator *locator, struct parsed_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_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 parsed_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; | |
| if (!locator->dtd) | |
| return; | |
| 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 (!attr->value) | |
| continue; | |
| 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->strings.xmlns_uri; | |
| } | |
| else if (!attr->prefix) | |
| { | |
| attr->uri = locator->strings.null_uri; | |
| } | |
| else if (!wcscmp(attr->prefix, L"xml")) | |
| { | |
| attr->uri = locator->strings.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]+ ';' | '&#x' [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); | |
| } | |
| } | |
| /* 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; | |
| if (locator->dtd) | |
| { | |
| LIST_FOR_EACH_ENTRY(entity, &locator->dtd->entities, struct entity_decl, entry) | |
| { | |
| if (bstr_equal(entity->name, name)) | |
| return entity; | |
| } | |
| } | |
| 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) | |
| { | |
| const struct entity_part *part = &entity->content.parts[i]; | |
| 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)); | |
| } | |
| } | |
| } | |
| /* [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | |
| | "'" ([^<&'] | Reference)* "'" */ | |
| static BSTR saxreader_parse_attvalue(struct saxlocator *locator) | |
| { | |
| struct string_buffer buffer = { 0 }; | |
| struct text_position position; | |
| WCHAR quote[2] = { 0 }, ch; | |
| bool normalized; | |
| BSTR name; | |
| if (!(quote[0] = saxreader_is_quote(locator))) | |
| { | |
| saxreader_set_error(locator, E_SAX_MISSINGQUOTE); | |
| return NULL; | |
| } | |
| saxreader_skip(locator, 1); | |
| /* All references are resolved */ | |
| while (locator->status == S_OK) | |
| { | |
| 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 | |
| { | |
| normalized = false; | |
| if (locator->saxreader->features & NormalizeAttributeValues) | |
| { | |
| if (saxreader_cmp(locator, L"\r\n") | |
| || saxreader_cmp(locator, L"\r") | |
| || saxreader_cmp(locator, L"\n") | |
| || saxreader_cmp(locator, L"\t")) | |
| { | |
| saxreader_string_append(locator, &buffer, L" ", 1); | |
| normalized = true; | |
| } | |
| } | |
| else | |
| { | |
| /* Potentially could happen on read */ | |
| if (saxreader_cmp(locator, L"\r\n")) | |
| { | |
| saxreader_string_append(locator, &buffer, L"\n", 1); | |
| normalized = true; | |
| } | |
| } | |
| if (!normalized) | |
| { | |
| ch = *saxreader_get_ptr_noread(locator); | |
| if (!ch) | |
| { | |
| saxreader_set_error(locator, E_SAX_BADCHARINSTRING); | |
| break; | |
| } | |
| 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 parsed_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 parsed_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); | |
| saxreader_free_name(&name); | |
| SysFreeString(value); | |
| } | |
| static BSTR saxreader_parse_xmldecl_attribute(struct saxlocator *locator, struct parsed_name *name) | |
| { | |
| BSTR value; | |
| 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); | |
| return value; | |
| } | |
| /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */ | |
| static void saxreader_parse_xmldecl(struct saxlocator *locator) | |
| { | |
| struct parsed_name name; | |
| bool done = false; | |
| BSTR value; | |
| 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); | |
| value = saxreader_parse_xmldecl_attribute(locator, &name); | |
| saxreader_parse_versioninfo(locator, &name, value); | |
| saxreader_free_name(&name); | |
| SysFreeString(value); | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L"?>")) | |
| return; | |
| while (!done && locator->status == S_OK) | |
| { | |
| value = saxreader_parse_xmldecl_attribute(locator, &name); | |
| if (!wcscmp(name.qname, L"encoding")) | |
| { | |
| saxreader_parse_encdecl(locator, &name, value); | |
| } | |
| else if (!wcscmp(name.qname, L"standalone")) | |
| { | |
| saxreader_parse_sddecl(locator, &name, value); | |
| done = true; | |
| } | |
| else | |
| { | |
| saxreader_set_error(locator, E_SAX_UNEXPECTED_ATTRIBUTE); | |
| continue; | |
| } | |
| saxreader_free_name(&name); | |
| SysFreeString(value); | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L"?>")) | |
| return; | |
| } | |
| saxreader_skipspaces(locator); | |
| if (!saxreader_cmp(locator, L"?>")) | |
| saxreader_set_error(locator, E_SAX_BAD_XMLDECL); | |
| } | |
| 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; | |
| } | |
| } | |
| /* [12 NS] STag ::= '<' QName (S Attribute)* S? '>' | |
| [14 NS] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>' */ | |
| static void saxreader_parse_starttag(struct saxlocator *locator) | |
| { | |
| struct text_position position = { 0 }; | |
| bool empty_element = false; | |
| struct element *element; | |
| struct parsed_name name; | |
| if (!saxreader_cmp(locator, L"<")) | |
| return saxreader_set_error(locator, E_SAX_INVALIDATROOTLEVEL); | |
| saxreader_parse_qname(locator, &name); | |
| if (bstr_startswith(name.prefix, L"xml", 3)) | |
| { | |
| 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); | |
| if (saxreader_cmp(locator, L"/>")) | |
| { | |
| empty_element = true; | |
| position = locator->buffer.position; | |
| break; | |
| } | |
| 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; | |
| struct parsed_name name; | |
| HRESULT hr = S_OK; | |
| /* 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; | |
| if (SUCCEEDED(hr)) | |
| { | |
| element = LIST_ENTRY(list_head(&locator->elements), struct element, entry); | |
| if (!wcscmp(element->name.qname, name.qname)) | |
| saxlocator_end_element(locator, &position, element); | |
| else | |
| 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; | |
| } | |
| if (locator->saxreader->features & NormalizeLineBreaks | |
| && (saxreader_cmp(locator, L"\r\n") || saxreader_cmp(locator, L"\r"))) | |
| { | |
| saxreader_string_append(locator, &buffer, L"\n", 1); | |
| } | |
| else | |
| { | |
| 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; | |
| 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"--")) | |
| { | |
| saxreader_set_error(locator, E_SAX_COMMENTSYNTAX); | |
| break; | |
| } | |
| else | |
| { | |
| if (locator->saxreader->features & NormalizeLineBreaks | |
| && (saxreader_cmp(locator, L"\r\n") || saxreader_cmp(locator, L"\r"))) | |
| { | |
| saxreader_string_append(locator, &buffer, L"\n", 1); | |
| } | |
| else | |
| { | |
| saxreader_string_append(locator, &buffer, &ch, 1); | |
| saxreader_skip(locator, 1); | |
| } | |
| } | |
| ch = *saxreader_get_ptr_noread(locator); | |
| } | |
| free(buffer.data); | |
| saxreader_set_error(locator, E_SAX_UNEXPECTED_EOF); | |
| } | |
| static bool saxreader_predefined_entity(struct saxlocator *locator, const struct text_position *position, BSTR name) | |
| { | |
| const WCHAR *value; | |
| BSTR str; | |
| if (locator->status != S_OK) | |
| return false; | |
| 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) | |
| { | |
| 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)) | |
| { | |
| 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]+ ';' | '&#x' [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 | |
| { | |
| /* 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 ((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); | |
| } | |
| } | |
| } | |
| SysFreeString(str); | |
| } | |
| struct chardata_context | |
| { | |
| struct string_buffer buffer; | |
| struct text_position position; | |
| size_t count; | |
| WCHAR ch; | |
| }; | |
| static void saxreader_parse_characters(struct saxlocator *locator, struct chardata_context *ctxt) | |
| { | |
| BSTR chars; | |
| /* Normalize line breaks */ | |
| if (ctxt->ch == '\r') | |
| { | |
| 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); | |
| 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 | |
| { | |
| saxreader_string_append(locator, &ctxt->buffer, &ctxt->ch, 1); | |
| saxreader_skip(locator, 1); | |
| } | |
| 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') | |
| { | |
| 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 | |
| { | |
| 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->features & NormalizeLineBreaks) | |
| { | |
| if (locator->saxreader->version >= MSXML4) | |
| saxreader_parse_characters_newparser(locator, &context); | |
| else | |
| saxreader_parse_characters(locator, &context); | |
| } | |
| else | |
| { | |
| saxreader_string_append(locator, &context.buffer, &context.ch, 1); | |
| saxreader_skip(locator, 1); | |
| context.ch = *saxreader_get_ptr(locator); | |
| } | |
| } | |
| 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->features & NormalizeLineBreaks) | |
| { | |
| if (locator->saxreader->version >= MSXML4) | |
| saxreader_parse_characters_newparser(locator, &context); | |
| else | |
| saxreader_parse_characters(locator, &context); | |
| } | |
| else | |
| { | |
| saxreader_string_append(locator, &context.buffer, &context.ch, 1); | |
| saxreader_skip(locator, 1); | |
| context.ch = *saxreader_get_ptr(locator); | |
| } | |
| } | |
| free(context.buffer.data); | |
| saxreader_set_error(locator, E_SAX_UNCLOSEDCDATA); | |
| } | |
| static struct element_content *saxreader_new_element_content(struct saxlocator *locator, | |
| BSTR *name, enum element_content_type type) | |
| { | |
| struct element_content *ret; | |
| if (locator->status != S_OK) | |
| { | |
| saxreader_free_bstr(name); | |
| return NULL; | |
| } | |
| if (!(ret = calloc(1, sizeof(*ret)))) | |
| { | |
| saxreader_free_bstr(name); | |
| saxreader_set_error(locator, E_OUTOFMEMORY); | |
| return NULL; | |
| } | |
| if (name) | |
| { | |
| ret->name = *name; | |
| *name = NULL; | |
| } | |
| ret->type = type; | |
| ret->occurrence = ELEMENT_CONTENT_ONCE; | |
| return ret; | |
| } | |
| static void saxreader_link_element_content_part(struct element_content *parent, struct element_content *child, bool left) | |
| { | |
| if (left) | |
| parent->left = child; | |
| else | |
| parent->right = child; | |
| if (child) | |
| child->parent = parent; | |
| } | |
| /* [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' */ | |
| static void saxreader_parse_mixed_contentspec(struct saxlocator *locator, struct element_decl *decl) | |
| { | |
| struct element_content *ret = NULL, *cur = NULL, *n, *left; | |
| BSTR name = NULL; | |
| WCHAR ch; | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L")")) | |
| { | |
| ret = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_PCDATA); | |
| if (saxreader_cmp(locator, L"*")) | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| } | |
| else | |
| { | |
| ch = *saxreader_get_ptr_noread(locator); | |
| if (ch == '(' || ch == '|') | |
| ret = cur = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_PCDATA); | |
| while (ch == '|' && locator->status == S_OK) | |
| { | |
| saxreader_skip(locator, 1); | |
| if (name) | |
| { | |
| n = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_OR); | |
| left = saxreader_new_element_content(locator, &name, ELEMENT_CONTENT_ELEMENT); | |
| saxreader_link_element_content_part(n, left, true); | |
| saxreader_link_element_content_part(cur, n, false); | |
| cur = n; | |
| } | |
| else | |
| { | |
| ret = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_OR); | |
| saxreader_link_element_content_part(ret, cur, true); | |
| cur = ret; | |
| } | |
| saxreader_skipspaces(locator); | |
| saxreader_parse_name(locator, &name); | |
| saxreader_skipspaces(locator); | |
| ch = *saxreader_get_ptr_noread(locator); | |
| } | |
| 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); | |
| if (name) | |
| { | |
| cur->right = saxreader_new_element_content(locator, &name, ELEMENT_CONTENT_ELEMENT); | |
| if (cur->right) cur->right->parent = cur; | |
| } | |
| if (ret) | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| } | |
| if (locator->status == S_OK) | |
| { | |
| decl->content = ret; | |
| decl->type = ELEMENT_TYPE_MIXED; | |
| } | |
| else | |
| { | |
| if (ret) | |
| saxreader_free_element_content(ret); | |
| decl->type = ELEMENT_TYPE_UNDEFINED; | |
| } | |
| } | |
| static struct element_content * saxreader_parse_children_contentspec(struct saxlocator *locator); | |
| static struct element_content * saxreader_parse_child_contentspec(struct saxlocator *locator) | |
| { | |
| struct element_content *ret; | |
| BSTR name; | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L"(")) | |
| { | |
| ret = saxreader_parse_children_contentspec(locator); | |
| saxreader_skipspaces(locator); | |
| } | |
| else | |
| { | |
| saxreader_parse_name(locator, &name); | |
| ret = saxreader_new_element_content(locator, &name, ELEMENT_CONTENT_ELEMENT); | |
| if (!ret) | |
| return NULL; | |
| if (saxreader_cmp(locator, L"?")) | |
| ret->occurrence = ELEMENT_CONTENT_OPT; | |
| else if (saxreader_cmp(locator, L"*")) | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| else if (saxreader_cmp(locator, L"+")) | |
| ret->occurrence = ELEMENT_CONTENT_PLUS; | |
| else | |
| ret->occurrence = ELEMENT_CONTENT_ONCE; | |
| } | |
| saxreader_skipspaces(locator); | |
| return ret; | |
| } | |
| static struct element_content * saxreader_free_element_content_pair(struct element_content *c1, struct element_content *c2) | |
| { | |
| if (c1 && c1 != c2) | |
| saxreader_free_element_content(c1); | |
| if (c2) | |
| saxreader_free_element_content(c2); | |
| return NULL; | |
| } | |
| static struct element_content * saxreader_parse_children_contentspec(struct saxlocator *locator) | |
| { | |
| struct element_content *ret = NULL, *cur = NULL, *last = NULL, *op = NULL; | |
| WCHAR ch, sep = 0; | |
| /* TODO: limit recursion depth */ | |
| if (locator->status != S_OK) | |
| return NULL; | |
| cur = ret = saxreader_parse_child_contentspec(locator); | |
| if (!cur) | |
| return NULL; | |
| ch = *saxreader_get_ptr(locator); | |
| while (ch != ')' && locator->status == S_OK) | |
| { | |
| if (ch == ',') | |
| { | |
| /* Check for "Name | Name , Name" */ | |
| if (sep == 0) sep = ch; | |
| else if (sep != ch) | |
| { | |
| ret = saxreader_free_element_content_pair(last, ret); | |
| saxreader_set_error(locator, E_SAX_INVALID_MODEL); | |
| break; | |
| } | |
| saxreader_skip(locator, 1); | |
| op = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_SEQ); | |
| if (!op) | |
| { | |
| ret = saxreader_free_element_content_pair(last, ret); | |
| break; | |
| } | |
| if (!last) | |
| { | |
| saxreader_link_element_content_part(op, ret, true); | |
| ret = cur = op; | |
| } | |
| else | |
| { | |
| saxreader_link_element_content_part(cur, op, false); | |
| saxreader_link_element_content_part(op, last, true); | |
| cur = op; | |
| last = NULL; | |
| } | |
| } | |
| else if (ch == '|') | |
| { | |
| /* Check for "Name , Name | Name" */ | |
| if (sep == 0) sep = ch; | |
| else if (sep != ch) | |
| { | |
| ret = saxreader_free_element_content_pair(last, ret); | |
| saxreader_set_error(locator, E_SAX_INVALID_MODEL); | |
| break; | |
| } | |
| saxreader_skip(locator, 1); | |
| op = saxreader_new_element_content(locator, NULL, ELEMENT_CONTENT_OR); | |
| if (!op) | |
| { | |
| ret = saxreader_free_element_content_pair(last, ret); | |
| break; | |
| } | |
| if (!last) | |
| { | |
| saxreader_link_element_content_part(op, ret, true); | |
| ret = cur = op; | |
| } | |
| else | |
| { | |
| saxreader_link_element_content_part(cur, op, false); | |
| saxreader_link_element_content_part(op, last, true); | |
| cur = op; | |
| last = NULL; | |
| } | |
| } | |
| else | |
| { | |
| ret = saxreader_free_element_content_pair(last, ret); | |
| saxreader_set_error(locator, E_SAX_INVALID_MODEL); | |
| break; | |
| } | |
| if (!(last = saxreader_parse_child_contentspec(locator))) | |
| { | |
| ret = saxreader_free_element_content(ret); | |
| break; | |
| } | |
| ch = *saxreader_get_ptr(locator); | |
| } | |
| if (!ret) | |
| return NULL; | |
| /* Closing ')' */ | |
| saxreader_skip(locator, 1); | |
| if (cur && last) | |
| saxreader_link_element_content_part(cur, last, false); | |
| if (saxreader_cmp(locator, L"?")) | |
| { | |
| if (ret->occurrence == ELEMENT_CONTENT_PLUS | |
| || ret->occurrence == ELEMENT_CONTENT_MULT) | |
| { | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| } | |
| else | |
| { | |
| ret->occurrence = ELEMENT_CONTENT_OPT; | |
| } | |
| } | |
| else if (saxreader_cmp(locator, L"*")) | |
| { | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| } | |
| else if (saxreader_cmp(locator, L"+")) | |
| { | |
| if (ret->occurrence == ELEMENT_CONTENT_PLUS | |
| || ret->occurrence == ELEMENT_CONTENT_MULT) | |
| { | |
| ret->occurrence = ELEMENT_CONTENT_MULT; | |
| } | |
| else | |
| { | |
| ret->occurrence = ELEMENT_CONTENT_PLUS; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* [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, struct element_decl *decl) | |
| { | |
| decl->content = NULL; | |
| if (saxreader_cmp(locator, L"EMPTY")) | |
| { | |
| decl->type = ELEMENT_TYPE_EMPTY; | |
| } | |
| else if (saxreader_cmp(locator, L"ANY")) | |
| { | |
| decl->type = ELEMENT_TYPE_ANY; | |
| } | |
| else if (saxreader_cmp(locator, L"(")) | |
| { | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L"#PCDATA")) | |
| return saxreader_parse_mixed_contentspec(locator, decl); | |
| else | |
| { | |
| decl->content = saxreader_parse_children_contentspec(locator); | |
| decl->type = ELEMENT_TYPE_ELEMENT; | |
| } | |
| } | |
| else | |
| { | |
| saxreader_set_error(locator, E_SAX_INVALID_MODEL); | |
| decl->type = ELEMENT_TYPE_UNDEFINED; | |
| } | |
| } | |
| /* [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' */ | |
| static void saxreader_parse_elementdecl(struct saxlocator *locator) | |
| { | |
| struct element_decl *decl; | |
| if (!(decl = saxreader_calloc(locator, 1, sizeof(*decl)))) | |
| return; | |
| list_add_tail(&locator->dtd->elements, &decl->entry); | |
| /* Skip <!ELEMENT */ | |
| saxreader_skip(locator, 9); | |
| saxreader_skip_required_spaces(locator); | |
| saxreader_parse_name(locator, &decl->name); | |
| saxreader_skip_required_spaces(locator); | |
| saxreader_parse_contentspec(locator, decl); | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L">")) | |
| saxlocator_elementdecl(locator, decl); | |
| else | |
| saxreader_set_error(locator, E_SAX_EXPECTINGTAGEND); | |
| } | |
| /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */ | |
| static bool saxreader_is_pubidchar(WCHAR ch) | |
| { | |
| 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 == '_'); | |
| } | |
| /* [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; | |
| if (!(quote = saxreader_is_quote(locator))) | |
| { | |
| saxreader_set_error(locator, E_SAX_MISSINGQUOTE); | |
| return NULL; | |
| } | |
| saxreader_skip(locator, 1); | |
| saxreader_more(locator); | |
| ch = *saxreader_get_ptr_noread(locator); | |
| while (saxreader_is_pubidchar(ch) && ch != quote) | |
| { | |
| saxreader_string_append(locator, &buffer, &ch, 1); | |
| saxreader_skip(locator, 1); | |
| ch = *saxreader_get_ptr_noread(locator); | |
| } | |
| if (ch == quote) | |
| { | |
| str = saxreader_string_to_bstr(locator, &buffer); | |
| /* Skip closing quote */ | |
| saxreader_skip(locator, 1); | |
| return str; | |
| } | |
| free(buffer.data); | |
| saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); | |
| return NULL; | |
| } | |
| /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */ | |
| static BSTR saxreader_parse_systemliteral(struct saxlocator *locator) | |
| { | |
| struct string_buffer buffer = { 0 }; | |
| WCHAR quote, ch; | |
| if (!(quote = saxreader_is_quote(locator))) | |
| { | |
| saxreader_set_error(locator, E_SAX_MISSINGQUOTE); | |
| return NULL; | |
| } | |
| saxreader_skip(locator, 1); | |
| saxreader_more(locator); | |
| ch = *saxreader_get_ptr_noread(locator); | |
| while (ch) | |
| { | |
| if (ch == quote) | |
| { | |
| 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); | |
| } | |
| free(buffer.data); | |
| saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); | |
| return NULL; | |
| } | |
| /* [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) | |
| { | |
| *systemid = *publicid = NULL; | |
| if (saxreader_cmp(locator, L"SYSTEM")) | |
| { | |
| if (!saxreader_skip_required_spaces(locator)) | |
| return; | |
| *systemid = saxreader_parse_systemliteral(locator); | |
| } | |
| else if (saxreader_cmp(locator, L"PUBLIC")) | |
| { | |
| if (!saxreader_skip_required_spaces(locator)) | |
| return; | |
| *publicid = saxreader_parse_pubidliteral(locator); | |
| if (is_notation) | |
| { | |
| if (!saxreader_skipspaces(locator)) | |
| return; | |
| if (!saxreader_is_quote(locator)) | |
| return; | |
| } | |
| else | |
| { | |
| if (!saxreader_skip_required_spaces(locator)) | |
| { | |
| SysFreeString(*publicid); | |
| *publicid = NULL; | |
| return; | |
| } | |
| } | |
| *systemid = saxreader_parse_systemliteral(locator); | |
| } | |
| } | |
| /* Read EntityRef without much validation */ | |
| static void saxreader_parse_entityvalue_entityref(struct saxlocator *locator, struct string_buffer *buffer) | |
| { | |
| WCHAR ch; | |
| saxreader_string_append(locator, buffer, L"&", 1); | |
| saxreader_skip(locator, 1); | |
| /* Check for empty name */ | |
| ch = *saxreader_get_ptr_noread(locator); | |
| if (!saxreader_is_namechar(ch)) | |
| saxreader_set_error(locator, E_SAX_BADCHARINENTREF); | |
| while (saxreader_is_namechar(ch)) | |
| { | |
| saxreader_string_append(locator, buffer, &ch, 1); | |
| saxreader_skip(locator, 1); | |
| ch = *saxreader_get_ptr_noread(locator); | |
| } | |
| if (saxreader_cmp(locator, L";")) | |
| saxreader_string_append(locator, buffer, L";", 1); | |
| else | |
| saxreader_set_error(locator, E_SAX_BADCHARINENTREF); | |
| } | |
| static void saxreader_entity_add_part(struct saxlocator *locator, struct entity_decl *decl, | |
| struct string_buffer *buffer, bool reference) | |
| { | |
| struct entity_part *part; | |
| if (!buffer->count) | |
| return; | |
| 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++]; | |
| part->value = saxreader_string_to_bstr(locator, buffer); | |
| part->reference = reference; | |
| } | |
| /* [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | |
| | "'" ([^%&'] | PEReference | Reference)* "'" */ | |
| static void saxreader_parse_entityvalue(struct saxlocator *locator, struct entity_decl *decl) | |
| { | |
| struct string_buffer buffer = { 0 }, part = { 0 }; | |
| struct text_position position; | |
| WCHAR quote, ch; | |
| size_t count; | |
| if (!(quote = saxreader_is_quote(locator))) | |
| { | |
| saxreader_set_error(locator, E_SAX_MISSINGQUOTE); | |
| return; | |
| } | |
| saxreader_skip(locator, 1); | |
| /* 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. */ | |
| 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); | |
| 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); | |
| 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); | |
| } | |
| if (ch == quote) | |
| { | |
| saxreader_entity_add_part(locator, decl, &part, false); | |
| decl->value = saxreader_string_to_bstr(locator, &buffer); | |
| /* Skip closing quote */ | |
| saxreader_skip(locator, 1); | |
| return; | |
| } | |
| free(part.data); | |
| free(buffer.data); | |
| saxreader_set_error(locator, E_SAX_EXPECTINGCLOSEQUOTE); | |
| } | |
| /* [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) | |
| { | |
| BSTR notation = NULL, pubid = NULL; | |
| struct string_buffer buffer = { 0 }; | |
| struct entity_decl *decl; | |
| bool pe, undeclared; | |
| /* Skip <!ENTITY */ | |
| saxreader_skip(locator, 8); | |
| saxreader_skip_required_spaces(locator); | |
| if ((pe = saxreader_cmp(locator, L"%"))) | |
| saxreader_skip_required_spaces(locator); | |
| if (!(decl = saxreader_calloc(locator, 1, sizeof(*decl)))) | |
| return; | |
| saxreader_parse_name_strict(locator, &decl->name); | |
| saxreader_skip_required_spaces(locator); | |
| if (pe) | |
| { | |
| 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; | |
| 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; | |
| 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_more(locator); | |
| if (!saxreader_peek(locator, L">", 1)) | |
| saxreader_skip_required_spaces(locator); | |
| 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); | |
| } | |
| } | |
| } | |
| } | |
| if (undeclared) | |
| list_add_tail(&locator->dtd->entities, &decl->entry); | |
| else | |
| saxreader_free_entity(decl); | |
| SysFreeString(pubid); | |
| SysFreeString(notation); | |
| } | |
| static void saxreader_discard_string(BSTR *str) | |
| { | |
| SysFreeString(*str); | |
| *str = NULL; | |
| } | |
| static void saxreader_append_enum_item(struct saxlocator *locator, struct enumeration *list, | |
| BSTR *value) | |
| { | |
| if (locator->status != S_OK) | |
| return saxreader_discard_string(value); | |
| if (!saxreader_array_reserve(locator, (void **)&list->values, &list->capacity, list->count + 1, | |
| sizeof(*list->values))) | |
| { | |
| return; | |
| } | |
| list->values[list->count++] = *value; | |
| *value = NULL; | |
| } | |
| /* [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' */ | |
| static enum attribute_type saxreader_parse_notation_type(struct saxlocator *locator, | |
| struct enumeration *list) | |
| { | |
| bool done = false; | |
| BSTR name; | |
| /* 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 ATTR_TYPE_INVALID; | |
| } | |
| while (locator->status == S_OK && !done) | |
| { | |
| saxreader_skipspaces(locator); | |
| saxreader_parse_name_strict(locator, &name); | |
| saxreader_append_enum_item(locator, list, &name); | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L")")) | |
| done = true; | |
| else if (!saxreader_cmp(locator, L"|")) | |
| saxreader_set_error(locator, E_SAX_BADCHARINENUMERATION); | |
| } | |
| if (locator->status == S_OK) | |
| return ATTR_TYPE_NOTATION; | |
| saxreader_clear_enumeration(list); | |
| return ATTR_TYPE_INVALID; | |
| } | |
| /* [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' */ | |
| static enum attribute_type saxreader_parse_enumeration_type(struct saxlocator *locator, | |
| struct enumeration *list) | |
| { | |
| bool done = false; | |
| BSTR token; | |
| /* Skip '(' */ | |
| saxreader_skip(locator, 1); | |
| while (locator->status == S_OK && !done) | |
| { | |
| saxreader_skipspaces(locator); | |
| saxreader_parse_nmtoken(locator, &token); | |
| saxreader_append_enum_item(locator, list, &token); | |
| saxreader_skipspaces(locator); | |
| if (saxreader_cmp(locator, L")")) | |
| done = true; | |
| else if (!saxreader_cmp(locator, L"|")) | |
| saxreader_set_error(locator, E_SAX_BADCHARINENUMERATION); | |
| } | |
| if (locator->status == S_OK) | |
| return ATTR_TYPE_ENUMERATION; | |
| saxreader_clear_enumeration(list); | |
| return ATTR_TYPE_INVALID; | |
| } | |
| /* [54] AttType ::= StringType | TokenizedType | EnumeratedType | |
| [55] StringType ::= 'CDATA' | |
| [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | |
| | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS' | |
| [57] EnumeratedType ::= NotationType | Enumeration */ | |
| static enum attribute_type saxreader_parse_atttype(struct saxlocator *locator, | |
| struct enumeration *list) | |
| { | |
| if (saxreader_cmp(locator, L"CDATA")) | |
| return ATTR_TYPE_CDATA; | |
| if (saxreader_cmp(locator, L"IDREFS")) | |
| return ATTR_TYPE_IDREFS; | |
| if (saxreader_cmp(locator, L"IDREF")) | |
| return ATTR_TYPE_IDREF; | |
| if (saxreader_cmp(locator, L"ID")) | |
| return ATTR_TYPE_ID; | |
| if (saxreader_cmp(locator, L"ENTITY")) | |
| return ATTR_TYPE_ENTITY; | |
| if (saxreader_cmp(locator, L"ENTITIES")) | |
| return ATTR_TYPE_ENTITIES; | |
| if (saxreader_cmp(locator, L"NMTOKENS")) | |
| return ATTR_TYPE_NMTOKENS; | |
| if (saxreader_cmp(locator, L"NMTOKEN")) | |
| return ATTR_TYPE_NMTOKEN; | |
| if (saxreader_peek(locator, L"NOTATION", 8)) | |
| return saxreader_parse_notation_type(locator, list); | |
| else if (saxreader_peek(locator, L"(", 1)) | |
| return saxreader_parse_enumeration_type(locator, list); | |
| saxreader_set_error(locator, E_SAX_INVALID_TYPE); | |
| return ATTR_TYPE_INVALID; | |
| } | |
| /* [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) */ | |
| static enum attribute_default_type saxreader_parse_defaultdecl(struct saxlocator *locator, BSTR *value) | |
| { | |
| enum attribute_default_type ret = ATTR_DEF_TYPE_NONE; | |
| *value = NULL; | |
| if (saxreader_cmp(locator, L"#REQUIRED")) | |
| return ATTR_DEF_TYPE_REQUIRED; | |
| if (saxreader_cmp(locator, L"#IMPLIED")) | |
| return ATTR_DEF_TYPE_IMPLIED; | |
| if (saxreader_cmp(locator, L"#FIXED")) | |
| { | |
| ret = ATTR_DEF_TYPE_FIXED; | |
| saxreader_skip_required_spaces(locator); | |
| } | |
| *value = saxreader_parse_attvalue(locator); | |
| return ret; | |
| } | |
| /* [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' | |
| [53] AttDef ::= S Name S AttType S DefaultDecl */ | |
| static void saxreader_parse_attlistdecl(struct saxlocator *locator) | |
| { | |
| struct attlist_decl *decl; | |
| if (!(decl = saxreader_calloc(locator, 1, sizeof(*decl)))) | |
| return; | |
| list_add_tail(&locator->dtd->attr_decls, &decl->entry); | |
| /* Skip <!ATTLIST */ | |
| saxreader_skip(locator, 9); | |
| saxreader_skip_required_spaces(locator); | |
| saxreader_parse_name(locator, &decl->name); | |
| saxreader_skipspaces(locator); | |
| while (locator->status == S_OK) | |
| { | |
| struct attlist_attr *attr; | |
| if (saxreader_cmp(locator, L">")) | |
| break; | |
| if (!saxreader_array_reserve(locator, (void **)&decl->attributes, &decl->capacity, | |
| decl->count + 1, sizeof(*decl->attributes))) | |
| { | |
| break; | |
| } | |
| attr = &decl->attributes[decl->count++]; | |
| memset(attr, 0, sizeof(*attr)); | |
| saxreader_parse_qname(locator, &attr->name); | |
| saxreader_skip_required_spaces(locator); | |
| attr->type = saxreader_parse_atttype(locator, &attr->valuelist); | |
| saxreader_skip_required_spaces(locator); | |
| attr->valuetype = saxreader_parse_defaultdecl(locator, &attr->value); | |
| if (saxreader_cmp(locator, L">")) | |
| break; | |
| saxreader_skip_required_spaces(locator); | |
| } | |
| saxlocator_attribute_decl(locator, decl); | |
| } | |
| /* [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' */ | |
| static void saxreader_parse_notationdecl(struct saxlocator *locator) | |
| { | |
| BSTR name = NULL, sysid = NULL, pubid = NULL; | |
| /* 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); | |
| } | |
| /* [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment */ | |
| static void saxreader_parse_markupdecl(struct saxlocator *locator) | |
| { | |
| 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); | |
| } | |
| /* [69] PEReference ::= '%' Name ';' */ | |
| static void saxreader_parse_pe_reference(struct saxlocator *locator) | |
| { | |
| struct string_buffer buffer = { 0 }; | |
| struct text_position position; | |
| struct entity_decl *entity; | |
| BSTR name; | |
| /* Skip '%' */ | |
| saxreader_skip(locator, 1); | |
| position = locator->buffer.position; | |
| saxreader_parse_name(locator, &name); | |
| if (!saxreader_cmp(locator, L";")) | |
| saxreader_set_error(locator, E_SAX_MISSINGSEMICOLON); | |
| 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); | |
| } | |
| /* [28a] DeclSep ::= PEReference | S | |
| [28b] intSubset ::= (markupdecl | DeclSep)* | |
| [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment */ | |
| static void saxreader_parse_intsubset(struct saxlocator *locator) | |
| { | |
| WCHAR *ptr; | |
| /* Opening '[' bracket already skipped */ | |
| saxreader_skipspaces(locator); | |
| while (locator->status == S_OK) | |
| { | |
| if (saxreader_cmp(locator, L"]")) | |
| return; | |
| // 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); | |
| } | |
| } | |
| /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */ | |
| static void saxreader_parse_doctype(struct saxlocator *locator) | |
| { | |
| BSTR name, sysid, pubid; | |
| if (locator->saxreader->features & ProhibitDTD) | |
| return saxreader_set_error(locator, E_SAX_INVALIDATROOTLEVEL); | |
| if (!(locator->dtd = saxreader_new_dtd(locator))) | |
| return; | |
| if (saxreader_has_handler(locator, SAXExtensionHandler)) | |
| locator->collect = true; | |
| /* Skip <!DOCTYPE */ | |
| saxreader_skip(locator, 9); | |
| saxreader_skip_required_spaces(locator); | |
| saxreader_parse_name(locator, &name); | |
| saxreader_skipspaces(locator); | |
| saxreader_parse_externalid(locator, &sysid, &pubid, false); | |
| saxreader_skipspaces(locator); | |
| saxreader_shrink(locator); | |
| if (saxreader_cmp(locator, L"[")) | |
| { | |
| 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); | |
| } | |
| else if (saxreader_cmp(locator, L">")) | |
| { | |
| saxlocator_startdtd(locator, name, pubid, sysid); | |
| } | |
| if (sysid) | |
| FIXME("External subset is not supported.\n"); | |
| saxlocator_extension_dtd(locator); | |
| saxlocator_enddtd(locator); | |
| SysFreeString(sysid); | |
| SysFreeString(pubid); | |
| } | |
| static enum xmlencoding saxreader_match_encoding(const char *data, size_t size, size_t *bom) | |
| { | |
| const BYTE *b = (const BYTE *)data; | |
| *bom = 0; | |
| if (size < 4) | |
| return XML_ENCODING_UNKNOWN; | |
| 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) | |
| { | |
| *bom = 3; | |
| return XML_ENCODING_UTF8; | |
| } | |
| if (b[0] == 0xfe && b[1] == 0xff) | |
| { | |
| *bom = 2; | |
| return XML_ENCODING_UTF16BE; | |
| } | |
| if (b[0] == 0xff && b[1] == 0xfe) | |
| { | |
| *bom = 2; | |
| return XML_ENCODING_UTF16LE; | |
| } | |
| return XML_ENCODING_UNKNOWN; | |
| } | |
| static void saxreader_detect_encoding(struct saxlocator *locator, bool force_utf16) | |
| { | |
| 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 (!saxreader_limit_xml_size(locator, read)) | |
| return; | |
| if (!read) | |
| return saxreader_set_error(locator, E_SAX_MISSINGROOT); | |
| raw->written = read; | |
| encoding = force_utf16 ? XML_ENCODING_UTF16LE : saxreader_match_encoding(raw->data, read, &bom); | |
| if (encoding == XML_ENCODING_UNKNOWN) | |
| { | |
| WARN("Failed to detect document encoding.\n"); | |
| return saxreader_set_error(locator, E_SAX_INVALIDENCODING); | |
| } | |
| locator->buffer.encoding = encoding; | |
| locator->buffer.converter = convert_get_converter(encoding); | |
| locator->buffer.code_page = convert_get_codepage(encoding); | |
| TRACE("detected encoding %s\n", saxreader_get_encoding_name(encoding)); | |
| size = read - bom; | |
| if (encoding == XML_ENCODING_UTF16LE) | |
| { | |
| if (!saxreader_reserve_buffer(locator, utf16, size + sizeof(WCHAR))) | |
| return; | |
| memcpy(utf16->data + utf16->written, raw->data + bom, size); | |
| utf16->written += size; | |
| *(WCHAR *)&utf16->data[utf16->written] = 0; | |
| encoded_buffer_cleanup(raw); | |
| } | |
| else if (bom) | |
| { | |
| memmove(raw->data, raw->data + bom, size); | |
| raw->written -= bom; | |
| } | |
| } | |
| static HRESULT saxreader_parse_stream(struct saxreader *reader, ISequentialStream *stream, | |
| bool force_utf16, bool vbInterface) | |
| { | |
| struct saxlocator *locator; | |
| HRESULT hr; | |
| if (FAILED(hr = saxlocator_create(reader, stream, vbInterface, &locator))) | |
| return hr; | |
| while (locator->state != SAX_PARSER_EOF) | |
| { | |
| if (locator->status != S_OK) | |
| { | |
| saxreader_fatal_error(locator); | |
| break; | |
| } | |
| switch (locator->state) | |
| { | |
| case SAX_PARSER_START: | |
| saxreader_detect_encoding(locator, force_utf16); | |
| 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_xmldecl(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); | |
| 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; | |
| } | |
| } | |
| } | |
| 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 (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; | |
| } | |
| } | |
| hr = locator->status; | |
| ISAXLocator_Release(&locator->ISAXLocator_iface); | |
| return hr; | |
| } | |
| static HRESULT saxreader_parse(struct saxreader *reader, VARIANT input, bool vbInterface) | |
| { | |
| ISequentialStream *stream; | |
| HRESULT hr; | |
| TRACE("%p, %s.\n", reader, debugstr_variant(&input)); | |
| switch (V_VT(&input)) | |
| { | |
| case VT_BSTR: | |
| case VT_BSTR|VT_BYREF: | |
| { | |
| 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, true, vbInterface); | |
| ISequentialStream_Release(stream); | |
| break; | |
| } | |
| case VT_ARRAY|VT_UI1: | |
| { | |
| LONG lBound, uBound; | |
| 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, false, vbInterface); | |
| ISequentialStream_Release(stream); | |
| } | |
| SafeArrayUnaccessData(V_ARRAY(&input)); | |
| break; | |
| } | |
| case VT_UNKNOWN: | |
| case VT_DISPATCH: | |
| { | |
| ISequentialStream *stream = NULL; | |
| IXMLDOMDocument *xmlDoc; | |
| if (!V_UNKNOWN(&input)) | |
| return E_INVALIDARG; | |
| if (IUnknown_QueryInterface(V_UNKNOWN(&input), &IID_IXMLDOMDocument, (void**)&xmlDoc) == S_OK) | |
| { | |
| BSTR bstrData; | |
| IXMLDOMDocument_get_xml(xmlDoc, &bstrData); | |
| stream_wrapper_create(bstrData, SysStringByteLen(bstrData), &stream); | |
| hr = saxreader_parse_stream(reader, stream, false, vbInterface); | |
| ISequentialStream_Release(stream); | |
| IXMLDOMDocument_Release(xmlDoc); | |
| SysFreeString(bstrData); | |
| break; | |
| } | |
| /* try base interface first */ | |
| 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(&input), &IID_IStream, (void **)&stream); | |
| if (stream) | |
| { | |
| hr = saxreader_parse_stream(reader, stream, false, vbInterface); | |
| ISequentialStream_Release(stream); | |
| } | |
| else | |
| { | |
| WARN("IUnknown* input doesn't support any of expected interfaces\n"); | |
| hr = E_INVALIDARG; | |
| } | |
| break; | |
| } | |
| default: | |
| WARN("input type %d is not supported\n", V_VT(&input)); | |
| hr = E_INVALIDARG; | |
| } | |
| return hr; | |
| } | |
| static HRESULT internal_vbonDataAvailable(void *obj, char *ptr, DWORD len) | |
| { | |
| struct saxreader *reader = obj; | |
| ISequentialStream *stream; | |
| HRESULT hr; | |
| if (SUCCEEDED(hr = stream_wrapper_create(ptr, len, &stream))) | |
| { | |
| hr = saxreader_parse_stream(reader, stream, false, true); | |
| ISequentialStream_Release(stream); | |
| } | |
| return hr; | |
| } | |
| static HRESULT internal_onDataAvailable(void *obj, char *ptr, DWORD len) | |
| { | |
| struct saxreader *reader = obj; | |
| ISequentialStream *stream; | |
| HRESULT hr; | |
| if (SUCCEEDED(hr = stream_wrapper_create(ptr, len, &stream))) | |
| { | |
| hr = saxreader_parse_stream(reader, stream, false, false); | |
| ISequentialStream_Release(stream); | |
| } | |
| return hr; | |
| } | |
| static HRESULT saxreader_parse_url(struct saxreader *reader, const WCHAR *url, bool vbInterface) | |
| { | |
| IMoniker *mon; | |
| bsc_t *bsc; | |
| HRESULT hr; | |
| TRACE("%p, %s.\n", reader, debugstr_w(url)); | |
| if (!url && reader->version < MSXML4) | |
| return E_INVALIDARG; | |
| hr = create_moniker_from_url(url, &mon); | |
| if (FAILED(hr)) | |
| return hr; | |
| 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)) | |
| return hr; | |
| return detach_bsc(bsc); | |
| } | |
| 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(reader, type, NULL, vb); | |
| switch (type) | |
| { | |
| case SAXDeclHandler: | |
| riid = vb ? &IID_IVBSAXDeclHandler : &IID_ISAXDeclHandler; | |
| break; | |
| case SAXLexicalHandler: | |
| riid = vb ? &IID_IVBSAXLexicalHandler : &IID_ISAXLexicalHandler; | |
| break; | |
| case SAXExtensionHandler: | |
| riid = &IID_ISAXExtensionHandler; | |
| if (vb) return E_UNEXPECTED; | |
| break; | |
| default: | |
| ERR("wrong handler type %d\n", type); | |
| return E_FAIL; | |
| } | |
| switch (V_VT(v)) | |
| { | |
| case VT_DISPATCH: | |
| case VT_UNKNOWN: | |
| { | |
| IUnknown *handler = NULL; | |
| if (V_UNKNOWN(v)) | |
| { | |
| HRESULT hr = IUnknown_QueryInterface(V_UNKNOWN(v), riid, (void**)&handler); | |
| if (FAILED(hr)) return hr; | |
| } | |
| saxreader_put_handler(reader, type, handler, vb); | |
| if (handler) IUnknown_Release(handler); | |
| break; | |
| } | |
| default: | |
| ERR("value type %d not supported\n", V_VT(v)); | |
| return E_INVALIDARG; | |
| } | |
| return S_OK; | |
| } | |
| HRESULT variant_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; | |
| TRACE("%p, %s, %s.\n", reader, debugstr_w(prop), debugstr_variant(&value)); | |
| if (reader->isParsing) return E_FAIL; | |
| v = V_VT(&value) == (VT_VARIANT|VT_BYREF) ? V_VARIANTREF(&value) : &value; | |
| if (!wcscmp(prop, L"http://xml.org/sax/properties/declaration-handler")) | |
| return saxreader_put_handler_from_variant(reader, SAXDeclHandler, v, vbInterface); | |
| 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"http://winehq.org/sax/properties/extension-handler")) | |
| return saxreader_put_handler_from_variant(reader, SAXExtensionHandler, v, vbInterface); | |
| if (!wcscmp(prop, L"max-xml-size")) | |
| { | |
| int size; | |
| if (FAILED(variant_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")) | |
| { | |
| int depth; | |
| if (FAILED(variant_get_int_property(&value, &depth))) | |
| return E_FAIL; | |
| if (depth < 0) | |
| return E_INVALIDARG; | |
| reader->max_element_depth = depth; | |
| return S_OK; | |
| } | |
| FIXME("(%p)->(%s:%s): unsupported property\n", reader, debugstr_w(prop), debugstr_variant(v)); | |
| if (!wcscmp(prop, L"charset")) | |
| return E_NOTIMPL; | |
| if (!wcscmp(prop, L"http://xml.org/sax/properties/dom-node")) | |
| return E_FAIL; | |
| if (!wcscmp(prop, L"input-source")) | |
| return E_NOTIMPL; | |
| if (!wcscmp(prop, L"schema-declaration-handler")) | |
| return E_NOTIMPL; | |
| if (!wcscmp(prop, L"xmldecl-encoding")) | |
| return E_FAIL; | |
| if (!wcscmp(prop, L"xmldecl-standalone")) | |
| return E_FAIL; | |
| if (!wcscmp(L"xmldecl-version", prop)) | |
| return E_FAIL; | |
| return E_INVALIDARG; | |
| } | |
| static HRESULT saxreader_get_property(const struct saxreader *reader, const WCHAR *prop, VARIANT *value, bool vb) | |
| { | |
| if (!value) return E_POINTER; | |
| if (!wcscmp(prop, L"http://xml.org/sax/properties/lexical-handler")) | |
| { | |
| V_VT(value) = vb ? VT_DISPATCH : VT_UNKNOWN; | |
| saxreader_get_handler(reader, SAXLexicalHandler, vb, (void**)&V_UNKNOWN(value)); | |
| return S_OK; | |
| } | |
| if (!wcscmp(prop, L"http://xml.org/sax/properties/declaration-handler")) | |
| { | |
| V_VT(value) = vb ? VT_DISPATCH : VT_UNKNOWN; | |
| saxreader_get_handler(reader, SAXDeclHandler, vb, (void**)&V_UNKNOWN(value)); | |
| return S_OK; | |
| } | |
| if (!wcscmp(prop, L"xmldecl-version")) | |
| { | |
| V_VT(value) = VT_BSTR; | |
| return return_bstr(reader->xmldecl_version, &V_BSTR(value)); | |
| } | |
| if (!wcscmp(prop, L"xmldecl-encoding")) | |
| { | |
| V_VT(value) = VT_BSTR; | |
| return return_bstr(reader->xmldecl_encoding, &V_BSTR(value)); | |
| } | |
| if (!wcscmp(prop, L"xmldecl-standalone")) | |
| { | |
| V_VT(value) = VT_BSTR; | |
| return return_bstr(reader->xmldecl_encoding, &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; | |
| } | |
| if (!wcscmp(prop, L"max-element-depth")) | |
| { | |
| V_VT(value) = VT_I4; | |
| V_I4(value) = reader->max_element_depth; | |
| return S_OK; | |
| } | |
| FIXME("(%p)->(%s) unsupported property\n", reader, debugstr_w(prop)); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI saxxmlreader_QueryInterface(IVBSAXXMLReader *iface, REFIID riid, void **obj) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); | |
| *obj = NULL; | |
| if (IsEqualGUID(riid, &IID_IUnknown) || | |
| IsEqualGUID(riid, &IID_IDispatch) || | |
| IsEqualGUID(riid, &IID_IVBSAXXMLReader)) | |
| { | |
| *obj = iface; | |
| } | |
| else if (IsEqualGUID(riid, &IID_ISAXXMLReader)) | |
| { | |
| *obj = &reader->ISAXXMLReader_iface; | |
| } | |
| else if (IsEqualGUID(riid, &IID_ISAXXMLReaderExtension)) | |
| { | |
| *obj = &reader->ISAXXMLReaderExtension_iface; | |
| } | |
| else if (dispex_query_interface(&reader->dispex, riid, obj)) | |
| { | |
| return *obj ? S_OK : E_NOINTERFACE; | |
| } | |
| else | |
| { | |
| FIXME("interface %s not implemented\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| IVBSAXXMLReader_AddRef(iface); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI saxxmlreader_AddRef(IVBSAXXMLReader *iface) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| LONG refcount = InterlockedIncrement(&reader->refcount); | |
| TRACE("%p, refcount %lu.\n", iface, refcount); | |
| return refcount; | |
| } | |
| static ULONG WINAPI saxxmlreader_Release(IVBSAXXMLReader *iface) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| LONG refcount = InterlockedDecrement(&reader->refcount); | |
| TRACE("%p, refcount %lu.\n", iface, refcount); | |
| if (!refcount) | |
| { | |
| for (int i = 0; i < ARRAYSIZE(reader->saxhandlers); ++i) | |
| { | |
| struct saxanyhandler_iface *saxiface = &reader->saxhandlers[i].u.anyhandler; | |
| if (saxiface->handler) | |
| IUnknown_Release(saxiface->handler); | |
| if (saxiface->vbhandler) | |
| IUnknown_Release(saxiface->vbhandler); | |
| } | |
| SysFreeString(reader->xmldecl_version); | |
| SysFreeString(reader->empty_bstr); | |
| free(reader); | |
| } | |
| return refcount; | |
| } | |
| static HRESULT WINAPI saxxmlreader_GetTypeInfoCount(IVBSAXXMLReader *iface, UINT *count) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return IDispatchEx_GetTypeInfoCount(&reader->dispex.IDispatchEx_iface, count); | |
| } | |
| static HRESULT WINAPI saxxmlreader_GetTypeInfo(IVBSAXXMLReader *iface, UINT index, LCID lcid, ITypeInfo **ti) | |
| { | |
| 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 *names, | |
| UINT count, LCID lcid, DISPID *dispid) | |
| { | |
| 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 dispid, REFIID riid, | |
| LCID lcid, WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *ei, | |
| UINT *argerr) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return IDispatchEx_Invoke(&reader->dispex.IDispatchEx_iface, dispid, riid, lcid, flags, params, result, ei, argerr); | |
| } | |
| static HRESULT WINAPI saxxmlreader_getFeature(IVBSAXXMLReader *iface, BSTR name, VARIANT_BOOL *value) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return ISAXXMLReader_getFeature(&reader->ISAXXMLReader_iface, name, value); | |
| } | |
| static HRESULT WINAPI saxxmlreader_putFeature(IVBSAXXMLReader *iface, BSTR name, VARIANT_BOOL value) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return ISAXXMLReader_putFeature(&reader->ISAXXMLReader_iface, name, value); | |
| } | |
| static HRESULT WINAPI saxxmlreader_getProperty(IVBSAXXMLReader *iface, BSTR name, VARIANT *value) | |
| { | |
| 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 name, VARIANT value) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return saxreader_put_handler(reader, SAXErrorHandler, handler, true); | |
| } | |
| static HRESULT WINAPI saxxmlreader_get_baseURL(IVBSAXXMLReader *iface, BSTR *url) | |
| { | |
| FIXME("%p, %p stub\n", iface, url); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI saxxmlreader_put_baseURL(IVBSAXXMLReader *iface, BSTR url) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return ISAXXMLReader_putBaseURL(&reader->ISAXXMLReader_iface, url); | |
| } | |
| static HRESULT WINAPI saxxmlreader_get_secureBaseURL(IVBSAXXMLReader *iface, BSTR *url) | |
| { | |
| FIXME("%p, %p stub\n", iface, url); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI saxxmlreader_put_secureBaseURL(IVBSAXXMLReader *iface, BSTR url) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return ISAXXMLReader_putSecureBaseURL(&reader->ISAXXMLReader_iface, url); | |
| } | |
| static HRESULT WINAPI saxxmlreader_parse(IVBSAXXMLReader *iface, VARIANT input) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return saxreader_parse(reader, input, true); | |
| } | |
| static HRESULT WINAPI saxxmlreader_parseURL(IVBSAXXMLReader *iface, BSTR url) | |
| { | |
| struct saxreader *reader = impl_from_IVBSAXXMLReader(iface); | |
| return saxreader_parse_url(reader, url, true); | |
| } | |
| static const struct IVBSAXXMLReaderVtbl vbsaxxmlreadervtbl = | |
| { | |
| saxxmlreader_QueryInterface, | |
| saxxmlreader_AddRef, | |
| saxxmlreader_Release, | |
| saxxmlreader_GetTypeInfoCount, | |
| saxxmlreader_GetTypeInfo, | |
| saxxmlreader_GetIDsOfNames, | |
| saxxmlreader_Invoke, | |
| saxxmlreader_getFeature, | |
| saxxmlreader_putFeature, | |
| saxxmlreader_getProperty, | |
| saxxmlreader_putProperty, | |
| saxxmlreader_get_entityResolver, | |
| saxxmlreader_putref_entityResolver, | |
| saxxmlreader_get_contentHandler, | |
| saxxmlreader_putref_contentHandler, | |
| saxxmlreader_get_dtdHandler, | |
| saxxmlreader_putref_dtdHandler, | |
| saxxmlreader_get_errorHandler, | |
| saxxmlreader_putref_errorHandler, | |
| saxxmlreader_get_baseURL, | |
| saxxmlreader_put_baseURL, | |
| saxxmlreader_get_secureBaseURL, | |
| saxxmlreader_put_secureBaseURL, | |
| saxxmlreader_parse, | |
| saxxmlreader_parseURL | |
| }; | |
| static HRESULT WINAPI isaxxmlreader_QueryInterface(ISAXXMLReader *iface, REFIID riid, void **obj) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return IVBSAXXMLReader_QueryInterface(&reader->IVBSAXXMLReader_iface, riid, obj); | |
| } | |
| static ULONG WINAPI isaxxmlreader_AddRef(ISAXXMLReader *iface) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return IVBSAXXMLReader_AddRef(&reader->IVBSAXXMLReader_iface); | |
| } | |
| static ULONG WINAPI isaxxmlreader_Release(ISAXXMLReader *iface) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return IVBSAXXMLReader_Release(&reader->IVBSAXXMLReader_iface); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getFeature(ISAXXMLReader *iface, const WCHAR *name, VARIANT_BOOL *value) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| enum saxreader_feature feature; | |
| TRACE("%p, %s, %p.\n", iface, debugstr_w(name), value); | |
| feature = get_saxreader_feature(name); | |
| if (reader->version < MSXML4 && (feature == ExhaustiveErrors || feature == SchemaValidation)) | |
| return E_INVALIDARG; | |
| if (feature == NormalizeLineBreaks && reader->version >= MSXML4) | |
| return E_INVALIDARG; | |
| if (feature == Namespaces || | |
| feature == NamespacePrefixes || | |
| feature == ExhaustiveErrors || | |
| feature == SchemaValidation || | |
| feature == NormalizeLineBreaks) | |
| return get_feature_value(reader, feature, value); | |
| FIXME("%p, %s, %p stub\n", iface, debugstr_w(name), value); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_putFeature(ISAXXMLReader *iface, const WCHAR *name, VARIANT_BOOL value) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| enum saxreader_feature feature; | |
| TRACE("%p, %s, %#x.\n", iface, debugstr_w(name), value); | |
| feature = get_saxreader_feature(name); | |
| if (feature == NormalizeLineBreaks && reader->version >= MSXML4) | |
| return E_INVALIDARG; | |
| /* accepted cases */ | |
| if ((feature == ExhaustiveErrors && value == VARIANT_FALSE) || | |
| (feature == SchemaValidation && value == VARIANT_FALSE) || | |
| feature == Namespaces || | |
| feature == NamespacePrefixes || | |
| feature == NormalizeLineBreaks || | |
| feature == NormalizeAttributeValues || | |
| feature == ProhibitDTD) | |
| { | |
| return set_feature_value(reader, feature, value); | |
| } | |
| if (feature == LexicalHandlerParEntities || | |
| feature == ExternalGeneralEntities || | |
| feature == ExternalParameterEntities) | |
| { | |
| FIXME("%p, %s, %#x stub\n", iface, debugstr_w(name), value); | |
| return set_feature_value(reader, feature, value); | |
| } | |
| FIXME("%p, %s, %#x stub\n", iface, debugstr_w(name), value); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getProperty(ISAXXMLReader *iface, const WCHAR *name, VARIANT *value) | |
| { | |
| 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 *name, VARIANT value) | |
| { | |
| 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) | |
| { | |
| 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) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_put_handler(reader, SAXEntityResolver, resolver, false); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getContentHandler(ISAXXMLReader *iface, ISAXContentHandler **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) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_put_handler(reader, SAXContentHandler, handler, false); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getDTDHandler(ISAXXMLReader *iface, ISAXDTDHandler **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) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_put_handler(reader, SAXDTDHandler, handler, false); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getErrorHandler(ISAXXMLReader *iface, ISAXErrorHandler **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) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_put_handler(reader, SAXErrorHandler, handler, false); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getBaseURL(ISAXXMLReader *iface, const WCHAR **url) | |
| { | |
| FIXME("%p, %p stub\n", iface, url); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_putBaseURL(ISAXXMLReader *iface, const WCHAR *url) | |
| { | |
| FIXME("%p, %s stub\n", iface, debugstr_w(url)); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_getSecureBaseURL(ISAXXMLReader *iface, const WCHAR **url) | |
| { | |
| FIXME("%p, %p stub\n", iface, url); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_putSecureBaseURL(ISAXXMLReader *iface, const WCHAR *url) | |
| { | |
| FIXME("%p, %s stub\n", iface, debugstr_w(url)); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI isaxxmlreader_parse(ISAXXMLReader *iface, VARIANT input) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_parse(reader, input, false); | |
| } | |
| static HRESULT WINAPI isaxxmlreader_parseURL(ISAXXMLReader *iface, const WCHAR *url) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReader(iface); | |
| return saxreader_parse_url(reader, url, false); | |
| } | |
| static const struct ISAXXMLReaderVtbl saxxmlreadervtbl = | |
| { | |
| isaxxmlreader_QueryInterface, | |
| isaxxmlreader_AddRef, | |
| isaxxmlreader_Release, | |
| isaxxmlreader_getFeature, | |
| isaxxmlreader_putFeature, | |
| isaxxmlreader_getProperty, | |
| isaxxmlreader_putProperty, | |
| isaxxmlreader_getEntityResolver, | |
| isaxxmlreader_putEntityResolver, | |
| isaxxmlreader_getContentHandler, | |
| isaxxmlreader_putContentHandler, | |
| isaxxmlreader_getDTDHandler, | |
| isaxxmlreader_putDTDHandler, | |
| isaxxmlreader_getErrorHandler, | |
| isaxxmlreader_putErrorHandler, | |
| isaxxmlreader_getBaseURL, | |
| isaxxmlreader_putBaseURL, | |
| isaxxmlreader_getSecureBaseURL, | |
| isaxxmlreader_putSecureBaseURL, | |
| isaxxmlreader_parse, | |
| isaxxmlreader_parseURL | |
| }; | |
| static HRESULT WINAPI saxxmlreader_extension_QueryInterface(ISAXXMLReaderExtension *iface, REFIID riid, void **obj) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReaderExtension(iface); | |
| return IVBSAXXMLReader_QueryInterface(&reader->IVBSAXXMLReader_iface, riid, obj); | |
| } | |
| static ULONG WINAPI saxxmlreader_extension_AddRef(ISAXXMLReaderExtension *iface) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReaderExtension(iface); | |
| return IVBSAXXMLReader_AddRef(&reader->IVBSAXXMLReader_iface); | |
| } | |
| static ULONG WINAPI saxxmlreader_extension_Release(ISAXXMLReaderExtension *iface) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReaderExtension(iface); | |
| return IVBSAXXMLReader_Release(&reader->IVBSAXXMLReader_iface); | |
| } | |
| static HRESULT WINAPI saxxmlreader_extension_parseUTF16(ISAXXMLReaderExtension *iface, ISequentialStream *stream) | |
| { | |
| struct saxreader *reader = impl_from_ISAXXMLReaderExtension(iface); | |
| return saxreader_parse_stream(reader, stream, true, false); | |
| } | |
| static const struct ISAXXMLReaderExtensionVtbl saxxmlreaderextensionvtbl = | |
| { | |
| saxxmlreader_extension_QueryInterface, | |
| saxxmlreader_extension_AddRef, | |
| saxxmlreader_extension_Release, | |
| saxxmlreader_extension_parseUTF16, | |
| }; | |
| static const tid_t saxreader_iface_tids[] = | |
| { | |
| IVBSAXXMLReader_tid, | |
| 0 | |
| }; | |
| static dispex_static_data_t saxreader_dispex = | |
| { | |
| NULL, | |
| IVBSAXXMLReader_tid, | |
| NULL, | |
| saxreader_iface_tids | |
| }; | |
| static HRESULT saxreader_create(MSXML_VERSION version, struct saxreader **reader) | |
| { | |
| struct saxreader *object; | |
| *reader = NULL; | |
| if (!(object = calloc(1, sizeof(*object)))) | |
| return E_OUTOFMEMORY; | |
| object->IVBSAXXMLReader_iface.lpVtbl = &vbsaxxmlreadervtbl; | |
| object->ISAXXMLReader_iface.lpVtbl = &saxxmlreadervtbl; | |
| object->ISAXXMLReaderExtension_iface.lpVtbl = &saxxmlreaderextensionvtbl; | |
| object->refcount = 1; | |
| object->features = Namespaces | NamespacePrefixes | NormalizeLineBreaks | NormalizeAttributeValues; | |
| object->version = version; | |
| object->empty_bstr = SysAllocString(L""); | |
| object->max_element_depth = version > MSXML3 ? 256 : 5000; | |
| init_dispex(&object->dispex, (IUnknown *)&object->IVBSAXXMLReader_iface, &saxreader_dispex); | |
| *reader = object; | |
| return S_OK; | |
| } | |
| HRESULT SAXXMLReader_create(MSXML_VERSION version, void **obj) | |
| { | |
| struct saxreader *reader; | |
| HRESULT hr; | |
| TRACE("%p\n", obj); | |
| *obj = NULL; | |
| if (SUCCEEDED(hr = saxreader_create(version, &reader))) | |
| *obj = &reader->IVBSAXXMLReader_iface; | |
| TRACE("returning iface %p\n", *obj); | |
| return hr; | |
| } | |
| /* Fixed size buffer variant, to be used without markup. */ | |
| static void saxreader_parse_xmldecl_body(struct saxlocator *locator) | |
| { | |
| struct parsed_name name; | |
| bool done = false; | |
| BSTR value; | |
| saxreader_skipspaces(locator); | |
| value = saxreader_parse_xmldecl_attribute(locator, &name); | |
| saxreader_parse_versioninfo(locator, &name, value); | |
| saxreader_free_name(&name); | |
| SysFreeString(value); | |
| saxreader_skipspaces(locator); | |
| if (locator->eos) | |
| return; | |
| while (!done && locator->status == S_OK) | |
| { | |
| value = saxreader_parse_xmldecl_attribute(locator, &name); | |
| if (!wcscmp(name.qname, L"encoding")) | |
| { | |
| saxreader_parse_encdecl(locator, &name, value); | |
| } | |
| else if (!wcscmp(name.qname, L"standalone")) | |
| { | |
| saxreader_parse_sddecl(locator, &name, value); | |
| done = true; | |
| } | |
| else | |
| { | |
| saxreader_set_error(locator, E_SAX_UNEXPECTED_ATTRIBUTE); | |
| } | |
| saxreader_free_name(&name); | |
| SysFreeString(value); | |
| if (locator->status != S_OK) | |
| continue; | |
| saxreader_skipspaces(locator); | |
| if (locator->eos) | |
| return; | |
| } | |
| saxreader_skipspaces(locator); | |
| if (!locator->eos) | |
| saxreader_set_error(locator, E_SAX_BAD_XMLDECL); | |
| } | |
| HRESULT parse_xml_decl_body(const WCHAR *text, BSTR *version, BSTR *encoding, BSTR *standalone) | |
| { | |
| struct saxlocator *locator; | |
| ISequentialStream *stream; | |
| struct saxreader *reader; | |
| HRESULT hr; | |
| *version = *encoding = *standalone = NULL; | |
| if (!text) | |
| return E_SAX_XMLDECLSYNTAX; | |
| if (FAILED(hr = stream_wrapper_create(text, wcslen(text) * sizeof(WCHAR), &stream))) | |
| return hr; | |
| hr = saxreader_create(MSXML3, &reader); | |
| if (SUCCEEDED(hr)) | |
| { | |
| hr = saxlocator_create(reader, stream, false, &locator); | |
| ISAXXMLReader_Release(&reader->ISAXXMLReader_iface); | |
| } | |
| if (SUCCEEDED(hr)) | |
| { | |
| saxreader_detect_encoding(locator, true); | |
| saxreader_parse_xmldecl_body(locator); | |
| *version = saxreader_alloc_string(locator, locator->saxreader->xmldecl_version); | |
| *encoding = saxreader_alloc_string(locator, locator->saxreader->xmldecl_encoding); | |
| *standalone = saxreader_alloc_string(locator, locator->saxreader->xmldecl_standalone); | |
| hr = locator->status; | |
| ISAXLocator_Release(&locator->ISAXLocator_iface); | |
| } | |
| ISequentialStream_Release(stream); | |
| return hr; | |
| } | |
| static void saxlocator_standalone_init(struct saxlocator *locator, ISequentialStream *stream) | |
| { | |
| locator->stream = stream; | |
| locator->buffer.encoding = XML_ENCODING_UTF16LE; | |
| locator->buffer.code_page = convert_get_codepage(XML_ENCODING_UTF16LE); | |
| locator->buffer.chunk_size = 4096; | |
| list_init(&locator->buffer.entities); | |
| saxreader_more(locator); | |
| } | |
| HRESULT parse_qualified_name(const WCHAR *src, struct parsed_name *name) | |
| { | |
| struct saxlocator locator = { 0 }; | |
| ISequentialStream *stream; | |
| HRESULT hr; | |
| if (FAILED(hr = stream_wrapper_create(src, wcslen(src) * sizeof(WCHAR), &stream))) | |
| return hr; | |
| saxlocator_standalone_init(&locator, stream); | |
| saxreader_parse_qname(&locator, name); | |
| encoded_buffer_cleanup(&locator.buffer.utf16); | |
| ISequentialStream_Release(stream); | |
| return locator.status; | |
| } | |
| void parsed_name_cleanup(struct parsed_name *name) | |
| { | |
| saxreader_free_name(name); | |
| } |