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 /* to make sure aligned length won't exceed allocated length */
365 ULONG len = buffer->allocated - buffer->written - 4;
369 /* always try to get aligned to 4 bytes, so the only case we can get partialy read characters is
370 variable width encodings like UTF-8 */
371 len = (len + 3) & ~3;
372 /* try to use allocated space or grow */
373 if (buffer->allocated - buffer->written < len)
375 buffer->allocated *= 2;
376 buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
377 len = buffer->allocated - buffer->written;
380 hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
381 if (FAILED(hr)) return hr;
382 TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
383 buffer->written += read;
388 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
389 static void readerinput_grow(xmlreaderinput *readerinput, int length)
391 encoded_buffer *buffer = &readerinput->buffer->utf16;
393 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
394 if (buffer->allocated < buffer->written + length + 4)
396 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
397 buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
398 buffer->allocated = grown_size;
402 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
404 encoded_buffer *buffer = &readerinput->buffer->encoded;
405 static char startA[] = {'<','?','x','m'};
406 static WCHAR startW[] = {'<','?'};
407 static char utf8bom[] = {0xef,0xbb,0xbf};
408 static char utf16lebom[] = {0xff,0xfe};
410 *enc = XmlEncoding_Unknown;
412 if (buffer->written <= 3) return MX_E_INPUTEND;
414 /* try start symbols if we have enough data to do that, input buffer should contain
415 first chunk already */
416 if (!memcmp(buffer->data, startA, sizeof(startA)))
417 *enc = XmlEncoding_UTF8;
418 else if (!memcmp(buffer->data, startW, sizeof(startW)))
419 *enc = XmlEncoding_UTF16;
420 /* try with BOM now */
421 else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
423 buffer->cur += sizeof(utf8bom);
424 *enc = XmlEncoding_UTF8;
426 else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
428 buffer->cur += sizeof(utf16lebom);
429 *enc = XmlEncoding_UTF16;
435 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
437 encoded_buffer *buffer = &readerinput->buffer->encoded;
438 int len = buffer->written;
440 /* complete single byte char */
441 if (!(buffer->data[len-1] & 0x80)) return len;
443 /* find start byte of multibyte char */
444 while (--len && !(buffer->data[len] & 0xc0))
450 /* Returns byte length of complete char sequence for buffer code page,
451 it's relative to current buffer position which is currently used for BOM handling
453 static int readerinput_get_convlen(xmlreaderinput *readerinput)
455 encoded_buffer *buffer = &readerinput->buffer->encoded;
458 if (readerinput->buffer->code_page == CP_UTF8)
459 len = readerinput_get_utf8_convlen(readerinput);
461 len = buffer->written;
463 TRACE("%d\n", len - (int)(buffer->cur - buffer->data));
464 return len - (buffer->cur - buffer->data);
467 /* It's possbile that raw buffer has some leftovers from last conversion - some char
468 sequence that doesn't represent a full code point. Length argument should be calculated with
469 readerinput_get_convlen(). */
470 static void readerinput_shrinkraw(xmlreaderinput *readerinput, int len)
472 encoded_buffer *buffer = &readerinput->buffer->encoded;
473 memmove(buffer->data, buffer->cur + (buffer->written - len), len);
474 /* everything lower cur is lost too */
475 buffer->written -= len + (buffer->cur - buffer->data);
476 /* after this point we don't need cur pointer really,
477 it's used only to mark where actual data begins when first chunk is read */
478 buffer->cur = buffer->data;
481 /* note that raw buffer content is kept */
482 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
484 encoded_buffer *src = &readerinput->buffer->encoded;
485 encoded_buffer *dest = &readerinput->buffer->utf16;
491 hr = get_code_page(enc, &cp);
492 if (FAILED(hr)) return;
494 readerinput->buffer->code_page = cp;
495 len = readerinput_get_convlen(readerinput);
497 TRACE("switching to cp %d\n", cp);
499 /* just copy in this case */
500 if (enc == XmlEncoding_UTF16)
502 readerinput_grow(readerinput, len);
503 memcpy(dest->data, src->cur, len);
504 dest->written += len*sizeof(WCHAR);
508 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
509 readerinput_grow(readerinput, dest_len);
510 ptr = (WCHAR*)dest->data;
511 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
513 dest->written += dest_len*sizeof(WCHAR);
516 /* shrinks parsed data a buffer begins with */
517 static void reader_shrink(xmlreader *reader)
519 encoded_buffer *buffer = &reader->input->buffer->utf16;
521 /* avoid to move too often using threshold shrink length */
522 if (buffer->cur - buffer->data > buffer->written / 2)
524 buffer->written -= buffer->cur - buffer->data;
525 memmove(buffer->data, buffer->cur, buffer->written);
526 buffer->cur = buffer->data;
527 *(WCHAR*)&buffer->cur[buffer->written] = 0;
531 /* This is a normal way for reader to get new data converted from raw buffer to utf16 buffer.
532 It won't attempt to shrink but will grow destination buffer if needed */
533 static void reader_more(xmlreader *reader)
535 xmlreaderinput *readerinput = reader->input;
536 encoded_buffer *src = &readerinput->buffer->encoded;
537 encoded_buffer *dest = &readerinput->buffer->utf16;
538 UINT cp = readerinput->buffer->code_page;
542 /* get some raw data from stream first */
543 readerinput_growraw(readerinput);
544 len = readerinput_get_convlen(readerinput);
546 /* just copy for UTF-16 case */
549 readerinput_grow(readerinput, len);
550 memcpy(dest->data, src->cur, len);
551 dest->written += len*sizeof(WCHAR);
555 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
556 readerinput_grow(readerinput, dest_len);
557 ptr = (WCHAR*)dest->data;
558 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
560 dest->written += dest_len*sizeof(WCHAR);
561 /* get rid of processed data */
562 readerinput_shrinkraw(readerinput, len);
565 static inline const WCHAR *reader_get_cur(xmlreader *reader)
567 WCHAR *ptr = (WCHAR*)reader->input->buffer->utf16.cur;
568 if (!*ptr) reader_more(reader);
572 static int reader_cmp(xmlreader *reader, const WCHAR *str)
574 const WCHAR *ptr = reader_get_cur(reader);
575 return strncmpW(str, ptr, strlenW(str));
578 /* moves cursor n WCHARs forward */
579 static void reader_skipn(xmlreader *reader, int n)
581 encoded_buffer *buffer = &reader->input->buffer->utf16;
582 const WCHAR *ptr = reader_get_cur(reader);
584 while (*ptr++ && n--)
586 buffer->cur += sizeof(WCHAR);
591 static inline int is_wchar_space(WCHAR ch)
593 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
596 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
597 static int reader_skipspaces(xmlreader *reader)
599 encoded_buffer *buffer = &reader->input->buffer->utf16;
600 const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
602 while (is_wchar_space(*ptr))
604 buffer->cur += sizeof(WCHAR);
607 else if (*ptr == '\n')
620 /* [26] VersionNum ::= '1.' [0-9]+ */
621 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
623 const WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
624 static const WCHAR onedotW[] = {'1','.',0};
626 if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
628 reader_skipn(reader, 2);
630 ptr2 = ptr = reader_get_cur(reader);
631 while (*ptr >= '0' && *ptr <= '9')
634 if (ptr2 == ptr) return WC_E_DIGIT;
635 TRACE("version=%s\n", debugstr_wn(start, ptr-start));
637 val->len = ptr-start;
638 reader_skipn(reader, ptr-ptr2);
642 /* [25] Eq ::= S? '=' S? */
643 static HRESULT reader_parse_eq(xmlreader *reader)
645 static const WCHAR eqW[] = {'=',0};
646 reader_skipspaces(reader);
647 if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
649 reader_skipn(reader, 1);
650 reader_skipspaces(reader);
654 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
655 static HRESULT reader_parse_versioninfo(xmlreader *reader)
657 static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
661 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
663 if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
664 name.str = reader_get_cur(reader);
667 reader_skipn(reader, 7);
669 hr = reader_parse_eq(reader);
670 if (FAILED(hr)) return hr;
672 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
675 reader_skipn(reader, 1);
677 hr = reader_parse_versionnum(reader, &val);
678 if (FAILED(hr)) return hr;
680 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
684 reader_skipn(reader, 1);
686 return reader_add_attr(reader, &name, &val);
689 /* ([A-Za-z0-9._] | '-') */
690 static inline int is_wchar_encname(WCHAR ch)
692 return ((ch >= 'A' && ch <= 'Z') ||
693 (ch >= 'a' && ch <= 'z') ||
694 (ch >= '0' && ch <= '9') ||
695 (ch == '.') || (ch == '_') ||
699 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
700 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
702 const WCHAR *start = reader_get_cur(reader), *ptr;
706 if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
710 while (is_wchar_encname(*++ptr))
714 enc = parse_encoding_name(start, len);
715 TRACE("encoding name %s\n", debugstr_wn(start, len));
719 if (enc == XmlEncoding_Unknown)
722 /* skip encoding name */
723 reader_skipn(reader, len);
727 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
728 static HRESULT reader_parse_encdecl(xmlreader *reader)
730 static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
734 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
736 if (reader_cmp(reader, encodingW)) return S_FALSE;
737 name.str = reader_get_cur(reader);
739 /* skip 'encoding' */
740 reader_skipn(reader, 8);
742 hr = reader_parse_eq(reader);
743 if (FAILED(hr)) return hr;
745 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
748 reader_skipn(reader, 1);
750 hr = reader_parse_encname(reader, &val);
751 if (FAILED(hr)) return hr;
753 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
757 reader_skipn(reader, 1);
759 return reader_add_attr(reader, &name, &val);
762 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
763 static HRESULT reader_parse_sddecl(xmlreader *reader)
765 static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
766 static const WCHAR yesW[] = {'y','e','s',0};
767 static const WCHAR noW[] = {'n','o',0};
768 const WCHAR *start, *ptr;
772 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
774 if (reader_cmp(reader, standaloneW)) return S_FALSE;
775 name.str = reader_get_cur(reader);
777 /* skip 'standalone' */
778 reader_skipn(reader, 10);
780 hr = reader_parse_eq(reader);
781 if (FAILED(hr)) return hr;
783 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
786 reader_skipn(reader, 1);
788 if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
791 start = reader_get_cur(reader);
792 /* skip 'yes'|'no' */
793 reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
794 ptr = reader_get_cur(reader);
795 TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
799 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
802 reader_skipn(reader, 1);
804 return reader_add_attr(reader, &name, &val);
807 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
808 static HRESULT reader_parse_xmldecl(xmlreader *reader)
810 static const WCHAR xmldeclW[] = {'<','?','x','m','l',0};
811 static const WCHAR declcloseW[] = {'?','>',0};
814 /* check if we have "<?xml" */
815 if (reader_cmp(reader, xmldeclW)) return S_FALSE;
817 reader_skipn(reader, 5);
818 hr = reader_parse_versioninfo(reader);
822 hr = reader_parse_encdecl(reader);
826 hr = reader_parse_sddecl(reader);
830 reader_skipspaces(reader);
831 if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
832 reader_skipn(reader, 2);
834 reader->nodetype = XmlNodeType_XmlDeclaration;
839 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
840 static HRESULT reader_parse_comment(xmlreader *reader)
842 const WCHAR *start, *ptr;
845 reader_skipn(reader, 4);
846 reader_shrink(reader);
847 ptr = start = reader_get_cur(reader);
857 TRACE("%s\n", debugstr_wn(start, ptr-start));
859 reader_skipn(reader, 3);
860 reader->nodetype = XmlNodeType_Comment;
874 reader_skipn(reader, 1);
875 ptr = reader_get_cur(reader);
879 return MX_E_INPUTEND;
882 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
883 static HRESULT reader_parse_pi(xmlreader *reader)
885 FIXME("PI not supported\n");
889 /* [27] Misc ::= Comment | PI | S */
890 static HRESULT reader_parse_misc(xmlreader *reader)
892 HRESULT hr = S_FALSE;
896 static const WCHAR commentW[] = {'<','!','-','-',0};
897 static const WCHAR piW[] = {'<','?',0};
898 const WCHAR *cur = reader_get_cur(reader);
900 if (is_wchar_space(*cur))
901 reader_skipspaces(reader);
902 else if (!reader_cmp(reader, commentW))
903 hr = reader_parse_comment(reader);
904 else if (!reader_cmp(reader, piW))
905 hr = reader_parse_pi(reader);
909 if (FAILED(hr)) return hr;
915 static HRESULT reader_parse_nextnode(xmlreader *reader)
921 switch (reader->instate)
923 /* if it's a first call for a new input we need to detect stream encoding */
924 case XmlReadInState_Initial:
928 hr = readerinput_growraw(reader->input);
929 if (FAILED(hr)) return hr;
931 /* try to detect encoding by BOM or data and set input code page */
932 hr = readerinput_detectencoding(reader->input, &enc);
933 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
934 if (FAILED(hr)) return hr;
936 /* always switch first time cause we have to put something in */
937 readerinput_switchencoding(reader->input, enc);
939 /* parse xml declaration */
940 hr = reader_parse_xmldecl(reader);
941 if (FAILED(hr)) return hr;
943 reader->instate = XmlReadInState_Misc_DTD;
944 if (hr == S_OK) return hr;
947 case XmlReadInState_Misc_DTD:
948 hr = reader_parse_misc(reader);
949 if (FAILED(hr)) return hr;
952 reader->instate = XmlReadInState_DTD;
956 case XmlReadInState_DTD:
957 FIXME("DTD parsing not supported\n");
960 FIXME("internal state %d not handled\n", reader->instate);
968 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
970 xmlreader *This = impl_from_IXmlReader(iface);
972 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
974 if (IsEqualGUID(riid, &IID_IUnknown) ||
975 IsEqualGUID(riid, &IID_IXmlReader))
981 FIXME("interface %s not implemented\n", debugstr_guid(riid));
982 return E_NOINTERFACE;
985 IXmlReader_AddRef(iface);
990 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
992 xmlreader *This = impl_from_IXmlReader(iface);
993 ULONG ref = InterlockedIncrement(&This->ref);
994 TRACE("(%p)->(%d)\n", This, ref);
998 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
1000 xmlreader *This = impl_from_IXmlReader(iface);
1001 LONG ref = InterlockedDecrement(&This->ref);
1003 TRACE("(%p)->(%d)\n", This, ref);
1007 IMalloc *imalloc = This->imalloc;
1008 if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
1009 reader_clear_attrs(This);
1010 reader_free(This, This);
1011 if (imalloc) IMalloc_Release(imalloc);
1017 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
1019 xmlreader *This = impl_from_IXmlReader(iface);
1022 TRACE("(%p %p)\n", This, input);
1026 readerinput_release_stream(This->input);
1027 IUnknown_Release(&This->input->IXmlReaderInput_iface);
1031 This->line = This->pos = 0;
1033 /* just reset current input */
1036 This->state = XmlReadState_Initial;
1040 /* now try IXmlReaderInput, ISequentialStream, IStream */
1041 hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
1044 IXmlReaderInput *readerinput;
1046 /* create IXmlReaderInput basing on supplied interface */
1047 hr = CreateXmlReaderInputWithEncodingName(input,
1048 NULL, NULL, FALSE, NULL, &readerinput);
1049 if (hr != S_OK) return hr;
1050 This->input = impl_from_IXmlReaderInput(readerinput);
1053 /* set stream for supplied IXmlReaderInput */
1054 hr = readerinput_query_for_stream(This->input);
1057 This->state = XmlReadState_Initial;
1058 This->instate = XmlReadInState_Initial;
1064 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1066 xmlreader *This = impl_from_IXmlReader(iface);
1068 TRACE("(%p %u %p)\n", This, property, value);
1070 if (!value) return E_INVALIDARG;
1074 case XmlReaderProperty_DtdProcessing:
1075 *value = This->dtdmode;
1077 case XmlReaderProperty_ReadState:
1078 *value = This->state;
1081 FIXME("Unimplemented property (%u)\n", property);
1088 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1090 xmlreader *This = impl_from_IXmlReader(iface);
1092 TRACE("(%p %u %lu)\n", iface, property, value);
1096 case XmlReaderProperty_DtdProcessing:
1097 if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1098 This->dtdmode = value;
1101 FIXME("Unimplemented property (%u)\n", property);
1108 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1110 xmlreader *This = impl_from_IXmlReader(iface);
1111 XmlNodeType oldtype = This->nodetype;
1114 TRACE("(%p)->(%p)\n", This, nodetype);
1116 if (This->state == XmlReadState_Closed) return S_FALSE;
1118 hr = reader_parse_nextnode(This);
1119 if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
1120 This->state = XmlReadState_Interactive;
1121 if (hr == S_OK) *nodetype = This->nodetype;
1126 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1128 xmlreader *This = impl_from_IXmlReader(iface);
1129 TRACE("(%p)->(%p)\n", This, node_type);
1131 /* When we're on attribute always return attribute type, container node type is kept.
1132 Note that container is not necessarily an element, and attribute doesn't mean it's
1133 an attribute in XML spec terms. */
1134 *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
1135 return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
1138 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1140 xmlreader *This = impl_from_IXmlReader(iface);
1142 TRACE("(%p)\n", This);
1144 if (!This->attr_count) return S_FALSE;
1145 This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1149 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1151 xmlreader *This = impl_from_IXmlReader(iface);
1152 const struct list *next;
1154 TRACE("(%p)\n", This);
1156 if (!This->attr_count) return S_FALSE;
1159 return IXmlReader_MoveToFirstAttribute(iface);
1161 next = list_next(&This->attrs, &This->attr->entry);
1163 This->attr = LIST_ENTRY(next, struct attribute, entry);
1165 return next ? S_OK : S_FALSE;
1168 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1170 LPCWSTR namespaceUri)
1172 FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1176 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1178 xmlreader *This = impl_from_IXmlReader(iface);
1180 TRACE("(%p)\n", This);
1182 if (!This->attr_count) return S_FALSE;
1187 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *qualifiedName,
1188 UINT *qualifiedName_length)
1190 FIXME("(%p %p %p): stub\n", iface, qualifiedName, qualifiedName_length);
1194 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1195 LPCWSTR *namespaceUri,
1196 UINT *namespaceUri_length)
1198 FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1202 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface,
1203 LPCWSTR *local_name,
1204 UINT *local_name_length)
1206 FIXME("(%p %p %p): stub\n", iface, local_name, local_name_length);
1210 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1212 UINT *prefix_length)
1214 FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1218 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface,
1222 FIXME("(%p %p %p): stub\n", iface, value, value_length);
1226 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1231 FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1235 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1237 UINT *baseUri_length)
1239 FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1243 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1245 FIXME("(%p): stub\n", iface);
1249 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1251 FIXME("(%p): stub\n", iface);
1255 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1257 xmlreader *This = impl_from_IXmlReader(iface);
1259 TRACE("(%p %p)\n", This, lineNumber);
1261 if (!lineNumber) return E_INVALIDARG;
1263 *lineNumber = This->line;
1268 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1270 xmlreader *This = impl_from_IXmlReader(iface);
1272 TRACE("(%p %p)\n", This, linePosition);
1274 if (!linePosition) return E_INVALIDARG;
1276 *linePosition = This->pos;
1281 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1283 xmlreader *This = impl_from_IXmlReader(iface);
1285 TRACE("(%p)->(%p)\n", This, count);
1287 if (!count) return E_INVALIDARG;
1289 *count = This->attr_count;
1293 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1295 FIXME("(%p %p): stub\n", iface, depth);
1299 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1301 FIXME("(%p): stub\n", iface);
1305 static const struct IXmlReaderVtbl xmlreader_vtbl =
1307 xmlreader_QueryInterface,
1311 xmlreader_GetProperty,
1312 xmlreader_SetProperty,
1314 xmlreader_GetNodeType,
1315 xmlreader_MoveToFirstAttribute,
1316 xmlreader_MoveToNextAttribute,
1317 xmlreader_MoveToAttributeByName,
1318 xmlreader_MoveToElement,
1319 xmlreader_GetQualifiedName,
1320 xmlreader_GetNamespaceUri,
1321 xmlreader_GetLocalName,
1322 xmlreader_GetPrefix,
1324 xmlreader_ReadValueChunk,
1325 xmlreader_GetBaseUri,
1326 xmlreader_IsDefault,
1327 xmlreader_IsEmptyElement,
1328 xmlreader_GetLineNumber,
1329 xmlreader_GetLinePosition,
1330 xmlreader_GetAttributeCount,
1335 /** IXmlReaderInput **/
1336 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1338 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1340 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1342 if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1343 IsEqualGUID(riid, &IID_IUnknown))
1349 WARN("interface %s not implemented\n", debugstr_guid(riid));
1350 return E_NOINTERFACE;
1353 IUnknown_AddRef(iface);
1358 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1360 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1361 ULONG ref = InterlockedIncrement(&This->ref);
1362 TRACE("(%p)->(%d)\n", This, ref);
1366 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1368 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1369 LONG ref = InterlockedDecrement(&This->ref);
1371 TRACE("(%p)->(%d)\n", This, ref);
1375 IMalloc *imalloc = This->imalloc;
1376 if (This->input) IUnknown_Release(This->input);
1377 if (This->stream) ISequentialStream_Release(This->stream);
1378 if (This->buffer) free_input_buffer(This->buffer);
1379 readerinput_free(This, This->baseuri);
1380 readerinput_free(This, This);
1381 if (imalloc) IMalloc_Release(imalloc);
1387 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1389 xmlreaderinput_QueryInterface,
1390 xmlreaderinput_AddRef,
1391 xmlreaderinput_Release
1394 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1398 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1400 if (!IsEqualGUID(riid, &IID_IXmlReader))
1402 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1407 reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1409 reader = heap_alloc(sizeof(*reader));
1410 if(!reader) return E_OUTOFMEMORY;
1412 reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1414 reader->input = NULL;
1415 reader->state = XmlReadState_Closed;
1416 reader->instate = XmlReadInState_Initial;
1417 reader->dtdmode = DtdProcessing_Prohibit;
1418 reader->line = reader->pos = 0;
1419 reader->imalloc = imalloc;
1420 if (imalloc) IMalloc_AddRef(imalloc);
1421 reader->nodetype = XmlNodeType_None;
1422 list_init(&reader->attrs);
1423 reader->attr_count = 0;
1424 reader->attr = NULL;
1426 *obj = &reader->IXmlReader_iface;
1428 TRACE("returning iface %p\n", *obj);
1433 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1438 IXmlReaderInput **ppInput)
1440 xmlreaderinput *readerinput;
1443 TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1444 hint, wine_dbgstr_w(base_uri), ppInput);
1446 if (!stream || !ppInput) return E_INVALIDARG;
1449 readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1451 readerinput = heap_alloc(sizeof(*readerinput));
1452 if(!readerinput) return E_OUTOFMEMORY;
1454 readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinput_vtbl;
1455 readerinput->ref = 1;
1456 readerinput->imalloc = imalloc;
1457 readerinput->stream = NULL;
1458 if (imalloc) IMalloc_AddRef(imalloc);
1459 readerinput->encoding = parse_encoding_name(encoding, -1);
1460 readerinput->hint = hint;
1461 readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
1463 hr = alloc_input_buffer(readerinput);
1466 readerinput_free(readerinput, readerinput);
1469 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1471 *ppInput = &readerinput->IXmlReaderInput_iface;
1473 TRACE("returning iface %p\n", *ppInput);