2 * IXmlReader implementation
4 * Copyright 2010, 2012 Nikolay Sivov
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "xmllite_private.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlReaderInput, 0x0b3ccc9b, 0x9214, 0x428b, 0xa2, 0xae, 0xef, 0x3a, 0xa8, 0x71, 0xaf, 0xda);
49 XmlReadInState_Initial,
50 XmlReadInState_XmlDecl,
51 XmlReadInState_Misc_DTD,
53 } XmlReaderInternalState;
55 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
56 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
58 static const WCHAR dblquoteW[] = {'\"',0};
59 static const WCHAR quoteW[] = {'\'',0};
61 struct xml_encoding_data
68 static const struct xml_encoding_data xml_encoding_map[] = {
69 { utf16W, XmlEncoding_UTF16, ~0 },
70 { utf8W, XmlEncoding_UTF8, CP_UTF8 }
77 unsigned int allocated;
81 typedef struct input_buffer input_buffer;
83 typedef struct _xmlreaderinput
85 IXmlReaderInput IXmlReaderInput_iface;
87 /* reference passed on IXmlReaderInput creation, is kept when input is created */
90 xml_encoding encoding;
93 /* stream reference set after SetInput() call from reader,
94 stored as sequential stream, cause currently
95 optimizations possible with IStream aren't implemented */
96 ISequentialStream *stream;
113 typedef struct _xmlreader
115 IXmlReader IXmlReader_iface;
117 xmlreaderinput *input;
120 XmlReaderInternalState instate;
121 XmlNodeType nodetype;
122 DtdProcessing dtdmode;
123 UINT line, pos; /* reader position in XML stream */
124 struct list attrs; /* attributes list for current node */
125 struct attribute *attr; /* current attribute */
131 encoded_buffer utf16;
132 encoded_buffer encoded;
134 xmlreaderinput *input;
137 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
139 return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
142 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
144 return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
147 static inline void *m_alloc(IMalloc *imalloc, size_t len)
150 return IMalloc_Alloc(imalloc, len);
152 return heap_alloc(len);
155 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
158 return IMalloc_Realloc(imalloc, mem, len);
160 return heap_realloc(mem, len);
163 static inline void m_free(IMalloc *imalloc, void *mem)
166 IMalloc_Free(imalloc, mem);
171 /* reader memory allocation functions */
172 static inline void *reader_alloc(xmlreader *reader, size_t len)
174 return m_alloc(reader->imalloc, len);
177 static inline void reader_free(xmlreader *reader, void *mem)
179 m_free(reader->imalloc, mem);
182 /* reader input memory allocation functions */
183 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
185 return m_alloc(input->imalloc, len);
188 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
190 return m_realloc(input->imalloc, mem, len);
193 static inline void readerinput_free(xmlreaderinput *input, void *mem)
195 m_free(input->imalloc, mem);
198 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
205 size = (strlenW(str)+1)*sizeof(WCHAR);
206 ret = readerinput_alloc(input, size);
207 if (ret) memcpy(ret, str, size);
213 static void reader_clear_attrs(xmlreader *reader)
215 struct attribute *attr, *attr2;
216 LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
218 reader_free(reader, attr);
220 list_init(&reader->attrs);
221 reader->attr_count = 0;
224 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
225 while we are on a node with attributes */
226 static HRESULT reader_add_attr(xmlreader *reader, strval *localname, strval *value)
228 struct attribute *attr;
230 attr = reader_alloc(reader, sizeof(*attr));
231 if (!attr) return E_OUTOFMEMORY;
233 attr->localname = *localname;
234 attr->value = *value;
235 list_add_tail(&reader->attrs, &attr->entry);
236 reader->attr_count++;
241 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
243 const int initial_len = 0x2000;
244 buffer->data = readerinput_alloc(input, initial_len);
245 if (!buffer->data) return E_OUTOFMEMORY;
247 memset(buffer->data, 0, 4);
248 buffer->cur = buffer->data;
249 buffer->allocated = initial_len;
255 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
257 readerinput_free(input, buffer->data);
260 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
262 if (encoding == XmlEncoding_Unknown)
264 FIXME("unsupported encoding %d\n", encoding);
268 *cp = xml_encoding_map[encoding].cp;
273 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
277 if (!name) return XmlEncoding_Unknown;
280 max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
287 c = strncmpiW(xml_encoding_map[n].name, name, len);
289 c = strcmpiW(xml_encoding_map[n].name, name);
291 return xml_encoding_map[n].enc;
299 return XmlEncoding_Unknown;
302 static HRESULT alloc_input_buffer(xmlreaderinput *input)
304 input_buffer *buffer;
307 input->buffer = NULL;
309 buffer = readerinput_alloc(input, sizeof(*buffer));
310 if (!buffer) return E_OUTOFMEMORY;
312 buffer->input = input;
313 buffer->code_page = ~0; /* code page is unknown at this point */
314 hr = init_encoded_buffer(input, &buffer->utf16);
316 readerinput_free(input, buffer);
320 hr = init_encoded_buffer(input, &buffer->encoded);
322 free_encoded_buffer(input, &buffer->utf16);
323 readerinput_free(input, buffer);
327 input->buffer = buffer;
331 static void free_input_buffer(input_buffer *buffer)
333 free_encoded_buffer(buffer->input, &buffer->encoded);
334 free_encoded_buffer(buffer->input, &buffer->utf16);
335 readerinput_free(buffer->input, buffer);
338 static void readerinput_release_stream(xmlreaderinput *readerinput)
340 if (readerinput->stream) {
341 ISequentialStream_Release(readerinput->stream);
342 readerinput->stream = NULL;
346 /* Queries already stored interface for IStream/ISequentialStream.
347 Interface supplied on creation will be overwritten */
348 static HRESULT readerinput_query_for_stream(xmlreaderinput *readerinput)
352 readerinput_release_stream(readerinput);
353 hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
355 hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
360 /* reads a chunk to raw buffer */
361 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
363 encoded_buffer *buffer = &readerinput->buffer->encoded;
364 ULONG len = buffer->allocated - buffer->written, read;
367 /* always try to get aligned to 4 bytes, so the only case we can get partialy read characters is
368 variable width encodings like UTF-8 */
369 len = (len + 3) & ~3;
370 /* try to use allocated space or grow */
371 if (buffer->allocated - buffer->written < len)
373 buffer->allocated *= 2;
374 buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
375 len = buffer->allocated - buffer->written;
378 hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
379 if (FAILED(hr)) return hr;
380 TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
381 buffer->written += read;
386 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
387 static void readerinput_grow(xmlreaderinput *readerinput, int length)
389 encoded_buffer *buffer = &readerinput->buffer->utf16;
391 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
392 if (buffer->allocated < buffer->written + length + 4)
394 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
395 buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
396 buffer->allocated = grown_size;
400 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
402 encoded_buffer *buffer = &readerinput->buffer->encoded;
403 static char startA[] = {'<','?','x','m'};
404 static WCHAR startW[] = {'<','?'};
405 static char utf8bom[] = {0xef,0xbb,0xbf};
406 static char utf16lebom[] = {0xff,0xfe};
408 *enc = XmlEncoding_Unknown;
410 if (buffer->written <= 3) return MX_E_INPUTEND;
412 /* try start symbols if we have enough data to do that, input buffer should contain
413 first chunk already */
414 if (!memcmp(buffer->data, startA, sizeof(startA)))
415 *enc = XmlEncoding_UTF8;
416 else if (!memcmp(buffer->data, startW, sizeof(startW)))
417 *enc = XmlEncoding_UTF16;
418 /* try with BOM now */
419 else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
421 buffer->cur += sizeof(utf8bom);
422 *enc = XmlEncoding_UTF8;
424 else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
426 buffer->cur += sizeof(utf16lebom);
427 *enc = XmlEncoding_UTF16;
433 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
435 encoded_buffer *buffer = &readerinput->buffer->encoded;
436 int len = buffer->written;
438 /* complete single byte char */
439 if (!(buffer->data[len-1] & 0x80)) return len;
441 /* find start byte of multibyte char */
442 while (--len && !(buffer->data[len] & 0xc0))
448 /* returns byte length of complete char sequence for specified code page, */
449 static int readerinput_get_convlen(xmlreaderinput *readerinput, UINT cp)
451 encoded_buffer *buffer = &readerinput->buffer->encoded;
455 len = readerinput_get_utf8_convlen(readerinput);
457 len = buffer->written;
459 return len - (buffer->cur - buffer->data);
462 /* note that raw buffer content is kept */
463 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
465 encoded_buffer *src = &readerinput->buffer->encoded;
466 encoded_buffer *dest = &readerinput->buffer->utf16;
472 hr = get_code_page(enc, &cp);
473 if (FAILED(hr)) return;
475 len = readerinput_get_convlen(readerinput, cp);
477 TRACE("switching to cp %d\n", cp);
479 /* just copy in this case */
480 if (enc == XmlEncoding_UTF16)
482 readerinput_grow(readerinput, len);
483 memcpy(dest->data, src->cur, len);
484 dest->written += len*sizeof(WCHAR);
485 readerinput->buffer->code_page = cp;
489 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
490 readerinput_grow(readerinput, dest_len);
491 ptr = (WCHAR*)dest->data;
492 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
494 readerinput->buffer->code_page = cp;
495 dest->written += dest_len*sizeof(WCHAR);
498 /* shrinks parsed data a buffer begins with */
499 static void reader_shrink(xmlreader *reader)
501 encoded_buffer *buffer = &reader->input->buffer->utf16;
503 /* avoid to move too often using threshold shrink length */
504 if (buffer->cur - buffer->data > buffer->written / 2)
506 buffer->written -= buffer->cur - buffer->data;
507 memmove(buffer->data, buffer->cur, buffer->written);
508 buffer->cur = buffer->data;
509 *(WCHAR*)&buffer->cur[buffer->written] = 0;
513 static inline const WCHAR *reader_get_cur(xmlreader *reader)
515 return (WCHAR*)reader->input->buffer->utf16.cur;
518 static int reader_cmp(xmlreader *reader, const WCHAR *str)
520 const WCHAR *ptr = reader_get_cur(reader);
521 return strncmpW(str, ptr, strlenW(str));
524 /* moves cursor n WCHARs forward */
525 static void reader_skipn(xmlreader *reader, int n)
527 encoded_buffer *buffer = &reader->input->buffer->utf16;
528 const WCHAR *ptr = reader_get_cur(reader);
530 while (*ptr++ && n--)
532 buffer->cur += sizeof(WCHAR);
537 static inline int is_wchar_space(WCHAR ch)
539 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
542 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
543 static int reader_skipspaces(xmlreader *reader)
545 encoded_buffer *buffer = &reader->input->buffer->utf16;
546 const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
548 while (is_wchar_space(*ptr))
550 buffer->cur += sizeof(WCHAR);
553 else if (*ptr == '\n')
566 /* [26] VersionNum ::= '1.' [0-9]+ */
567 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
569 const WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
570 static const WCHAR onedotW[] = {'1','.',0};
572 if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
574 reader_skipn(reader, 2);
576 ptr2 = ptr = reader_get_cur(reader);
577 while (*ptr >= '0' && *ptr <= '9')
580 if (ptr2 == ptr) return WC_E_DIGIT;
581 TRACE("version=%s\n", debugstr_wn(start, ptr-start));
583 val->len = ptr-start;
584 reader_skipn(reader, ptr-ptr2);
588 /* [25] Eq ::= S? '=' S? */
589 static HRESULT reader_parse_eq(xmlreader *reader)
591 static const WCHAR eqW[] = {'=',0};
592 reader_skipspaces(reader);
593 if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
595 reader_skipn(reader, 1);
596 reader_skipspaces(reader);
600 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
601 static HRESULT reader_parse_versioninfo(xmlreader *reader)
603 static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
607 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
609 if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
610 name.str = reader_get_cur(reader);
613 reader_skipn(reader, 7);
615 hr = reader_parse_eq(reader);
616 if (FAILED(hr)) return hr;
618 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
621 reader_skipn(reader, 1);
623 hr = reader_parse_versionnum(reader, &val);
624 if (FAILED(hr)) return hr;
626 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
630 reader_skipn(reader, 1);
632 return reader_add_attr(reader, &name, &val);
635 /* ([A-Za-z0-9._] | '-') */
636 static inline int is_wchar_encname(WCHAR ch)
638 return ((ch >= 'A' && ch <= 'Z') ||
639 (ch >= 'a' && ch <= 'z') ||
640 (ch >= '0' && ch <= '9') ||
641 (ch == '.') || (ch == '_') ||
645 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
646 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
648 const WCHAR *start = reader_get_cur(reader), *ptr;
652 if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
656 while (is_wchar_encname(*++ptr))
660 enc = parse_encoding_name(start, len);
661 TRACE("encoding name %s\n", debugstr_wn(start, len));
665 if (enc == XmlEncoding_Unknown)
668 /* skip encoding name */
669 reader_skipn(reader, len);
673 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
674 static HRESULT reader_parse_encdecl(xmlreader *reader)
676 static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
680 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
682 if (reader_cmp(reader, encodingW)) return S_FALSE;
683 name.str = reader_get_cur(reader);
685 /* skip 'encoding' */
686 reader_skipn(reader, 8);
688 hr = reader_parse_eq(reader);
689 if (FAILED(hr)) return hr;
691 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
694 reader_skipn(reader, 1);
696 hr = reader_parse_encname(reader, &val);
697 if (FAILED(hr)) return hr;
699 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
703 reader_skipn(reader, 1);
705 return reader_add_attr(reader, &name, &val);
708 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
709 static HRESULT reader_parse_sddecl(xmlreader *reader)
711 static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
712 static const WCHAR yesW[] = {'y','e','s',0};
713 static const WCHAR noW[] = {'n','o',0};
714 const WCHAR *start, *ptr;
718 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
720 if (reader_cmp(reader, standaloneW)) return S_FALSE;
721 name.str = reader_get_cur(reader);
723 /* skip 'standalone' */
724 reader_skipn(reader, 10);
726 hr = reader_parse_eq(reader);
727 if (FAILED(hr)) return hr;
729 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
732 reader_skipn(reader, 1);
734 if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
737 start = reader_get_cur(reader);
738 /* skip 'yes'|'no' */
739 reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
740 ptr = reader_get_cur(reader);
741 TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
745 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
748 reader_skipn(reader, 1);
750 return reader_add_attr(reader, &name, &val);
753 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
754 static HRESULT reader_parse_xmldecl(xmlreader *reader)
756 static const WCHAR xmldeclW[] = {'<','?','x','m','l',0};
757 static const WCHAR declcloseW[] = {'?','>',0};
760 /* check if we have "<?xml" */
761 if (reader_cmp(reader, xmldeclW)) return S_FALSE;
763 reader_skipn(reader, 5);
764 hr = reader_parse_versioninfo(reader);
768 hr = reader_parse_encdecl(reader);
772 hr = reader_parse_sddecl(reader);
776 reader_skipspaces(reader);
777 if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
778 reader_skipn(reader, 2);
780 reader->nodetype = XmlNodeType_XmlDeclaration;
785 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
786 static HRESULT reader_parse_comment(xmlreader *reader)
788 const WCHAR *start, *ptr;
791 reader_skipn(reader, 4);
792 reader_shrink(reader);
793 ptr = start = reader_get_cur(reader);
797 if (ptr[0] == '-' && ptr[1] == '-')
801 TRACE("%s\n", debugstr_wn(start, ptr-start));
803 reader_skipn(reader, 3);
804 reader->nodetype = XmlNodeType_Comment;
812 reader_skipn(reader, 1);
813 ptr = reader_get_cur(reader);
817 return MX_E_INPUTEND;
820 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
821 static HRESULT reader_parse_pi(xmlreader *reader)
823 FIXME("PI not supported\n");
827 /* [27] Misc ::= Comment | PI | S */
828 static HRESULT reader_parse_misc(xmlreader *reader)
830 HRESULT hr = S_FALSE;
834 static const WCHAR commentW[] = {'<','!','-','-',0};
835 static const WCHAR piW[] = {'<','?',0};
836 const WCHAR *cur = reader_get_cur(reader);
838 if (is_wchar_space(*cur))
839 reader_skipspaces(reader);
840 else if (!reader_cmp(reader, commentW))
841 hr = reader_parse_comment(reader);
842 else if (!reader_cmp(reader, piW))
843 hr = reader_parse_pi(reader);
847 if (FAILED(hr)) return hr;
853 static HRESULT reader_parse_nextnode(xmlreader *reader)
859 switch (reader->instate)
861 /* if it's a first call for a new input we need to detect stream encoding */
862 case XmlReadInState_Initial:
866 hr = readerinput_growraw(reader->input);
867 if (FAILED(hr)) return hr;
869 /* try to detect encoding by BOM or data and set input code page */
870 hr = readerinput_detectencoding(reader->input, &enc);
871 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
872 if (FAILED(hr)) return hr;
874 /* always switch first time cause we have to put something in */
875 readerinput_switchencoding(reader->input, enc);
877 /* parse xml declaration */
878 hr = reader_parse_xmldecl(reader);
879 if (FAILED(hr)) return hr;
881 reader->instate = XmlReadInState_Misc_DTD;
882 if (hr == S_OK) return hr;
885 case XmlReadInState_Misc_DTD:
886 hr = reader_parse_misc(reader);
887 if (FAILED(hr)) return hr;
890 reader->instate = XmlReadInState_DTD;
894 case XmlReadInState_DTD:
895 FIXME("DTD parsing not supported\n");
898 FIXME("internal state %d not handled\n", reader->instate);
906 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
908 xmlreader *This = impl_from_IXmlReader(iface);
910 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
912 if (IsEqualGUID(riid, &IID_IUnknown) ||
913 IsEqualGUID(riid, &IID_IXmlReader))
919 FIXME("interface %s not implemented\n", debugstr_guid(riid));
920 return E_NOINTERFACE;
923 IXmlReader_AddRef(iface);
928 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
930 xmlreader *This = impl_from_IXmlReader(iface);
931 ULONG ref = InterlockedIncrement(&This->ref);
932 TRACE("(%p)->(%d)\n", This, ref);
936 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
938 xmlreader *This = impl_from_IXmlReader(iface);
939 LONG ref = InterlockedDecrement(&This->ref);
941 TRACE("(%p)->(%d)\n", This, ref);
945 IMalloc *imalloc = This->imalloc;
946 if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
947 reader_clear_attrs(This);
948 reader_free(This, This);
949 if (imalloc) IMalloc_Release(imalloc);
955 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
957 xmlreader *This = impl_from_IXmlReader(iface);
960 TRACE("(%p %p)\n", This, input);
964 readerinput_release_stream(This->input);
965 IUnknown_Release(&This->input->IXmlReaderInput_iface);
969 This->line = This->pos = 0;
971 /* just reset current input */
974 This->state = XmlReadState_Initial;
978 /* now try IXmlReaderInput, ISequentialStream, IStream */
979 hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
982 IXmlReaderInput *readerinput;
984 /* create IXmlReaderInput basing on supplied interface */
985 hr = CreateXmlReaderInputWithEncodingName(input,
986 NULL, NULL, FALSE, NULL, &readerinput);
987 if (hr != S_OK) return hr;
988 This->input = impl_from_IXmlReaderInput(readerinput);
991 /* set stream for supplied IXmlReaderInput */
992 hr = readerinput_query_for_stream(This->input);
995 This->state = XmlReadState_Initial;
996 This->instate = XmlReadInState_Initial;
1002 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1004 xmlreader *This = impl_from_IXmlReader(iface);
1006 TRACE("(%p %u %p)\n", This, property, value);
1008 if (!value) return E_INVALIDARG;
1012 case XmlReaderProperty_DtdProcessing:
1013 *value = This->dtdmode;
1015 case XmlReaderProperty_ReadState:
1016 *value = This->state;
1019 FIXME("Unimplemented property (%u)\n", property);
1026 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1028 xmlreader *This = impl_from_IXmlReader(iface);
1030 TRACE("(%p %u %lu)\n", iface, property, value);
1034 case XmlReaderProperty_DtdProcessing:
1035 if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1036 This->dtdmode = value;
1039 FIXME("Unimplemented property (%u)\n", property);
1046 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1048 xmlreader *This = impl_from_IXmlReader(iface);
1049 XmlNodeType oldtype = This->nodetype;
1052 TRACE("(%p)->(%p)\n", This, nodetype);
1054 if (This->state == XmlReadState_Closed) return S_FALSE;
1056 hr = reader_parse_nextnode(This);
1057 if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
1058 This->state = XmlReadState_Interactive;
1059 if (hr == S_OK) *nodetype = This->nodetype;
1064 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1066 xmlreader *This = impl_from_IXmlReader(iface);
1067 TRACE("(%p)->(%p)\n", This, node_type);
1069 /* When we're on attribute always return attribute type, container node type is kept.
1070 Note that container is not necessarily an element, and attribute doesn't mean it's
1071 an attribute in XML spec terms. */
1072 *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
1073 return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
1076 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1078 xmlreader *This = impl_from_IXmlReader(iface);
1080 TRACE("(%p)\n", This);
1082 if (!This->attr_count) return S_FALSE;
1083 This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1087 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1089 xmlreader *This = impl_from_IXmlReader(iface);
1090 const struct list *next;
1092 TRACE("(%p)\n", This);
1094 if (!This->attr_count) return S_FALSE;
1097 return IXmlReader_MoveToFirstAttribute(iface);
1099 next = list_next(&This->attrs, &This->attr->entry);
1101 This->attr = LIST_ENTRY(next, struct attribute, entry);
1103 return next ? S_OK : S_FALSE;
1106 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1108 LPCWSTR namespaceUri)
1110 FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1114 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1116 xmlreader *This = impl_from_IXmlReader(iface);
1118 TRACE("(%p)\n", This);
1120 if (!This->attr_count) return S_FALSE;
1125 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *qualifiedName,
1126 UINT *qualifiedName_length)
1128 FIXME("(%p %p %p): stub\n", iface, qualifiedName, qualifiedName_length);
1132 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1133 LPCWSTR *namespaceUri,
1134 UINT *namespaceUri_length)
1136 FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1140 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface,
1141 LPCWSTR *local_name,
1142 UINT *local_name_length)
1144 FIXME("(%p %p %p): stub\n", iface, local_name, local_name_length);
1148 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1150 UINT *prefix_length)
1152 FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1156 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface,
1160 FIXME("(%p %p %p): stub\n", iface, value, value_length);
1164 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1169 FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1173 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1175 UINT *baseUri_length)
1177 FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1181 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1183 FIXME("(%p): stub\n", iface);
1187 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1189 FIXME("(%p): stub\n", iface);
1193 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1195 xmlreader *This = impl_from_IXmlReader(iface);
1197 TRACE("(%p %p)\n", This, lineNumber);
1199 if (!lineNumber) return E_INVALIDARG;
1201 *lineNumber = This->line;
1206 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1208 xmlreader *This = impl_from_IXmlReader(iface);
1210 TRACE("(%p %p)\n", This, linePosition);
1212 if (!linePosition) return E_INVALIDARG;
1214 *linePosition = This->pos;
1219 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1221 xmlreader *This = impl_from_IXmlReader(iface);
1223 TRACE("(%p)->(%p)\n", This, count);
1225 if (!count) return E_INVALIDARG;
1227 *count = This->attr_count;
1231 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1233 FIXME("(%p %p): stub\n", iface, depth);
1237 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1239 FIXME("(%p): stub\n", iface);
1243 static const struct IXmlReaderVtbl xmlreader_vtbl =
1245 xmlreader_QueryInterface,
1249 xmlreader_GetProperty,
1250 xmlreader_SetProperty,
1252 xmlreader_GetNodeType,
1253 xmlreader_MoveToFirstAttribute,
1254 xmlreader_MoveToNextAttribute,
1255 xmlreader_MoveToAttributeByName,
1256 xmlreader_MoveToElement,
1257 xmlreader_GetQualifiedName,
1258 xmlreader_GetNamespaceUri,
1259 xmlreader_GetLocalName,
1260 xmlreader_GetPrefix,
1262 xmlreader_ReadValueChunk,
1263 xmlreader_GetBaseUri,
1264 xmlreader_IsDefault,
1265 xmlreader_IsEmptyElement,
1266 xmlreader_GetLineNumber,
1267 xmlreader_GetLinePosition,
1268 xmlreader_GetAttributeCount,
1273 /** IXmlReaderInput **/
1274 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1276 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1278 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1280 if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1281 IsEqualGUID(riid, &IID_IUnknown))
1287 WARN("interface %s not implemented\n", debugstr_guid(riid));
1288 return E_NOINTERFACE;
1291 IUnknown_AddRef(iface);
1296 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1298 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1299 ULONG ref = InterlockedIncrement(&This->ref);
1300 TRACE("(%p)->(%d)\n", This, ref);
1304 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1306 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1307 LONG ref = InterlockedDecrement(&This->ref);
1309 TRACE("(%p)->(%d)\n", This, ref);
1313 IMalloc *imalloc = This->imalloc;
1314 if (This->input) IUnknown_Release(This->input);
1315 if (This->stream) ISequentialStream_Release(This->stream);
1316 if (This->buffer) free_input_buffer(This->buffer);
1317 readerinput_free(This, This->baseuri);
1318 readerinput_free(This, This);
1319 if (imalloc) IMalloc_Release(imalloc);
1325 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1327 xmlreaderinput_QueryInterface,
1328 xmlreaderinput_AddRef,
1329 xmlreaderinput_Release
1332 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1336 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1338 if (!IsEqualGUID(riid, &IID_IXmlReader))
1340 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1345 reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1347 reader = heap_alloc(sizeof(*reader));
1348 if(!reader) return E_OUTOFMEMORY;
1350 reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1352 reader->input = NULL;
1353 reader->state = XmlReadState_Closed;
1354 reader->instate = XmlReadInState_Initial;
1355 reader->dtdmode = DtdProcessing_Prohibit;
1356 reader->line = reader->pos = 0;
1357 reader->imalloc = imalloc;
1358 if (imalloc) IMalloc_AddRef(imalloc);
1359 reader->nodetype = XmlNodeType_None;
1360 list_init(&reader->attrs);
1361 reader->attr_count = 0;
1362 reader->attr = NULL;
1364 *obj = &reader->IXmlReader_iface;
1366 TRACE("returning iface %p\n", *obj);
1371 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1376 IXmlReaderInput **ppInput)
1378 xmlreaderinput *readerinput;
1381 TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1382 hint, wine_dbgstr_w(base_uri), ppInput);
1384 if (!stream || !ppInput) return E_INVALIDARG;
1387 readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1389 readerinput = heap_alloc(sizeof(*readerinput));
1390 if(!readerinput) return E_OUTOFMEMORY;
1392 readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinput_vtbl;
1393 readerinput->ref = 1;
1394 readerinput->imalloc = imalloc;
1395 readerinput->stream = NULL;
1396 if (imalloc) IMalloc_AddRef(imalloc);
1397 readerinput->encoding = parse_encoding_name(encoding, -1);
1398 readerinput->hint = hint;
1399 readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
1401 hr = alloc_input_buffer(readerinput);
1404 readerinput_free(readerinput, readerinput);
1407 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1409 *ppInput = &readerinput->IXmlReaderInput_iface;
1411 TRACE("returning iface %p\n", *ppInput);