xmllite: Implement decoding new input chunk from stream, use it for comment parsing.
[wine] / dlls / xmllite / reader.c
1 /*
2  * IXmlReader implementation
3  *
4  * Copyright 2010, 2012 Nikolay Sivov
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "initguid.h"
27 #include "objbase.h"
28 #include "xmllite.h"
29 #include "xmllite_private.h"
30
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
36
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlReaderInput, 0x0b3ccc9b, 0x9214, 0x428b, 0xa2, 0xae, 0xef, 0x3a, 0xa8, 0x71, 0xaf, 0xda);
39
40 typedef enum
41 {
42     XmlEncoding_UTF16,
43     XmlEncoding_UTF8,
44     XmlEncoding_Unknown
45 } xml_encoding;
46
47 typedef enum
48 {
49     XmlReadInState_Initial,
50     XmlReadInState_XmlDecl,
51     XmlReadInState_Misc_DTD,
52     XmlReadInState_DTD
53 } XmlReaderInternalState;
54
55 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
56 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
57
58 static const WCHAR dblquoteW[] = {'\"',0};
59 static const WCHAR quoteW[] = {'\'',0};
60
61 struct xml_encoding_data
62 {
63     const WCHAR *name;
64     xml_encoding enc;
65     UINT cp;
66 };
67
68 static const struct xml_encoding_data xml_encoding_map[] = {
69     { utf16W, XmlEncoding_UTF16, ~0 },
70     { utf8W,  XmlEncoding_UTF8,  CP_UTF8 }
71 };
72
73 typedef struct
74 {
75     char *data;
76     char *cur;
77     unsigned int allocated;
78     unsigned int written;
79 } encoded_buffer;
80
81 typedef struct input_buffer input_buffer;
82
83 typedef struct _xmlreaderinput
84 {
85     IXmlReaderInput IXmlReaderInput_iface;
86     LONG ref;
87     /* reference passed on IXmlReaderInput creation, is kept when input is created */
88     IUnknown *input;
89     IMalloc *imalloc;
90     xml_encoding encoding;
91     BOOL hint;
92     WCHAR *baseuri;
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;
97     input_buffer *buffer;
98 } xmlreaderinput;
99
100 typedef struct
101 {
102     const WCHAR *str;
103     UINT len;
104 } strval;
105
106 struct attribute
107 {
108     struct list entry;
109     strval localname;
110     strval value;
111 };
112
113 typedef struct _xmlreader
114 {
115     IXmlReader IXmlReader_iface;
116     LONG ref;
117     xmlreaderinput *input;
118     IMalloc *imalloc;
119     XmlReadState state;
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 */
126     UINT attr_count;
127 } xmlreader;
128
129 struct input_buffer
130 {
131     encoded_buffer utf16;
132     encoded_buffer encoded;
133     UINT code_page;
134     xmlreaderinput *input;
135 };
136
137 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
138 {
139     return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
140 }
141
142 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
143 {
144     return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
145 }
146
147 static inline void *m_alloc(IMalloc *imalloc, size_t len)
148 {
149     if (imalloc)
150         return IMalloc_Alloc(imalloc, len);
151     else
152         return heap_alloc(len);
153 }
154
155 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
156 {
157     if (imalloc)
158         return IMalloc_Realloc(imalloc, mem, len);
159     else
160         return heap_realloc(mem, len);
161 }
162
163 static inline void m_free(IMalloc *imalloc, void *mem)
164 {
165     if (imalloc)
166         IMalloc_Free(imalloc, mem);
167     else
168         heap_free(mem);
169 }
170
171 /* reader memory allocation functions */
172 static inline void *reader_alloc(xmlreader *reader, size_t len)
173 {
174     return m_alloc(reader->imalloc, len);
175 }
176
177 static inline void reader_free(xmlreader *reader, void *mem)
178 {
179     m_free(reader->imalloc, mem);
180 }
181
182 /* reader input memory allocation functions */
183 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
184 {
185     return m_alloc(input->imalloc, len);
186 }
187
188 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
189 {
190     return m_realloc(input->imalloc, mem, len);
191 }
192
193 static inline void readerinput_free(xmlreaderinput *input, void *mem)
194 {
195     m_free(input->imalloc, mem);
196 }
197
198 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
199 {
200     LPWSTR ret = NULL;
201
202     if(str) {
203         DWORD size;
204
205         size = (strlenW(str)+1)*sizeof(WCHAR);
206         ret = readerinput_alloc(input, size);
207         if (ret) memcpy(ret, str, size);
208     }
209
210     return ret;
211 }
212
213 static void reader_clear_attrs(xmlreader *reader)
214 {
215     struct attribute *attr, *attr2;
216     LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
217     {
218         reader_free(reader, attr);
219     }
220     list_init(&reader->attrs);
221     reader->attr_count = 0;
222 }
223
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)
227 {
228     struct attribute *attr;
229
230     attr = reader_alloc(reader, sizeof(*attr));
231     if (!attr) return E_OUTOFMEMORY;
232
233     attr->localname = *localname;
234     attr->value = *value;
235     list_add_tail(&reader->attrs, &attr->entry);
236     reader->attr_count++;
237
238     return S_OK;
239 }
240
241 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
242 {
243     const int initial_len = 0x2000;
244     buffer->data = readerinput_alloc(input, initial_len);
245     if (!buffer->data) return E_OUTOFMEMORY;
246
247     memset(buffer->data, 0, 4);
248     buffer->cur = buffer->data;
249     buffer->allocated = initial_len;
250     buffer->written = 0;
251
252     return S_OK;
253 }
254
255 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
256 {
257     readerinput_free(input, buffer->data);
258 }
259
260 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
261 {
262     if (encoding == XmlEncoding_Unknown)
263     {
264         FIXME("unsupported encoding %d\n", encoding);
265         return E_NOTIMPL;
266     }
267
268     *cp = xml_encoding_map[encoding].cp;
269
270     return S_OK;
271 }
272
273 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
274 {
275     int min, max, n, c;
276
277     if (!name) return XmlEncoding_Unknown;
278
279     min = 0;
280     max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
281
282     while (min <= max)
283     {
284         n = (min+max)/2;
285
286         if (len != -1)
287             c = strncmpiW(xml_encoding_map[n].name, name, len);
288         else
289             c = strcmpiW(xml_encoding_map[n].name, name);
290         if (!c)
291             return xml_encoding_map[n].enc;
292
293         if (c > 0)
294             max = n-1;
295         else
296             min = n+1;
297     }
298
299     return XmlEncoding_Unknown;
300 }
301
302 static HRESULT alloc_input_buffer(xmlreaderinput *input)
303 {
304     input_buffer *buffer;
305     HRESULT hr;
306
307     input->buffer = NULL;
308
309     buffer = readerinput_alloc(input, sizeof(*buffer));
310     if (!buffer) return E_OUTOFMEMORY;
311
312     buffer->input = input;
313     buffer->code_page = ~0; /* code page is unknown at this point */
314     hr = init_encoded_buffer(input, &buffer->utf16);
315     if (hr != S_OK) {
316         readerinput_free(input, buffer);
317         return hr;
318     }
319
320     hr = init_encoded_buffer(input, &buffer->encoded);
321     if (hr != S_OK) {
322         free_encoded_buffer(input, &buffer->utf16);
323         readerinput_free(input, buffer);
324         return hr;
325     }
326
327     input->buffer = buffer;
328     return S_OK;
329 }
330
331 static void free_input_buffer(input_buffer *buffer)
332 {
333     free_encoded_buffer(buffer->input, &buffer->encoded);
334     free_encoded_buffer(buffer->input, &buffer->utf16);
335     readerinput_free(buffer->input, buffer);
336 }
337
338 static void readerinput_release_stream(xmlreaderinput *readerinput)
339 {
340     if (readerinput->stream) {
341         ISequentialStream_Release(readerinput->stream);
342         readerinput->stream = NULL;
343     }
344 }
345
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)
349 {
350     HRESULT hr;
351
352     readerinput_release_stream(readerinput);
353     hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
354     if (hr != S_OK)
355         hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
356
357     return hr;
358 }
359
360 /* reads a chunk to raw buffer */
361 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
362 {
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;
366     ULONG read;
367     HRESULT hr;
368
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)
374     {
375         buffer->allocated *= 2;
376         buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
377         len = buffer->allocated - buffer->written;
378     }
379
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;
384
385     return hr;
386 }
387
388 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
389 static void readerinput_grow(xmlreaderinput *readerinput, int length)
390 {
391     encoded_buffer *buffer = &readerinput->buffer->utf16;
392
393     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
394     if (buffer->allocated < buffer->written + length + 4)
395     {
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;
399     }
400 }
401
402 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
403 {
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};
409
410     *enc = XmlEncoding_Unknown;
411
412     if (buffer->written <= 3) return MX_E_INPUTEND;
413
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)))
422     {
423         buffer->cur += sizeof(utf8bom);
424         *enc = XmlEncoding_UTF8;
425     }
426     else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
427     {
428         buffer->cur += sizeof(utf16lebom);
429         *enc = XmlEncoding_UTF16;
430     }
431
432     return S_OK;
433 }
434
435 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
436 {
437     encoded_buffer *buffer = &readerinput->buffer->encoded;
438     int len = buffer->written;
439
440     /* complete single byte char */
441     if (!(buffer->data[len-1] & 0x80)) return len;
442
443     /* find start byte of multibyte char */
444     while (--len && !(buffer->data[len] & 0xc0))
445         ;
446
447     return len;
448 }
449
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
452    only. */
453 static int readerinput_get_convlen(xmlreaderinput *readerinput)
454 {
455     encoded_buffer *buffer = &readerinput->buffer->encoded;
456     int len;
457
458     if (readerinput->buffer->code_page == CP_UTF8)
459         len = readerinput_get_utf8_convlen(readerinput);
460     else
461         len = buffer->written;
462
463     TRACE("%d\n", len - (int)(buffer->cur - buffer->data));
464     return len - (buffer->cur - buffer->data);
465 }
466
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)
471 {
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;
479 }
480
481 /* note that raw buffer content is kept */
482 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
483 {
484     encoded_buffer *src = &readerinput->buffer->encoded;
485     encoded_buffer *dest = &readerinput->buffer->utf16;
486     int len, dest_len;
487     HRESULT hr;
488     WCHAR *ptr;
489     UINT cp;
490
491     hr = get_code_page(enc, &cp);
492     if (FAILED(hr)) return;
493
494     readerinput->buffer->code_page = cp;
495     len = readerinput_get_convlen(readerinput);
496
497     TRACE("switching to cp %d\n", cp);
498
499     /* just copy in this case */
500     if (enc == XmlEncoding_UTF16)
501     {
502         readerinput_grow(readerinput, len);
503         memcpy(dest->data, src->cur, len);
504         dest->written += len*sizeof(WCHAR);
505         return;
506     }
507
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);
512     ptr[dest_len] = 0;
513     dest->written += dest_len*sizeof(WCHAR);
514 }
515
516 /* shrinks parsed data a buffer begins with */
517 static void reader_shrink(xmlreader *reader)
518 {
519     encoded_buffer *buffer = &reader->input->buffer->utf16;
520
521     /* avoid to move too often using threshold shrink length */
522     if (buffer->cur - buffer->data > buffer->written / 2)
523     {
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;
528     }
529 }
530
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)
534 {
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;
539     int len, dest_len;
540     WCHAR *ptr;
541
542     /* get some raw data from stream first */
543     readerinput_growraw(readerinput);
544     len = readerinput_get_convlen(readerinput);
545
546     /* just copy for UTF-16 case */
547     if (cp == ~0)
548     {
549         readerinput_grow(readerinput, len);
550         memcpy(dest->data, src->cur, len);
551         dest->written += len*sizeof(WCHAR);
552         return;
553     }
554
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);
559     ptr[dest_len] = 0;
560     dest->written += dest_len*sizeof(WCHAR);
561     /* get rid of processed data */
562     readerinput_shrinkraw(readerinput, len);
563 }
564
565 static inline const WCHAR *reader_get_cur(xmlreader *reader)
566 {
567     WCHAR *ptr = (WCHAR*)reader->input->buffer->utf16.cur;
568     if (!*ptr) reader_more(reader);
569     return ptr;
570 }
571
572 static int reader_cmp(xmlreader *reader, const WCHAR *str)
573 {
574     const WCHAR *ptr = reader_get_cur(reader);
575     return strncmpW(str, ptr, strlenW(str));
576 }
577
578 /* moves cursor n WCHARs forward */
579 static void reader_skipn(xmlreader *reader, int n)
580 {
581     encoded_buffer *buffer = &reader->input->buffer->utf16;
582     const WCHAR *ptr = reader_get_cur(reader);
583
584     while (*ptr++ && n--)
585     {
586         buffer->cur += sizeof(WCHAR);
587         reader->pos++;
588     }
589 }
590
591 static inline int is_wchar_space(WCHAR ch)
592 {
593     return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
594 }
595
596 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
597 static int reader_skipspaces(xmlreader *reader)
598 {
599     encoded_buffer *buffer = &reader->input->buffer->utf16;
600     const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
601
602     while (is_wchar_space(*ptr))
603     {
604         buffer->cur += sizeof(WCHAR);
605         if (*ptr == '\r')
606             reader->pos = 0;
607         else if (*ptr == '\n')
608         {
609             reader->line++;
610             reader->pos = 0;
611         }
612         else
613             reader->pos++;
614         ptr++;
615     }
616
617     return ptr - start;
618 }
619
620 /* [26] VersionNum ::= '1.' [0-9]+ */
621 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
622 {
623     const WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
624     static const WCHAR onedotW[] = {'1','.',0};
625
626     if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
627     /* skip "1." */
628     reader_skipn(reader, 2);
629
630     ptr2 = ptr = reader_get_cur(reader);
631     while (*ptr >= '0' && *ptr <= '9')
632         ptr++;
633
634     if (ptr2 == ptr) return WC_E_DIGIT;
635     TRACE("version=%s\n", debugstr_wn(start, ptr-start));
636     val->str = start;
637     val->len = ptr-start;
638     reader_skipn(reader, ptr-ptr2);
639     return S_OK;
640 }
641
642 /* [25] Eq ::= S? '=' S? */
643 static HRESULT reader_parse_eq(xmlreader *reader)
644 {
645     static const WCHAR eqW[] = {'=',0};
646     reader_skipspaces(reader);
647     if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
648     /* skip '=' */
649     reader_skipn(reader, 1);
650     reader_skipspaces(reader);
651     return S_OK;
652 }
653
654 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
655 static HRESULT reader_parse_versioninfo(xmlreader *reader)
656 {
657     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
658     strval val, name;
659     HRESULT hr;
660
661     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
662
663     if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
664     name.str = reader_get_cur(reader);
665     name.len = 7;
666     /* skip 'version' */
667     reader_skipn(reader, 7);
668
669     hr = reader_parse_eq(reader);
670     if (FAILED(hr)) return hr;
671
672     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
673         return WC_E_QUOTE;
674     /* skip "'"|'"' */
675     reader_skipn(reader, 1);
676
677     hr = reader_parse_versionnum(reader, &val);
678     if (FAILED(hr)) return hr;
679
680     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
681         return WC_E_QUOTE;
682
683     /* skip "'"|'"' */
684     reader_skipn(reader, 1);
685
686     return reader_add_attr(reader, &name, &val);
687 }
688
689 /* ([A-Za-z0-9._] | '-') */
690 static inline int is_wchar_encname(WCHAR ch)
691 {
692     return ((ch >= 'A' && ch <= 'Z') ||
693             (ch >= 'a' && ch <= 'z') ||
694             (ch >= '0' && ch <= '9') ||
695             (ch == '.') || (ch == '_') ||
696             (ch == '-'));
697 }
698
699 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
700 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
701 {
702     const WCHAR *start = reader_get_cur(reader), *ptr;
703     xml_encoding enc;
704     int len;
705
706     if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
707         return WC_E_ENCNAME;
708
709     ptr = start;
710     while (is_wchar_encname(*++ptr))
711         ;
712
713     len = ptr - start;
714     enc = parse_encoding_name(start, len);
715     TRACE("encoding name %s\n", debugstr_wn(start, len));
716     val->str = start;
717     val->len = len;
718
719     if (enc == XmlEncoding_Unknown)
720         return WC_E_ENCNAME;
721
722     /* skip encoding name */
723     reader_skipn(reader, len);
724     return S_OK;
725 }
726
727 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
728 static HRESULT reader_parse_encdecl(xmlreader *reader)
729 {
730     static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
731     strval name, val;
732     HRESULT hr;
733
734     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
735
736     if (reader_cmp(reader, encodingW)) return S_FALSE;
737     name.str = reader_get_cur(reader);
738     name.len = 8;
739     /* skip 'encoding' */
740     reader_skipn(reader, 8);
741
742     hr = reader_parse_eq(reader);
743     if (FAILED(hr)) return hr;
744
745     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
746         return WC_E_QUOTE;
747     /* skip "'"|'"' */
748     reader_skipn(reader, 1);
749
750     hr = reader_parse_encname(reader, &val);
751     if (FAILED(hr)) return hr;
752
753     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
754         return WC_E_QUOTE;
755
756     /* skip "'"|'"' */
757     reader_skipn(reader, 1);
758
759     return reader_add_attr(reader, &name, &val);
760 }
761
762 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
763 static HRESULT reader_parse_sddecl(xmlreader *reader)
764 {
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;
769     strval name, val;
770     HRESULT hr;
771
772     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
773
774     if (reader_cmp(reader, standaloneW)) return S_FALSE;
775     name.str = reader_get_cur(reader);
776     name.len = 10;
777     /* skip 'standalone' */
778     reader_skipn(reader, 10);
779
780     hr = reader_parse_eq(reader);
781     if (FAILED(hr)) return hr;
782
783     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
784         return WC_E_QUOTE;
785     /* skip "'"|'"' */
786     reader_skipn(reader, 1);
787
788     if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
789         return WC_E_XMLDECL;
790
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));
796     val.str = start;
797     val.len = ptr-start;
798
799     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
800         return WC_E_QUOTE;
801     /* skip "'"|'"' */
802     reader_skipn(reader, 1);
803
804     return reader_add_attr(reader, &name, &val);
805 }
806
807 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
808 static HRESULT reader_parse_xmldecl(xmlreader *reader)
809 {
810     static const WCHAR xmldeclW[] = {'<','?','x','m','l',0};
811     static const WCHAR declcloseW[] = {'?','>',0};
812     HRESULT hr;
813
814     /* check if we have "<?xml" */
815     if (reader_cmp(reader, xmldeclW)) return S_FALSE;
816
817     reader_skipn(reader, 5);
818     hr = reader_parse_versioninfo(reader);
819     if (FAILED(hr))
820         return hr;
821
822     hr = reader_parse_encdecl(reader);
823     if (FAILED(hr))
824         return hr;
825
826     hr = reader_parse_sddecl(reader);
827     if (FAILED(hr))
828         return hr;
829
830     reader_skipspaces(reader);
831     if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
832     reader_skipn(reader, 2);
833
834     reader->nodetype = XmlNodeType_XmlDeclaration;
835
836     return S_OK;
837 }
838
839 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
840 static HRESULT reader_parse_comment(xmlreader *reader)
841 {
842     const WCHAR *start, *ptr;
843
844     /* skip '<!--' */
845     reader_skipn(reader, 4);
846     reader_shrink(reader);
847     ptr = start = reader_get_cur(reader);
848
849     while (*ptr)
850     {
851         if (ptr[0] == '-')
852         {
853             if (ptr[1] == '-')
854             {
855                 if (ptr[2] == '>')
856                 {
857                     TRACE("%s\n", debugstr_wn(start, ptr-start));
858                     /* skip '-->' */
859                     reader_skipn(reader, 3);
860                     reader->nodetype = XmlNodeType_Comment;
861                     return S_OK;
862                 }
863                 else
864                     return WC_E_COMMENT;
865             }
866             else
867             {
868                 ptr++;
869                 reader_more(reader);
870             }
871         }
872         else
873         {
874             reader_skipn(reader, 1);
875             ptr = reader_get_cur(reader);
876         }
877     }
878
879     return MX_E_INPUTEND;
880 }
881
882 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
883 static HRESULT reader_parse_pi(xmlreader *reader)
884 {
885     FIXME("PI not supported\n");
886     return E_NOTIMPL;
887 }
888
889 /* [27] Misc ::= Comment | PI | S */
890 static HRESULT reader_parse_misc(xmlreader *reader)
891 {
892     HRESULT hr = S_FALSE;
893
894     while (1)
895     {
896         static const WCHAR commentW[] = {'<','!','-','-',0};
897         static const WCHAR piW[] = {'<','?',0};
898         const WCHAR *cur = reader_get_cur(reader);
899
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);
906         else
907             break;
908
909         if (FAILED(hr)) return hr;
910     }
911
912     return hr;
913 }
914
915 static HRESULT reader_parse_nextnode(xmlreader *reader)
916 {
917     HRESULT hr;
918
919     while (1)
920     {
921         switch (reader->instate)
922         {
923         /* if it's a first call for a new input we need to detect stream encoding */
924         case XmlReadInState_Initial:
925             {
926                 xml_encoding enc;
927
928                 hr = readerinput_growraw(reader->input);
929                 if (FAILED(hr)) return hr;
930
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;
935
936                 /* always switch first time cause we have to put something in */
937                 readerinput_switchencoding(reader->input, enc);
938
939                 /* parse xml declaration */
940                 hr = reader_parse_xmldecl(reader);
941                 if (FAILED(hr)) return hr;
942
943                 reader->instate = XmlReadInState_Misc_DTD;
944                 if (hr == S_OK) return hr;
945             }
946             break;
947         case XmlReadInState_Misc_DTD:
948             hr = reader_parse_misc(reader);
949             if (FAILED(hr)) return hr;
950             if (hr == S_FALSE)
951             {
952                 reader->instate = XmlReadInState_DTD;
953                 return S_OK;
954             }
955             break;
956         case XmlReadInState_DTD:
957             FIXME("DTD parsing not supported\n");
958             return E_NOTIMPL;
959         default:
960             FIXME("internal state %d not handled\n", reader->instate);
961             return E_NOTIMPL;
962         }
963     }
964
965     return E_NOTIMPL;
966 }
967
968 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
969 {
970     xmlreader *This = impl_from_IXmlReader(iface);
971
972     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
973
974     if (IsEqualGUID(riid, &IID_IUnknown) ||
975         IsEqualGUID(riid, &IID_IXmlReader))
976     {
977         *ppvObject = iface;
978     }
979     else
980     {
981         FIXME("interface %s not implemented\n", debugstr_guid(riid));
982         return E_NOINTERFACE;
983     }
984
985     IXmlReader_AddRef(iface);
986
987     return S_OK;
988 }
989
990 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
991 {
992     xmlreader *This = impl_from_IXmlReader(iface);
993     ULONG ref = InterlockedIncrement(&This->ref);
994     TRACE("(%p)->(%d)\n", This, ref);
995     return ref;
996 }
997
998 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
999 {
1000     xmlreader *This = impl_from_IXmlReader(iface);
1001     LONG ref = InterlockedDecrement(&This->ref);
1002
1003     TRACE("(%p)->(%d)\n", This, ref);
1004
1005     if (ref == 0)
1006     {
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);
1012     }
1013
1014     return ref;
1015 }
1016
1017 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
1018 {
1019     xmlreader *This = impl_from_IXmlReader(iface);
1020     HRESULT hr;
1021
1022     TRACE("(%p %p)\n", This, input);
1023
1024     if (This->input)
1025     {
1026         readerinput_release_stream(This->input);
1027         IUnknown_Release(&This->input->IXmlReaderInput_iface);
1028         This->input = NULL;
1029     }
1030
1031     This->line = This->pos = 0;
1032
1033     /* just reset current input */
1034     if (!input)
1035     {
1036         This->state = XmlReadState_Initial;
1037         return S_OK;
1038     }
1039
1040     /* now try IXmlReaderInput, ISequentialStream, IStream */
1041     hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
1042     if (hr != S_OK)
1043     {
1044         IXmlReaderInput *readerinput;
1045
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);
1051     }
1052
1053     /* set stream for supplied IXmlReaderInput */
1054     hr = readerinput_query_for_stream(This->input);
1055     if (hr == S_OK)
1056     {
1057         This->state = XmlReadState_Initial;
1058         This->instate = XmlReadInState_Initial;
1059     }
1060
1061     return hr;
1062 }
1063
1064 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1065 {
1066     xmlreader *This = impl_from_IXmlReader(iface);
1067
1068     TRACE("(%p %u %p)\n", This, property, value);
1069
1070     if (!value) return E_INVALIDARG;
1071
1072     switch (property)
1073     {
1074         case XmlReaderProperty_DtdProcessing:
1075             *value = This->dtdmode;
1076             break;
1077         case XmlReaderProperty_ReadState:
1078             *value = This->state;
1079             break;
1080         default:
1081             FIXME("Unimplemented property (%u)\n", property);
1082             return E_NOTIMPL;
1083     }
1084
1085     return S_OK;
1086 }
1087
1088 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1089 {
1090     xmlreader *This = impl_from_IXmlReader(iface);
1091
1092     TRACE("(%p %u %lu)\n", iface, property, value);
1093
1094     switch (property)
1095     {
1096         case XmlReaderProperty_DtdProcessing:
1097             if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1098             This->dtdmode = value;
1099             break;
1100         default:
1101             FIXME("Unimplemented property (%u)\n", property);
1102             return E_NOTIMPL;
1103     }
1104
1105     return S_OK;
1106 }
1107
1108 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1109 {
1110     xmlreader *This = impl_from_IXmlReader(iface);
1111     XmlNodeType oldtype = This->nodetype;
1112     HRESULT hr;
1113
1114     TRACE("(%p)->(%p)\n", This, nodetype);
1115
1116     if (This->state == XmlReadState_Closed) return S_FALSE;
1117
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;
1122
1123     return hr;
1124 }
1125
1126 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1127 {
1128     xmlreader *This = impl_from_IXmlReader(iface);
1129     TRACE("(%p)->(%p)\n", This, node_type);
1130
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;
1136 }
1137
1138 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1139 {
1140     xmlreader *This = impl_from_IXmlReader(iface);
1141
1142     TRACE("(%p)\n", This);
1143
1144     if (!This->attr_count) return S_FALSE;
1145     This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1146     return S_OK;
1147 }
1148
1149 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1150 {
1151     xmlreader *This = impl_from_IXmlReader(iface);
1152     const struct list *next;
1153
1154     TRACE("(%p)\n", This);
1155
1156     if (!This->attr_count) return S_FALSE;
1157
1158     if (!This->attr)
1159         return IXmlReader_MoveToFirstAttribute(iface);
1160
1161     next = list_next(&This->attrs, &This->attr->entry);
1162     if (next)
1163         This->attr = LIST_ENTRY(next, struct attribute, entry);
1164
1165     return next ? S_OK : S_FALSE;
1166 }
1167
1168 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1169                                                       LPCWSTR local_name,
1170                                                       LPCWSTR namespaceUri)
1171 {
1172     FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1173     return E_NOTIMPL;
1174 }
1175
1176 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1177 {
1178     xmlreader *This = impl_from_IXmlReader(iface);
1179
1180     TRACE("(%p)\n", This);
1181
1182     if (!This->attr_count) return S_FALSE;
1183     This->attr = NULL;
1184     return S_OK;
1185 }
1186
1187 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *qualifiedName,
1188                                                  UINT *qualifiedName_length)
1189 {
1190     FIXME("(%p %p %p): stub\n", iface, qualifiedName, qualifiedName_length);
1191     return E_NOTIMPL;
1192 }
1193
1194 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1195                                                 LPCWSTR *namespaceUri,
1196                                                 UINT *namespaceUri_length)
1197 {
1198     FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1199     return E_NOTIMPL;
1200 }
1201
1202 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface,
1203                                              LPCWSTR *local_name,
1204                                              UINT *local_name_length)
1205 {
1206     FIXME("(%p %p %p): stub\n", iface, local_name, local_name_length);
1207     return E_NOTIMPL;
1208 }
1209
1210 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1211                                           LPCWSTR *prefix,
1212                                           UINT *prefix_length)
1213 {
1214     FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1215     return E_NOTIMPL;
1216 }
1217
1218 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface,
1219                                          LPCWSTR *value,
1220                                          UINT *value_length)
1221 {
1222     FIXME("(%p %p %p): stub\n", iface, value, value_length);
1223     return E_NOTIMPL;
1224 }
1225
1226 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1227                                                WCHAR *buffer,
1228                                                UINT   chunk_size,
1229                                                UINT  *read)
1230 {
1231     FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1232     return E_NOTIMPL;
1233 }
1234
1235 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1236                                            LPCWSTR *baseUri,
1237                                            UINT *baseUri_length)
1238 {
1239     FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1240     return E_NOTIMPL;
1241 }
1242
1243 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1244 {
1245     FIXME("(%p): stub\n", iface);
1246     return E_NOTIMPL;
1247 }
1248
1249 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1250 {
1251     FIXME("(%p): stub\n", iface);
1252     return E_NOTIMPL;
1253 }
1254
1255 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1256 {
1257     xmlreader *This = impl_from_IXmlReader(iface);
1258
1259     TRACE("(%p %p)\n", This, lineNumber);
1260
1261     if (!lineNumber) return E_INVALIDARG;
1262
1263     *lineNumber = This->line;
1264
1265     return S_OK;
1266 }
1267
1268 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1269 {
1270     xmlreader *This = impl_from_IXmlReader(iface);
1271
1272     TRACE("(%p %p)\n", This, linePosition);
1273
1274     if (!linePosition) return E_INVALIDARG;
1275
1276     *linePosition = This->pos;
1277
1278     return S_OK;
1279 }
1280
1281 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1282 {
1283     xmlreader *This = impl_from_IXmlReader(iface);
1284
1285     TRACE("(%p)->(%p)\n", This, count);
1286
1287     if (!count) return E_INVALIDARG;
1288
1289     *count = This->attr_count;
1290     return S_OK;
1291 }
1292
1293 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1294 {
1295     FIXME("(%p %p): stub\n", iface, depth);
1296     return E_NOTIMPL;
1297 }
1298
1299 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1300 {
1301     FIXME("(%p): stub\n", iface);
1302     return E_NOTIMPL;
1303 }
1304
1305 static const struct IXmlReaderVtbl xmlreader_vtbl =
1306 {
1307     xmlreader_QueryInterface,
1308     xmlreader_AddRef,
1309     xmlreader_Release,
1310     xmlreader_SetInput,
1311     xmlreader_GetProperty,
1312     xmlreader_SetProperty,
1313     xmlreader_Read,
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,
1323     xmlreader_GetValue,
1324     xmlreader_ReadValueChunk,
1325     xmlreader_GetBaseUri,
1326     xmlreader_IsDefault,
1327     xmlreader_IsEmptyElement,
1328     xmlreader_GetLineNumber,
1329     xmlreader_GetLinePosition,
1330     xmlreader_GetAttributeCount,
1331     xmlreader_GetDepth,
1332     xmlreader_IsEOF
1333 };
1334
1335 /** IXmlReaderInput **/
1336 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1337 {
1338     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1339
1340     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1341
1342     if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1343         IsEqualGUID(riid, &IID_IUnknown))
1344     {
1345         *ppvObject = iface;
1346     }
1347     else
1348     {
1349         WARN("interface %s not implemented\n", debugstr_guid(riid));
1350         return E_NOINTERFACE;
1351     }
1352
1353     IUnknown_AddRef(iface);
1354
1355     return S_OK;
1356 }
1357
1358 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1359 {
1360     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1361     ULONG ref = InterlockedIncrement(&This->ref);
1362     TRACE("(%p)->(%d)\n", This, ref);
1363     return ref;
1364 }
1365
1366 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1367 {
1368     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1369     LONG ref = InterlockedDecrement(&This->ref);
1370
1371     TRACE("(%p)->(%d)\n", This, ref);
1372
1373     if (ref == 0)
1374     {
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);
1382     }
1383
1384     return ref;
1385 }
1386
1387 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1388 {
1389     xmlreaderinput_QueryInterface,
1390     xmlreaderinput_AddRef,
1391     xmlreaderinput_Release
1392 };
1393
1394 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1395 {
1396     xmlreader *reader;
1397
1398     TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1399
1400     if (!IsEqualGUID(riid, &IID_IXmlReader))
1401     {
1402         ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1403         return E_FAIL;
1404     }
1405
1406     if (imalloc)
1407         reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1408     else
1409         reader = heap_alloc(sizeof(*reader));
1410     if(!reader) return E_OUTOFMEMORY;
1411
1412     reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1413     reader->ref = 1;
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;
1425
1426     *obj = &reader->IXmlReader_iface;
1427
1428     TRACE("returning iface %p\n", *obj);
1429
1430     return S_OK;
1431 }
1432
1433 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1434                                                     IMalloc *imalloc,
1435                                                     LPCWSTR encoding,
1436                                                     BOOL hint,
1437                                                     LPCWSTR base_uri,
1438                                                     IXmlReaderInput **ppInput)
1439 {
1440     xmlreaderinput *readerinput;
1441     HRESULT hr;
1442
1443     TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1444                                        hint, wine_dbgstr_w(base_uri), ppInput);
1445
1446     if (!stream || !ppInput) return E_INVALIDARG;
1447
1448     if (imalloc)
1449         readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1450     else
1451         readerinput = heap_alloc(sizeof(*readerinput));
1452     if(!readerinput) return E_OUTOFMEMORY;
1453
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);
1462
1463     hr = alloc_input_buffer(readerinput);
1464     if (hr != S_OK)
1465     {
1466         readerinput_free(readerinput, readerinput);
1467         return hr;
1468     }
1469     IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1470
1471     *ppInput = &readerinput->IXmlReaderInput_iface;
1472
1473     TRACE("returning iface %p\n", *ppInput);
1474
1475     return S_OK;
1476 }