/* * SAX Reader implementation * * Copyright 2008 Alistair Leslie-Hughes * Copyright 2008 Piotr Caban * * 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 "config.h" #include #ifdef HAVE_LIBXML2 # include # include # include # include #endif #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 "wine/debug.h" #include "msxml_private.h" WINE_DEFAULT_DEBUG_CHANNEL(msxml); #ifdef HAVE_LIBXML2 typedef enum { FeatureUnknown = 0, ExhaustiveErrors = 1 << 1, ExternalGeneralEntities = 1 << 2, ExternalParameterEntities = 1 << 3, ForcedResync = 1 << 4, NamespacePrefixes = 1 << 5, Namespaces = 1 << 6, ParameterEntities = 1 << 7, PreserveSystemIndentifiers = 1 << 8, ProhibitDTD = 1 << 9, SchemaValidation = 1 << 10, ServerHttpRequest = 1 << 11, SuppressValidationfatalError = 1 << 12, UseInlineSchema = 1 << 13, UseSchemaLocation = 1 << 14, LexicalHandlerParEntities = 1 << 15 } saxreader_feature; /* feature names */ static const WCHAR FeatureExternalGeneralEntitiesW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/','s','a','x','/', 'f','e','a','t','u','r','e','s','/','e','x','t','e','r','n','a','l','-','g','e','n','e','r','a','l', '-','e','n','t','i','t','i','e','s',0 }; static const WCHAR FeatureExternalParameterEntitiesW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/','s','a','x','/','f','e','a','t','u','r','e','s', '/','e','x','t','e','r','n','a','l','-','p','a','r','a','m','e','t','e','r','-','e','n','t','i','t','i','e','s',0 }; static const WCHAR FeatureLexicalHandlerParEntitiesW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/','s','a','x','/','f','e','a','t','u','r','e','s', '/','l','e','x','i','c','a','l','-','h','a','n','d','l','e','r','/','p','a','r','a','m','e','t','e','r','-','e','n','t','i','t','i','e','s',0 }; static const WCHAR FeatureProhibitDTDW[] = { 'p','r','o','h','i','b','i','t','-','d','t','d',0 }; static const WCHAR FeatureNamespacesW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/','s','a','x','/','f','e','a','t','u','r','e','s', '/','n','a','m','e','s','p','a','c','e','s',0 }; static const WCHAR FeatureNamespacePrefixesW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/','s','a','x','/','f','e','a','t','u','r','e','s', '/','n','a','m','e','s','p','a','c','e','-','p','r','e','f','i','x','e','s',0 }; struct saxreader_feature_pair { saxreader_feature feature; const WCHAR *name; }; static const struct saxreader_feature_pair saxreader_feature_map[] = { { ExternalGeneralEntities, FeatureExternalGeneralEntitiesW }, { ExternalParameterEntities, FeatureExternalParameterEntitiesW }, { LexicalHandlerParEntities, FeatureLexicalHandlerParEntitiesW }, { NamespacePrefixes, FeatureNamespacePrefixesW }, { Namespaces, FeatureNamespacesW }, { ProhibitDTD, FeatureProhibitDTDW } }; static saxreader_feature get_saxreader_feature(const WCHAR *name) { int min, max, n, c; min = 0; max = sizeof(saxreader_feature_map)/sizeof(struct saxreader_feature_pair) - 1; while (min <= max) { n = (min+max)/2; c = strcmpW(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; } struct bstrpool { BSTR *pool; unsigned int index; unsigned int len; }; 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, SAXHandler_Last }; struct saxanyhandler_iface { IUnknown *handler; IUnknown *vbhandler; }; struct saxcontenthandler_iface { ISAXContentHandler *handler; IVBSAXContentHandler *vbhandler; }; struct saxerrorhandler_iface { ISAXErrorHandler *handler; IVBSAXErrorHandler *vbhandler; }; struct saxlexicalhandler_iface { ISAXLexicalHandler *handler; IVBSAXLexicalHandler *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 saxanyhandler_iface anyhandler; } u; }; typedef struct { DispatchEx dispex; IVBSAXXMLReader IVBSAXXMLReader_iface; ISAXXMLReader ISAXXMLReader_iface; LONG ref; struct saxhandler_iface saxhandlers[SAXHandler_Last]; xmlSAXHandler sax; BOOL isParsing; struct bstrpool pool; saxreader_feature features; BSTR xmldecl_version; MSXML_VERSION version; } saxreader; static HRESULT saxreader_put_handler(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 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(saxreader *reader) { return &reader->saxhandlers[SAXContentHandler].u.content; } static struct saxerrorhandler_iface *saxreader_get_errorhandler(saxreader *reader) { return &reader->saxhandlers[SAXErrorHandler].u.error; } static struct saxlexicalhandler_iface *saxreader_get_lexicalhandler(saxreader *reader) { return &reader->saxhandlers[SAXLexicalHandler].u.lexical; } typedef struct { IVBSAXLocator IVBSAXLocator_iface; ISAXLocator ISAXLocator_iface; IVBSAXAttributes IVBSAXAttributes_iface; ISAXAttributes ISAXAttributes_iface; LONG ref; saxreader *saxreader; HRESULT ret; xmlParserCtxtPtr pParserCtxt; BSTR publicId; BSTR systemId; int line; int column; BOOL vbInterface; struct list elements; BSTR namespaceUri; int attributesSize; int nb_attributes; struct _attributes { BSTR szLocalname; BSTR szURI; BSTR szValue; BSTR szQName; } *attributes; } saxlocator; static inline saxreader *impl_from_IVBSAXXMLReader( IVBSAXXMLReader *iface ) { return CONTAINING_RECORD(iface, saxreader, IVBSAXXMLReader_iface); } static inline saxreader *impl_from_ISAXXMLReader( ISAXXMLReader *iface ) { return CONTAINING_RECORD(iface, saxreader, ISAXXMLReader_iface); } static inline saxlocator *impl_from_IVBSAXLocator( IVBSAXLocator *iface ) { return CONTAINING_RECORD(iface, saxlocator, IVBSAXLocator_iface); } static inline saxlocator *impl_from_ISAXLocator( ISAXLocator *iface ) { return CONTAINING_RECORD(iface, saxlocator, ISAXLocator_iface); } static inline saxlocator *impl_from_IVBSAXAttributes( IVBSAXAttributes *iface ) { return CONTAINING_RECORD(iface, saxlocator, IVBSAXAttributes_iface); } static inline saxlocator *impl_from_ISAXAttributes( ISAXAttributes *iface ) { return CONTAINING_RECORD(iface, saxlocator, ISAXAttributes_iface); } static inline BOOL saxreader_has_handler(const 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 HRESULT saxreader_saxcharacters(saxlocator *locator, BSTR chars) { struct saxcontenthandler_iface *content = saxreader_get_contenthandler(locator->saxreader); HRESULT hr; if (!saxreader_has_handler(locator, SAXContentHandler)) return S_OK; if (locator->vbInterface) hr = IVBSAXContentHandler_characters(content->vbhandler, &chars); else hr = ISAXContentHandler_characters(content->handler, chars, SysStringLen(chars)); return hr; } /* property names */ static const WCHAR PropertyCharsetW[] = { 'c','h','a','r','s','e','t',0 }; static const WCHAR PropertyXmlDeclVersionW[] = { 'x','m','l','d','e','c','l','-','v','e','r','s','i','o','n',0 }; static const WCHAR PropertyDeclHandlerW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/', 's','a','x','/','p','r','o','p','e','r','t','i','e','s','/', 'd','e','c','l','a','r','a','t','i','o','n', '-','h','a','n','d','l','e','r',0 }; static const WCHAR PropertyDomNodeW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/', 's','a','x','/','p','r','o','p','e','r','t','i','e','s','/', 'd','o','m','-','n','o','d','e',0 }; static const WCHAR PropertyInputSourceW[] = { 'i','n','p','u','t','-','s','o','u','r','c','e',0 }; static const WCHAR PropertyLexicalHandlerW[] = { 'h','t','t','p',':','/','/','x','m','l','.','o','r','g','/', 's','a','x','/','p','r','o','p','e','r','t','i','e','s','/', 'l','e','x','i','c','a','l','-','h','a','n','d','l','e','r',0 }; static const WCHAR PropertyMaxElementDepthW[] = { 'm','a','x','-','e','l','e','m','e','n','t','-','d','e','p','t','h',0 }; static const WCHAR PropertyMaxXMLSizeW[] = { 'm','a','x','-','x','m','l','-','s','i','z','e',0 }; static const WCHAR PropertySchemaDeclHandlerW[] = { 's','c','h','e','m','a','-','d','e','c','l','a','r','a','t','i','o','n','-', 'h','a','n','d','l','e','r',0 }; static const WCHAR PropertyXMLDeclEncodingW[] = { 'x','m','l','d','e','c','l','-','e','n','c','o','d','i','n','g',0 }; static const WCHAR PropertyXMLDeclStandaloneW[] = { 'x','m','l','d','e','c','l','-','s','t','a','n','d','a','l','o','n','e',0 }; static const WCHAR PropertyXMLDeclVersionW[] = { 'x','m','l','d','e','c','l','-','v','e','r','s','i','o','n',0 }; static inline HRESULT set_feature_value(saxreader *reader, 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 saxreader *reader, saxreader_feature feature, VARIANT_BOOL *value) { *value = reader->features & feature ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } static BOOL is_namespaces_enabled(const saxreader *reader) { return (reader->version < MSXML4) || (reader->features & Namespaces); } static BSTR build_qname(BSTR prefix, BSTR local) { if (prefix && *prefix) { BSTR qname = SysAllocStringLen(NULL, SysStringLen(prefix) + SysStringLen(local) + 1); WCHAR *ptr; ptr = qname; strcpyW(ptr, prefix); ptr += SysStringLen(prefix); *ptr++ = ':'; strcpyW(ptr, local); return qname; } else return SysAllocString(local); } static element_entry* alloc_element_entry(const xmlChar *local, const xmlChar *prefix, int nb_ns, const xmlChar **namespaces) { element_entry *ret; int i; ret = heap_alloc(sizeof(*ret)); if (!ret) return ret; ret->local = bstr_from_xmlChar(local); ret->prefix = bstr_from_xmlChar(prefix); ret->qname = build_qname(ret->prefix, ret->local); ret->ns = nb_ns ? heap_alloc(nb_ns*sizeof(ns)) : NULL; ret->ns_count = nb_ns; for (i=0; i < nb_ns; i++) { ret->ns[i].prefix = bstr_from_xmlChar(namespaces[2*i]); ret->ns[i].uri = bstr_from_xmlChar(namespaces[2*i+1]); } return ret; } static void free_element_entry(element_entry *element) { int i; for (i=0; ins_count;i++) { SysFreeString(element->ns[i].prefix); SysFreeString(element->ns[i].uri); } SysFreeString(element->prefix); SysFreeString(element->local); SysFreeString(element->qname); heap_free(element->ns); heap_free(element); } static void push_element_ns(saxlocator *locator, element_entry *element) { list_add_head(&locator->elements, &element->entry); } static element_entry * pop_element_ns(saxlocator *locator) { element_entry *element = LIST_ENTRY(list_head(&locator->elements), element_entry, entry); if (element) list_remove(&element->entry); return element; } static BSTR find_element_uri(saxlocator *locator, const xmlChar *uri) { element_entry *element; BSTR uriW; int i; if (!uri) return NULL; uriW = bstr_from_xmlChar(uri); LIST_FOR_EACH_ENTRY(element, &locator->elements, element_entry, entry) { for (i=0; i < element->ns_count; i++) if (!strcmpW(uriW, element->ns[i].uri)) { SysFreeString(uriW); return element->ns[i].uri; } } SysFreeString(uriW); ERR("namespace uri not found, %s\n", debugstr_a((char*)uri)); return NULL; } /* used to localize version dependent error check behaviour */ static inline BOOL sax_callback_failed(saxlocator *This, HRESULT hr) { return This->saxreader->version >= MSXML4 ? FAILED(hr) : hr != S_OK; } /* index value -1 means it tries to loop for a first time */ static inline BOOL iterate_endprefix_index(saxlocator *This, const element_entry *element, int *i) { if (This->saxreader->version >= MSXML4) { if (*i == -1) *i = 0; else ++*i; return *i < element->ns_count; } else { if (*i == -1) *i = element->ns_count-1; else --*i; return *i >= 0; } } static BOOL bstr_pool_insert(struct bstrpool *pool, BSTR pool_entry) { if (!pool->pool) { pool->pool = HeapAlloc(GetProcessHeap(), 0, 16 * sizeof(*pool->pool)); if (!pool->pool) return FALSE; pool->index = 0; pool->len = 16; } else if (pool->index == pool->len) { BSTR *realloc = HeapReAlloc(GetProcessHeap(), 0, pool->pool, pool->len * 2 * sizeof(*realloc)); if (!realloc) return FALSE; pool->pool = realloc; pool->len *= 2; } pool->pool[pool->index++] = pool_entry; return TRUE; } static void free_bstr_pool(struct bstrpool *pool) { unsigned int i; for (i = 0; i < pool->index; i++) SysFreeString(pool->pool[i]); HeapFree(GetProcessHeap(), 0, pool->pool); pool->pool = NULL; pool->index = pool->len = 0; } static BSTR bstr_from_xmlCharN(const xmlChar *buf, int len) { DWORD dLen; BSTR bstr; if (!buf) return NULL; dLen = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, NULL, 0); if(len != -1) dLen++; bstr = SysAllocStringLen(NULL, dLen-1); if (!bstr) return NULL; MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, bstr, dLen); if(len != -1) bstr[dLen-1] = '\0'; return bstr; } static BSTR QName_from_xmlChar(const xmlChar *prefix, const xmlChar *name) { xmlChar *qname; BSTR bstr; if(!name) return NULL; if(!prefix || !*prefix) return bstr_from_xmlChar(name); qname = xmlBuildQName(name, prefix, NULL, 0); bstr = bstr_from_xmlChar(qname); xmlFree(qname); return bstr; } static BSTR pooled_bstr_from_xmlChar(struct bstrpool *pool, const xmlChar *buf) { BSTR pool_entry = bstr_from_xmlChar(buf); if (pool_entry && !bstr_pool_insert(pool, pool_entry)) { SysFreeString(pool_entry); return NULL; } return pool_entry; } static BSTR pooled_bstr_from_xmlCharN(struct bstrpool *pool, const xmlChar *buf, int len) { BSTR pool_entry = bstr_from_xmlCharN(buf, len); if (pool_entry && !bstr_pool_insert(pool, pool_entry)) { SysFreeString(pool_entry); return NULL; } return pool_entry; } static void format_error_message_from_id(saxlocator *This, HRESULT hr) { struct saxerrorhandler_iface *handler = saxreader_get_errorhandler(This->saxreader); xmlStopParser(This->pParserCtxt); This->ret = hr; if (saxreader_has_handler(This, SAXErrorHandler)) { WCHAR msg[1024]; if(!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, msg, sizeof(msg), NULL)) { FIXME("MSXML errors not yet supported.\n"); msg[0] = '\0'; } if(This->vbInterface) { BSTR bstrMsg = SysAllocString(msg); IVBSAXErrorHandler_fatalError(handler->vbhandler, &This->IVBSAXLocator_iface, &bstrMsg, hr); SysFreeString(bstrMsg); } else ISAXErrorHandler_fatalError(handler->handler, &This->ISAXLocator_iface, msg, hr); } } static void update_position(saxlocator *This, BOOL fix_column) { const xmlChar *p = This->pParserCtxt->input->cur-1; This->line = xmlSAX2GetLineNumber(This->pParserCtxt); if(fix_column) { This->column = 1; for(; *p!='\n' && *p!='\r' && p>=This->pParserCtxt->input->base; p--) This->column++; } else { This->column = xmlSAX2GetColumnNumber(This->pParserCtxt); } } /*** IVBSAXAttributes interface ***/ /*** IUnknown methods ***/ static HRESULT WINAPI ivbsaxattributes_QueryInterface( IVBSAXAttributes* iface, REFIID riid, void **ppvObject) { saxlocator *This = impl_from_IVBSAXAttributes(iface); TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); return IVBSAXLocator_QueryInterface(&This->IVBSAXLocator_iface, riid, ppvObject); } static ULONG WINAPI ivbsaxattributes_AddRef(IVBSAXAttributes* iface) { saxlocator *This = impl_from_IVBSAXAttributes(iface); return ISAXLocator_AddRef(&This->ISAXLocator_iface); } static ULONG WINAPI ivbsaxattributes_Release(IVBSAXAttributes* iface) { saxlocator *This = impl_from_IVBSAXAttributes(iface); return ISAXLocator_Release(&This->ISAXLocator_iface); } /*** IDispatch methods ***/ static HRESULT WINAPI ivbsaxattributes_GetTypeInfoCount( IVBSAXAttributes *iface, UINT* pctinfo ) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI ivbsaxattributes_GetTypeInfo( IVBSAXAttributes *iface, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo ) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); HRESULT hr; TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); hr = get_typeinfo(IVBSAXAttributes_tid, ppTInfo); return hr; } static HRESULT WINAPI ivbsaxattributes_GetIDsOfNames( IVBSAXAttributes *iface, REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, 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 wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IVBSAXAttributes_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, &This->IVBSAXAttributes_iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } /*** IVBSAXAttributes methods ***/ static HRESULT WINAPI ivbsaxattributes_get_length( IVBSAXAttributes* iface, int *nLength) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getLength(&This->ISAXAttributes_iface, nLength); } static HRESULT WINAPI ivbsaxattributes_getURI( IVBSAXAttributes* iface, int nIndex, BSTR *uri) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getURI(&This->ISAXAttributes_iface, nIndex, (const WCHAR**)uri, &len); } static HRESULT WINAPI ivbsaxattributes_getLocalName( IVBSAXAttributes* iface, int nIndex, BSTR *localName) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getLocalName(&This->ISAXAttributes_iface, nIndex, (const WCHAR**)localName, &len); } static HRESULT WINAPI ivbsaxattributes_getQName( IVBSAXAttributes* iface, int nIndex, BSTR *QName) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getQName(&This->ISAXAttributes_iface, nIndex, (const WCHAR**)QName, &len); } static HRESULT WINAPI ivbsaxattributes_getIndexFromName( IVBSAXAttributes* iface, BSTR uri, BSTR localName, int *index) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getIndexFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), localName, SysStringLen(localName), index); } static HRESULT WINAPI ivbsaxattributes_getIndexFromQName( IVBSAXAttributes* iface, BSTR QName, int *index) { saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getIndexFromQName(&This->ISAXAttributes_iface, QName, SysStringLen(QName), index); } static HRESULT WINAPI ivbsaxattributes_getType( IVBSAXAttributes* iface, int nIndex, BSTR *type) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getType(&This->ISAXAttributes_iface, nIndex, (const WCHAR**)type, &len); } static HRESULT WINAPI ivbsaxattributes_getTypeFromName( IVBSAXAttributes* iface, BSTR uri, BSTR localName, BSTR *type) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getTypeFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), localName, SysStringLen(localName), (const WCHAR**)type, &len); } static HRESULT WINAPI ivbsaxattributes_getTypeFromQName( IVBSAXAttributes* iface, BSTR QName, BSTR *type) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getTypeFromQName(&This->ISAXAttributes_iface, QName, SysStringLen(QName), (const WCHAR**)type, &len); } static HRESULT WINAPI ivbsaxattributes_getValue( IVBSAXAttributes* iface, int nIndex, BSTR *value) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getValue(&This->ISAXAttributes_iface, nIndex, (const WCHAR**)value, &len); } static HRESULT WINAPI ivbsaxattributes_getValueFromName( IVBSAXAttributes* iface, BSTR uri, BSTR localName, BSTR *value) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getValueFromName(&This->ISAXAttributes_iface, uri, SysStringLen(uri), localName, SysStringLen(localName), (const WCHAR**)value, &len); } static HRESULT WINAPI ivbsaxattributes_getValueFromQName( IVBSAXAttributes* iface, BSTR QName, BSTR *value) { int len; saxlocator *This = impl_from_IVBSAXAttributes( iface ); return ISAXAttributes_getValueFromQName(&This->ISAXAttributes_iface, QName, SysStringLen(QName), (const WCHAR**)value, &len); } 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 }; /*** ISAXAttributes interface ***/ /*** IUnknown methods ***/ static HRESULT WINAPI isaxattributes_QueryInterface( ISAXAttributes* iface, REFIID riid, void **ppvObject) { saxlocator *This = impl_from_ISAXAttributes(iface); TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject); return ISAXLocator_QueryInterface(&This->ISAXLocator_iface, riid, ppvObject); } static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface) { saxlocator *This = impl_from_ISAXAttributes(iface); TRACE("%p\n", This); return ISAXLocator_AddRef(&This->ISAXLocator_iface); } static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface) { saxlocator *This = impl_from_ISAXAttributes(iface); TRACE("%p\n", This); return ISAXLocator_Release(&This->ISAXLocator_iface); } /*** ISAXAttributes methods ***/ static HRESULT WINAPI isaxattributes_getLength( ISAXAttributes* iface, int *length) { saxlocator *This = impl_from_ISAXAttributes( iface ); *length = This->nb_attributes; TRACE("Length set to %d\n", *length); return S_OK; } static HRESULT WINAPI isaxattributes_getURI( ISAXAttributes* iface, int index, const WCHAR **url, int *size) { saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%d)\n", This, index); if(index >= This->nb_attributes || index < 0) return E_INVALIDARG; if(!url || !size) return E_POINTER; *size = SysStringLen(This->attributes[index].szURI); *url = This->attributes[index].szURI; TRACE("(%s:%d)\n", debugstr_w(This->attributes[index].szURI), *size); return S_OK; } static HRESULT WINAPI isaxattributes_getLocalName( ISAXAttributes* iface, int nIndex, const WCHAR **pLocalName, int *pLocalNameLength) { saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%d)\n", This, nIndex); if(nIndex>=This->nb_attributes || nIndex<0) return E_INVALIDARG; if(!pLocalName || !pLocalNameLength) return E_POINTER; *pLocalNameLength = SysStringLen(This->attributes[nIndex].szLocalname); *pLocalName = This->attributes[nIndex].szLocalname; return S_OK; } static HRESULT WINAPI isaxattributes_getQName( ISAXAttributes* iface, int nIndex, const WCHAR **pQName, int *pQNameLength) { saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%d)\n", This, nIndex); if(nIndex>=This->nb_attributes || nIndex<0) return E_INVALIDARG; if(!pQName || !pQNameLength) return E_POINTER; *pQNameLength = SysStringLen(This->attributes[nIndex].szQName); *pQName = This->attributes[nIndex].szQName; return S_OK; } static HRESULT WINAPI isaxattributes_getName( ISAXAttributes* iface, int index, const WCHAR **uri, int *pUriLength, const WCHAR **localName, int *pLocalNameSize, const WCHAR **QName, int *pQNameLength) { saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%d)\n", This, index); if(index>=This->nb_attributes || index<0) return E_INVALIDARG; if(!uri || !pUriLength || !localName || !pLocalNameSize || !QName || !pQNameLength) return E_POINTER; *pUriLength = SysStringLen(This->attributes[index].szURI); *uri = This->attributes[index].szURI; *pLocalNameSize = SysStringLen(This->attributes[index].szLocalname); *localName = This->attributes[index].szLocalname; *pQNameLength = SysStringLen(This->attributes[index].szQName); *QName = This->attributes[index].szQName; TRACE("(%s, %s, %s)\n", debugstr_w(*uri), debugstr_w(*localName), debugstr_w(*QName)); return S_OK; } static HRESULT WINAPI isaxattributes_getIndexFromName( ISAXAttributes* iface, const WCHAR *pUri, int cUriLength, const WCHAR *pLocalName, int cocalNameLength, int *index) { saxlocator *This = impl_from_ISAXAttributes( iface ); int i; TRACE("(%p)->(%s, %d, %s, %d)\n", This, debugstr_w(pUri), cUriLength, debugstr_w(pLocalName), cocalNameLength); if(!pUri || !pLocalName || !index) return E_POINTER; for(i=0; inb_attributes; i++) { if(cUriLength!=SysStringLen(This->attributes[i].szURI) || cocalNameLength!=SysStringLen(This->attributes[i].szLocalname)) continue; if(cUriLength && memcmp(pUri, This->attributes[i].szURI, sizeof(WCHAR)*cUriLength)) continue; if(cocalNameLength && memcmp(pLocalName, This->attributes[i].szLocalname, sizeof(WCHAR)*cocalNameLength)) continue; *index = i; return S_OK; } return E_INVALIDARG; } static HRESULT WINAPI isaxattributes_getIndexFromQName( ISAXAttributes* iface, const WCHAR *pQName, int nQNameLength, int *index) { saxlocator *This = impl_from_ISAXAttributes( iface ); int i; TRACE("(%p)->(%s, %d)\n", This, debugstr_w(pQName), nQNameLength); if(!pQName || !index) return E_POINTER; if(!nQNameLength) return E_INVALIDARG; for(i=0; inb_attributes; i++) { if(nQNameLength!=SysStringLen(This->attributes[i].szQName)) continue; if(memcmp(pQName, This->attributes[i].szQName, sizeof(WCHAR)*nQNameLength)) continue; *index = i; return S_OK; } return E_INVALIDARG; } static HRESULT WINAPI isaxattributes_getType( ISAXAttributes* iface, int nIndex, const WCHAR **pType, int *pTypeLength) { saxlocator *This = impl_from_ISAXAttributes( iface ); FIXME("(%p)->(%d) stub\n", This, nIndex); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getTypeFromName( ISAXAttributes* iface, const WCHAR *pUri, int nUri, const WCHAR *pLocalName, int nLocalName, const WCHAR **pType, int *nType) { saxlocator *This = impl_from_ISAXAttributes( iface ); FIXME("(%p)->(%s, %d, %s, %d) stub\n", This, debugstr_w(pUri), nUri, debugstr_w(pLocalName), nLocalName); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getTypeFromQName( ISAXAttributes* iface, const WCHAR *pQName, int nQName, const WCHAR **pType, int *nType) { saxlocator *This = impl_from_ISAXAttributes( iface ); FIXME("(%p)->(%s, %d) stub\n", This, debugstr_w(pQName), nQName); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getValue( ISAXAttributes* iface, int index, const WCHAR **value, int *nValue) { saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%d)\n", This, index); if(index>=This->nb_attributes || index<0) return E_INVALIDARG; if(!value || !nValue) return E_POINTER; *nValue = SysStringLen(This->attributes[index].szValue); *value = This->attributes[index].szValue; TRACE("(%s:%d)\n", debugstr_w(*value), *nValue); return S_OK; } static HRESULT WINAPI isaxattributes_getValueFromName( ISAXAttributes* iface, const WCHAR *pUri, int nUri, const WCHAR *pLocalName, int nLocalName, const WCHAR **pValue, int *nValue) { HRESULT hr; int index; saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%s, %d, %s, %d)\n", This, debugstr_w(pUri), nUri, debugstr_w(pLocalName), nLocalName); hr = ISAXAttributes_getIndexFromName(iface, pUri, nUri, pLocalName, nLocalName, &index); if(hr==S_OK) hr = ISAXAttributes_getValue(iface, index, pValue, nValue); return hr; } static HRESULT WINAPI isaxattributes_getValueFromQName( ISAXAttributes* iface, const WCHAR *pQName, int nQName, const WCHAR **pValue, int *nValue) { HRESULT hr; int index; saxlocator *This = impl_from_ISAXAttributes( iface ); TRACE("(%p)->(%s, %d)\n", This, debugstr_w(pQName), nQName); hr = ISAXAttributes_getIndexFromQName(iface, pQName, nQName, &index); if(hr==S_OK) hr = ISAXAttributes_getValue(iface, index, pValue, nValue); return hr; } 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 }; /* Libxml2 escapes '&' back to char reference '&' in attribute value, so when document has escaped value with '&' it's parsed to '&' and then escaped to '&'. This function takes care of ampersands only. */ static BSTR saxreader_get_unescaped_value(const xmlChar *buf, int len) { static const WCHAR ampescW[] = {'&','#','3','8',';',0}; WCHAR *dest, *ptrW, *str; DWORD str_len; BSTR bstr; if (!buf) return NULL; str_len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, NULL, 0); if (len != -1) str_len++; str = heap_alloc(str_len*sizeof(WCHAR)); if (!str) return NULL; MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, len, str, str_len); if (len != -1) str[str_len-1] = 0; ptrW = str; while ((dest = strstrW(ptrW, ampescW))) { WCHAR *src; /* leave first '&' from a reference as a value */ src = dest + (sizeof(ampescW)/sizeof(WCHAR) - 1); dest++; /* move together with null terminator */ memmove(dest, src, (strlenW(src) + 1)*sizeof(WCHAR)); ptrW++; } bstr = SysAllocString(str); heap_free(str); return bstr; } static HRESULT SAXAttributes_populate(saxlocator *locator, int nb_namespaces, const xmlChar **xmlNamespaces, int nb_attributes, const xmlChar **xmlAttributes) { static const xmlChar xmlns[] = "xmlns"; static const WCHAR xmlnsW[] = { 'x','m','l','n','s',0 }; struct _attributes *attrs; int i; /* skip namespace definitions */ if ((locator->saxreader->features & NamespacePrefixes) == 0) nb_namespaces = 0; locator->nb_attributes = nb_namespaces + nb_attributes; if(locator->nb_attributes > locator->attributesSize) { attrs = heap_realloc(locator->attributes, sizeof(struct _attributes)*locator->nb_attributes*2); if(!attrs) { locator->nb_attributes = 0; return E_OUTOFMEMORY; } locator->attributes = attrs; } else { attrs = locator->attributes; } for (i = 0; i < nb_namespaces; i++) { attrs[nb_attributes+i].szLocalname = SysAllocStringLen(NULL, 0); attrs[nb_attributes+i].szURI = locator->namespaceUri; attrs[nb_attributes+i].szValue = bstr_from_xmlChar(xmlNamespaces[2*i+1]); if(!xmlNamespaces[2*i]) attrs[nb_attributes+i].szQName = SysAllocString(xmlnsW); else attrs[nb_attributes+i].szQName = QName_from_xmlChar(xmlns, xmlNamespaces[2*i]); } for (i = 0; i < nb_attributes; i++) { static const xmlChar xmlA[] = "xml"; if (xmlStrEqual(xmlAttributes[i*5+1], xmlA)) attrs[i].szURI = bstr_from_xmlChar(xmlAttributes[i*5+2]); else /* that's an important feature to keep same uri pointer for every reported attribute */ attrs[i].szURI = find_element_uri(locator, xmlAttributes[i*5+2]); attrs[i].szLocalname = bstr_from_xmlChar(xmlAttributes[i*5]); attrs[i].szValue = saxreader_get_unescaped_value(xmlAttributes[i*5+3], xmlAttributes[i*5+4]-xmlAttributes[i*5+3]); attrs[i].szQName = QName_from_xmlChar(xmlAttributes[i*5+1], xmlAttributes[i*5]); } return S_OK; } /*** LibXML callbacks ***/ static void libxmlStartDocument(void *ctx) { saxlocator *This = ctx; struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); HRESULT hr; if (This->saxreader->version >= MSXML4) { const xmlChar *p = This->pParserCtxt->input->cur-1; update_position(This, FALSE); while(p>This->pParserCtxt->input->base && *p!='>') { if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) This->line--; p--; } This->column = 0; for(; p>=This->pParserCtxt->input->base && *p!='\n' && *p!='\r'; p--) This->column++; } /* store version value, declaration has to contain version attribute */ if (This->pParserCtxt->standalone != -1) { SysFreeString(This->saxreader->xmldecl_version); This->saxreader->xmldecl_version = bstr_from_xmlChar(This->pParserCtxt->version); } if (saxreader_has_handler(This, SAXContentHandler)) { if(This->vbInterface) hr = IVBSAXContentHandler_startDocument(handler->vbhandler); else hr = ISAXContentHandler_startDocument(handler->handler); if (sax_callback_failed(This, hr)) format_error_message_from_id(This, hr); } } static void libxmlEndDocument(void *ctx) { saxlocator *This = ctx; struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); HRESULT hr; if (This->saxreader->version >= MSXML4) { update_position(This, FALSE); if(This->column > 1) This->line++; This->column = 0; } else { This->column = 0; This->line = 0; } if(This->ret != S_OK) return; if (saxreader_has_handler(This, SAXContentHandler)) { if(This->vbInterface) hr = IVBSAXContentHandler_endDocument(handler->vbhandler); else hr = ISAXContentHandler_endDocument(handler->handler); if (sax_callback_failed(This, hr)) format_error_message_from_id(This, hr); } } static void libxmlStartElementNS( void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { saxlocator *This = ctx; struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); element_entry *element; HRESULT hr = S_OK; BSTR uri; update_position(This, TRUE); if(*(This->pParserCtxt->input->cur) == '/') This->column++; if(This->saxreader->version < MSXML4) This->column++; element = alloc_element_entry(localname, prefix, nb_namespaces, namespaces); push_element_ns(This, element); if (is_namespaces_enabled(This->saxreader)) { int i; for (i = 0; i < nb_namespaces && saxreader_has_handler(This, SAXContentHandler); i++) { if (This->vbInterface) hr = IVBSAXContentHandler_startPrefixMapping( handler->vbhandler, &element->ns[i].prefix, &element->ns[i].uri); else hr = ISAXContentHandler_startPrefixMapping( handler->handler, element->ns[i].prefix, SysStringLen(element->ns[i].prefix), element->ns[i].uri, SysStringLen(element->ns[i].uri)); if (sax_callback_failed(This, hr)) { format_error_message_from_id(This, hr); return; } } } uri = find_element_uri(This, URI); hr = SAXAttributes_populate(This, nb_namespaces, namespaces, nb_attributes, attributes); if (hr == S_OK && saxreader_has_handler(This, SAXContentHandler)) { BSTR local; if (is_namespaces_enabled(This->saxreader)) local = element->local; else uri = local = NULL; if (This->vbInterface) hr = IVBSAXContentHandler_startElement(handler->vbhandler, &uri, &local, &element->qname, &This->IVBSAXAttributes_iface); else hr = ISAXContentHandler_startElement(handler->handler, uri, SysStringLen(uri), local, SysStringLen(local), element->qname, SysStringLen(element->qname), &This->ISAXAttributes_iface); if (sax_callback_failed(This, hr)) format_error_message_from_id(This, hr); } } static void libxmlEndElementNS( void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI) { saxlocator *This = ctx; struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); element_entry *element; const xmlChar *p; BSTR uri, local; HRESULT hr; update_position(This, FALSE); p = This->pParserCtxt->input->cur; if (This->saxreader->version >= MSXML4) { p--; while(p>This->pParserCtxt->input->base && *p!='>') { if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) This->line--; p--; } } else if(*(p-1)!='>' || *(p-2)!='/') { p--; while(p-2>=This->pParserCtxt->input->base && *(p-2)!='<' && *(p-1)!='/') { if(*p=='\n' || (*p=='\r' && *(p+1)!='\n')) This->line--; p--; } } This->column = 0; for(; p>=This->pParserCtxt->input->base && *p!='\n' && *p!='\r'; p--) This->column++; uri = find_element_uri(This, URI); element = pop_element_ns(This); if (!saxreader_has_handler(This, SAXContentHandler)) { This->nb_attributes = 0; free_element_entry(element); return; } if (is_namespaces_enabled(This->saxreader)) local = element->local; else uri = local = NULL; if (This->vbInterface) hr = IVBSAXContentHandler_endElement( handler->vbhandler, &uri, &local, &element->qname); else hr = ISAXContentHandler_endElement( handler->handler, uri, SysStringLen(uri), local, SysStringLen(local), element->qname, SysStringLen(element->qname)); This->nb_attributes = 0; if (sax_callback_failed(This, hr)) { format_error_message_from_id(This, hr); free_element_entry(element); return; } if (is_namespaces_enabled(This->saxreader)) { int i = -1; while (iterate_endprefix_index(This, element, &i) && saxreader_has_handler(This, SAXContentHandler)) { if (This->vbInterface) hr = IVBSAXContentHandler_endPrefixMapping( handler->vbhandler, &element->ns[i].prefix); else hr = ISAXContentHandler_endPrefixMapping( handler->handler, element->ns[i].prefix, SysStringLen(element->ns[i].prefix)); if (sax_callback_failed(This, hr)) break; } if (sax_callback_failed(This, hr)) format_error_message_from_id(This, hr); } free_element_entry(element); } static void libxmlCharacters( void *ctx, const xmlChar *ch, int len) { saxlocator *This = ctx; BSTR Chars; HRESULT hr; xmlChar *cur, *end; BOOL lastEvent = FALSE; if (!saxreader_has_handler(This, SAXContentHandler)) return; update_position(This, FALSE); cur = (xmlChar*)This->pParserCtxt->input->cur; while(cur>=This->pParserCtxt->input->base && *cur!='>') { if(*cur=='\n' || (*cur=='\r' && *(cur+1)!='\n')) This->line--; cur--; } This->column = 1; for(; cur>=This->pParserCtxt->input->base && *cur!='\n' && *cur!='\r'; cur--) This->column++; cur = (xmlChar*)ch; if(*(ch-1)=='\r') cur--; end = cur; while(1) { while(end-chsaxreader->version >= MSXML4) { xmlChar *p; for(p=cur; p!=end; p++) { if(*p=='\n') { This->line++; This->column = 1; } else { This->column++; } } if(!lastEvent) This->column = 0; } Chars = pooled_bstr_from_xmlCharN(&This->saxreader->pool, cur, end-cur); hr = saxreader_saxcharacters(This, Chars); if (sax_callback_failed(This, hr)) { format_error_message_from_id(This, hr); return; } if (This->saxreader->version < MSXML4) This->column += end-cur; if(lastEvent) break; *(end-1) = '\r'; if(*end == '\n') { end++; This->column++; } cur = end; if(end-ch == len) break; } } static void libxmlSetDocumentLocator( void *ctx, xmlSAXLocatorPtr loc) { saxlocator *This = ctx; struct saxcontenthandler_iface *handler = saxreader_get_contenthandler(This->saxreader); HRESULT hr = S_OK; if (saxreader_has_handler(This, SAXContentHandler)) { if(This->vbInterface) hr = IVBSAXContentHandler_putref_documentLocator(handler->vbhandler, &This->IVBSAXLocator_iface); else hr = ISAXContentHandler_putDocumentLocator(handler->handler, &This->ISAXLocator_iface); } if(FAILED(hr)) format_error_message_from_id(This, hr); } static void libxmlComment(void *ctx, const xmlChar *value) { saxlocator *This = ctx; struct saxlexicalhandler_iface *handler = saxreader_get_lexicalhandler(This->saxreader); BSTR bValue; HRESULT hr; const xmlChar *p = This->pParserCtxt->input->cur; update_position(This, FALSE); while(p-4>=This->pParserCtxt->input->base && memcmp(p-4, "