netstat: Initial implementation.
[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     ULONG len = buffer->allocated - buffer->written, read;
365     HRESULT hr;
366
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)
372     {
373         buffer->allocated *= 2;
374         buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
375         len = buffer->allocated - buffer->written;
376     }
377
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;
382
383     return hr;
384 }
385
386 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
387 static void readerinput_grow(xmlreaderinput *readerinput, int length)
388 {
389     encoded_buffer *buffer = &readerinput->buffer->utf16;
390
391     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
392     if (buffer->allocated < buffer->written + length + 4)
393     {
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;
397     }
398 }
399
400 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
401 {
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};
407
408     *enc = XmlEncoding_Unknown;
409
410     if (buffer->written <= 3) return MX_E_INPUTEND;
411
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)))
420     {
421         buffer->cur += sizeof(utf8bom);
422         *enc = XmlEncoding_UTF8;
423     }
424     else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
425     {
426         buffer->cur += sizeof(utf16lebom);
427         *enc = XmlEncoding_UTF16;
428     }
429
430     return S_OK;
431 }
432
433 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
434 {
435     encoded_buffer *buffer = &readerinput->buffer->encoded;
436     int len = buffer->written;
437
438     /* complete single byte char */
439     if (!(buffer->data[len-1] & 0x80)) return len;
440
441     /* find start byte of multibyte char */
442     while (--len && !(buffer->data[len] & 0xc0))
443         ;
444
445     return len;
446 }
447
448 /* returns byte length of complete char sequence for specified code page, */
449 static int readerinput_get_convlen(xmlreaderinput *readerinput, UINT cp)
450 {
451     encoded_buffer *buffer = &readerinput->buffer->encoded;
452     int len;
453
454     if (cp == CP_UTF8)
455         len = readerinput_get_utf8_convlen(readerinput);
456     else
457         len = buffer->written;
458
459     return len - (buffer->cur - buffer->data);
460 }
461
462 /* note that raw buffer content is kept */
463 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
464 {
465     encoded_buffer *src = &readerinput->buffer->encoded;
466     encoded_buffer *dest = &readerinput->buffer->utf16;
467     int len, dest_len;
468     HRESULT hr;
469     WCHAR *ptr;
470     UINT cp;
471
472     hr = get_code_page(enc, &cp);
473     if (FAILED(hr)) return;
474
475     len = readerinput_get_convlen(readerinput, cp);
476
477     TRACE("switching to cp %d\n", cp);
478
479     /* just copy in this case */
480     if (enc == XmlEncoding_UTF16)
481     {
482         readerinput_grow(readerinput, len);
483         memcpy(dest->data, src->cur, len);
484         dest->written += len*sizeof(WCHAR);
485         readerinput->buffer->code_page = cp;
486         return;
487     }
488
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);
493     ptr[dest_len] = 0;
494     readerinput->buffer->code_page = cp;
495     dest->written += dest_len*sizeof(WCHAR);
496 }
497
498 /* shrinks parsed data a buffer begins with */
499 static void reader_shrink(xmlreader *reader)
500 {
501     encoded_buffer *buffer = &reader->input->buffer->utf16;
502
503     /* avoid to move too often using threshold shrink length */
504     if (buffer->cur - buffer->data > buffer->written / 2)
505     {
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;
510     }
511 }
512
513 static inline const WCHAR *reader_get_cur(xmlreader *reader)
514 {
515     return (WCHAR*)reader->input->buffer->utf16.cur;
516 }
517
518 static int reader_cmp(xmlreader *reader, const WCHAR *str)
519 {
520     const WCHAR *ptr = reader_get_cur(reader);
521     return strncmpW(str, ptr, strlenW(str));
522 }
523
524 /* moves cursor n WCHARs forward */
525 static void reader_skipn(xmlreader *reader, int n)
526 {
527     encoded_buffer *buffer = &reader->input->buffer->utf16;
528     const WCHAR *ptr = reader_get_cur(reader);
529
530     while (*ptr++ && n--)
531     {
532         buffer->cur += sizeof(WCHAR);
533         reader->pos++;
534     }
535 }
536
537 static inline int is_wchar_space(WCHAR ch)
538 {
539     return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
540 }
541
542 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
543 static int reader_skipspaces(xmlreader *reader)
544 {
545     encoded_buffer *buffer = &reader->input->buffer->utf16;
546     const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
547
548     while (is_wchar_space(*ptr))
549     {
550         buffer->cur += sizeof(WCHAR);
551         if (*ptr == '\r')
552             reader->pos = 0;
553         else if (*ptr == '\n')
554         {
555             reader->line++;
556             reader->pos = 0;
557         }
558         else
559             reader->pos++;
560         ptr++;
561     }
562
563     return ptr - start;
564 }
565
566 /* [26] VersionNum ::= '1.' [0-9]+ */
567 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
568 {
569     const WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
570     static const WCHAR onedotW[] = {'1','.',0};
571
572     if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
573     /* skip "1." */
574     reader_skipn(reader, 2);
575
576     ptr2 = ptr = reader_get_cur(reader);
577     while (*ptr >= '0' && *ptr <= '9')
578         ptr++;
579
580     if (ptr2 == ptr) return WC_E_DIGIT;
581     TRACE("version=%s\n", debugstr_wn(start, ptr-start));
582     val->str = start;
583     val->len = ptr-start;
584     reader_skipn(reader, ptr-ptr2);
585     return S_OK;
586 }
587
588 /* [25] Eq ::= S? '=' S? */
589 static HRESULT reader_parse_eq(xmlreader *reader)
590 {
591     static const WCHAR eqW[] = {'=',0};
592     reader_skipspaces(reader);
593     if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
594     /* skip '=' */
595     reader_skipn(reader, 1);
596     reader_skipspaces(reader);
597     return S_OK;
598 }
599
600 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
601 static HRESULT reader_parse_versioninfo(xmlreader *reader)
602 {
603     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
604     strval val, name;
605     HRESULT hr;
606
607     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
608
609     if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
610     name.str = reader_get_cur(reader);
611     name.len = 7;
612     /* skip 'version' */
613     reader_skipn(reader, 7);
614
615     hr = reader_parse_eq(reader);
616     if (FAILED(hr)) return hr;
617
618     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
619         return WC_E_QUOTE;
620     /* skip "'"|'"' */
621     reader_skipn(reader, 1);
622
623     hr = reader_parse_versionnum(reader, &val);
624     if (FAILED(hr)) return hr;
625
626     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
627         return WC_E_QUOTE;
628
629     /* skip "'"|'"' */
630     reader_skipn(reader, 1);
631
632     return reader_add_attr(reader, &name, &val);
633 }
634
635 /* ([A-Za-z0-9._] | '-') */
636 static inline int is_wchar_encname(WCHAR ch)
637 {
638     return ((ch >= 'A' && ch <= 'Z') ||
639             (ch >= 'a' && ch <= 'z') ||
640             (ch >= '0' && ch <= '9') ||
641             (ch == '.') || (ch == '_') ||
642             (ch == '-'));
643 }
644
645 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
646 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
647 {
648     const WCHAR *start = reader_get_cur(reader), *ptr;
649     xml_encoding enc;
650     int len;
651
652     if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
653         return WC_E_ENCNAME;
654
655     ptr = start;
656     while (is_wchar_encname(*++ptr))
657         ;
658
659     len = ptr - start;
660     enc = parse_encoding_name(start, len);
661     TRACE("encoding name %s\n", debugstr_wn(start, len));
662     val->str = start;
663     val->len = len;
664
665     if (enc == XmlEncoding_Unknown)
666         return WC_E_ENCNAME;
667
668     /* skip encoding name */
669     reader_skipn(reader, len);
670     return S_OK;
671 }
672
673 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
674 static HRESULT reader_parse_encdecl(xmlreader *reader)
675 {
676     static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
677     strval name, val;
678     HRESULT hr;
679
680     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
681
682     if (reader_cmp(reader, encodingW)) return S_FALSE;
683     name.str = reader_get_cur(reader);
684     name.len = 8;
685     /* skip 'encoding' */
686     reader_skipn(reader, 8);
687
688     hr = reader_parse_eq(reader);
689     if (FAILED(hr)) return hr;
690
691     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
692         return WC_E_QUOTE;
693     /* skip "'"|'"' */
694     reader_skipn(reader, 1);
695
696     hr = reader_parse_encname(reader, &val);
697     if (FAILED(hr)) return hr;
698
699     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
700         return WC_E_QUOTE;
701
702     /* skip "'"|'"' */
703     reader_skipn(reader, 1);
704
705     return reader_add_attr(reader, &name, &val);
706 }
707
708 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
709 static HRESULT reader_parse_sddecl(xmlreader *reader)
710 {
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;
715     strval name, val;
716     HRESULT hr;
717
718     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
719
720     if (reader_cmp(reader, standaloneW)) return S_FALSE;
721     name.str = reader_get_cur(reader);
722     name.len = 10;
723     /* skip 'standalone' */
724     reader_skipn(reader, 10);
725
726     hr = reader_parse_eq(reader);
727     if (FAILED(hr)) return hr;
728
729     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
730         return WC_E_QUOTE;
731     /* skip "'"|'"' */
732     reader_skipn(reader, 1);
733
734     if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
735         return WC_E_XMLDECL;
736
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));
742     val.str = start;
743     val.len = ptr-start;
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     return reader_add_attr(reader, &name, &val);
751 }
752
753 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
754 static HRESULT reader_parse_xmldecl(xmlreader *reader)
755 {
756     static const WCHAR xmldeclW[] = {'<','?','x','m','l',0};
757     static const WCHAR declcloseW[] = {'?','>',0};
758     HRESULT hr;
759
760     /* check if we have "<?xml" */
761     if (reader_cmp(reader, xmldeclW)) return S_FALSE;
762
763     reader_skipn(reader, 5);
764     hr = reader_parse_versioninfo(reader);
765     if (FAILED(hr))
766         return hr;
767
768     hr = reader_parse_encdecl(reader);
769     if (FAILED(hr))
770         return hr;
771
772     hr = reader_parse_sddecl(reader);
773     if (FAILED(hr))
774         return hr;
775
776     reader_skipspaces(reader);
777     if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
778     reader_skipn(reader, 2);
779
780     reader->nodetype = XmlNodeType_XmlDeclaration;
781
782     return S_OK;
783 }
784
785 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
786 static HRESULT reader_parse_comment(xmlreader *reader)
787 {
788     const WCHAR *start, *ptr;
789
790     /* skip '<!--' */
791     reader_skipn(reader, 4);
792     reader_shrink(reader);
793     ptr = start = reader_get_cur(reader);
794
795     while (*ptr)
796     {
797         if (ptr[0] == '-' && ptr[1] == '-')
798         {
799             if (ptr[2] == '>')
800             {
801                 TRACE("%s\n", debugstr_wn(start, ptr-start));
802                 /* skip '-->' */
803                 reader_skipn(reader, 3);
804                 reader->nodetype = XmlNodeType_Comment;
805                 return S_OK;
806             }
807             else
808                 return WC_E_COMMENT;
809         }
810         else
811         {
812             reader_skipn(reader, 1);
813             ptr = reader_get_cur(reader);
814         }
815     }
816
817     return MX_E_INPUTEND;
818 }
819
820 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
821 static HRESULT reader_parse_pi(xmlreader *reader)
822 {
823     FIXME("PI not supported\n");
824     return E_NOTIMPL;
825 }
826
827 /* [27] Misc ::= Comment | PI | S */
828 static HRESULT reader_parse_misc(xmlreader *reader)
829 {
830     HRESULT hr = S_FALSE;
831
832     while (1)
833     {
834         static const WCHAR commentW[] = {'<','!','-','-',0};
835         static const WCHAR piW[] = {'<','?',0};
836         const WCHAR *cur = reader_get_cur(reader);
837
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);
844         else
845             break;
846
847         if (FAILED(hr)) return hr;
848     }
849
850     return hr;
851 }
852
853 static HRESULT reader_parse_nextnode(xmlreader *reader)
854 {
855     HRESULT hr;
856
857     while (1)
858     {
859         switch (reader->instate)
860         {
861         /* if it's a first call for a new input we need to detect stream encoding */
862         case XmlReadInState_Initial:
863             {
864                 xml_encoding enc;
865
866                 hr = readerinput_growraw(reader->input);
867                 if (FAILED(hr)) return hr;
868
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;
873
874                 /* always switch first time cause we have to put something in */
875                 readerinput_switchencoding(reader->input, enc);
876
877                 /* parse xml declaration */
878                 hr = reader_parse_xmldecl(reader);
879                 if (FAILED(hr)) return hr;
880
881                 reader->instate = XmlReadInState_Misc_DTD;
882                 if (hr == S_OK) return hr;
883             }
884             break;
885         case XmlReadInState_Misc_DTD:
886             hr = reader_parse_misc(reader);
887             if (FAILED(hr)) return hr;
888             if (hr == S_FALSE)
889             {
890                 reader->instate = XmlReadInState_DTD;
891                 return S_OK;
892             }
893             break;
894         case XmlReadInState_DTD:
895             FIXME("DTD parsing not supported\n");
896             return E_NOTIMPL;
897         default:
898             FIXME("internal state %d not handled\n", reader->instate);
899             return E_NOTIMPL;
900         }
901     }
902
903     return E_NOTIMPL;
904 }
905
906 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
907 {
908     xmlreader *This = impl_from_IXmlReader(iface);
909
910     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
911
912     if (IsEqualGUID(riid, &IID_IUnknown) ||
913         IsEqualGUID(riid, &IID_IXmlReader))
914     {
915         *ppvObject = iface;
916     }
917     else
918     {
919         FIXME("interface %s not implemented\n", debugstr_guid(riid));
920         return E_NOINTERFACE;
921     }
922
923     IXmlReader_AddRef(iface);
924
925     return S_OK;
926 }
927
928 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
929 {
930     xmlreader *This = impl_from_IXmlReader(iface);
931     ULONG ref = InterlockedIncrement(&This->ref);
932     TRACE("(%p)->(%d)\n", This, ref);
933     return ref;
934 }
935
936 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
937 {
938     xmlreader *This = impl_from_IXmlReader(iface);
939     LONG ref = InterlockedDecrement(&This->ref);
940
941     TRACE("(%p)->(%d)\n", This, ref);
942
943     if (ref == 0)
944     {
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);
950     }
951
952     return ref;
953 }
954
955 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
956 {
957     xmlreader *This = impl_from_IXmlReader(iface);
958     HRESULT hr;
959
960     TRACE("(%p %p)\n", This, input);
961
962     if (This->input)
963     {
964         readerinput_release_stream(This->input);
965         IUnknown_Release(&This->input->IXmlReaderInput_iface);
966         This->input = NULL;
967     }
968
969     This->line = This->pos = 0;
970
971     /* just reset current input */
972     if (!input)
973     {
974         This->state = XmlReadState_Initial;
975         return S_OK;
976     }
977
978     /* now try IXmlReaderInput, ISequentialStream, IStream */
979     hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
980     if (hr != S_OK)
981     {
982         IXmlReaderInput *readerinput;
983
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);
989     }
990
991     /* set stream for supplied IXmlReaderInput */
992     hr = readerinput_query_for_stream(This->input);
993     if (hr == S_OK)
994     {
995         This->state = XmlReadState_Initial;
996         This->instate = XmlReadInState_Initial;
997     }
998
999     return hr;
1000 }
1001
1002 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1003 {
1004     xmlreader *This = impl_from_IXmlReader(iface);
1005
1006     TRACE("(%p %u %p)\n", This, property, value);
1007
1008     if (!value) return E_INVALIDARG;
1009
1010     switch (property)
1011     {
1012         case XmlReaderProperty_DtdProcessing:
1013             *value = This->dtdmode;
1014             break;
1015         case XmlReaderProperty_ReadState:
1016             *value = This->state;
1017             break;
1018         default:
1019             FIXME("Unimplemented property (%u)\n", property);
1020             return E_NOTIMPL;
1021     }
1022
1023     return S_OK;
1024 }
1025
1026 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1027 {
1028     xmlreader *This = impl_from_IXmlReader(iface);
1029
1030     TRACE("(%p %u %lu)\n", iface, property, value);
1031
1032     switch (property)
1033     {
1034         case XmlReaderProperty_DtdProcessing:
1035             if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1036             This->dtdmode = value;
1037             break;
1038         default:
1039             FIXME("Unimplemented property (%u)\n", property);
1040             return E_NOTIMPL;
1041     }
1042
1043     return S_OK;
1044 }
1045
1046 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1047 {
1048     xmlreader *This = impl_from_IXmlReader(iface);
1049     XmlNodeType oldtype = This->nodetype;
1050     HRESULT hr;
1051
1052     TRACE("(%p)->(%p)\n", This, nodetype);
1053
1054     if (This->state == XmlReadState_Closed) return S_FALSE;
1055
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;
1060
1061     return hr;
1062 }
1063
1064 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1065 {
1066     xmlreader *This = impl_from_IXmlReader(iface);
1067     TRACE("(%p)->(%p)\n", This, node_type);
1068
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;
1074 }
1075
1076 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1077 {
1078     xmlreader *This = impl_from_IXmlReader(iface);
1079
1080     TRACE("(%p)\n", This);
1081
1082     if (!This->attr_count) return S_FALSE;
1083     This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1084     return S_OK;
1085 }
1086
1087 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1088 {
1089     xmlreader *This = impl_from_IXmlReader(iface);
1090     const struct list *next;
1091
1092     TRACE("(%p)\n", This);
1093
1094     if (!This->attr_count) return S_FALSE;
1095
1096     if (!This->attr)
1097         return IXmlReader_MoveToFirstAttribute(iface);
1098
1099     next = list_next(&This->attrs, &This->attr->entry);
1100     if (next)
1101         This->attr = LIST_ENTRY(next, struct attribute, entry);
1102
1103     return next ? S_OK : S_FALSE;
1104 }
1105
1106 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1107                                                       LPCWSTR local_name,
1108                                                       LPCWSTR namespaceUri)
1109 {
1110     FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1111     return E_NOTIMPL;
1112 }
1113
1114 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1115 {
1116     xmlreader *This = impl_from_IXmlReader(iface);
1117
1118     TRACE("(%p)\n", This);
1119
1120     if (!This->attr_count) return S_FALSE;
1121     This->attr = NULL;
1122     return S_OK;
1123 }
1124
1125 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *qualifiedName,
1126                                                  UINT *qualifiedName_length)
1127 {
1128     FIXME("(%p %p %p): stub\n", iface, qualifiedName, qualifiedName_length);
1129     return E_NOTIMPL;
1130 }
1131
1132 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1133                                                 LPCWSTR *namespaceUri,
1134                                                 UINT *namespaceUri_length)
1135 {
1136     FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1137     return E_NOTIMPL;
1138 }
1139
1140 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface,
1141                                              LPCWSTR *local_name,
1142                                              UINT *local_name_length)
1143 {
1144     FIXME("(%p %p %p): stub\n", iface, local_name, local_name_length);
1145     return E_NOTIMPL;
1146 }
1147
1148 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1149                                           LPCWSTR *prefix,
1150                                           UINT *prefix_length)
1151 {
1152     FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1153     return E_NOTIMPL;
1154 }
1155
1156 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface,
1157                                          LPCWSTR *value,
1158                                          UINT *value_length)
1159 {
1160     FIXME("(%p %p %p): stub\n", iface, value, value_length);
1161     return E_NOTIMPL;
1162 }
1163
1164 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1165                                                WCHAR *buffer,
1166                                                UINT   chunk_size,
1167                                                UINT  *read)
1168 {
1169     FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1170     return E_NOTIMPL;
1171 }
1172
1173 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1174                                            LPCWSTR *baseUri,
1175                                            UINT *baseUri_length)
1176 {
1177     FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1178     return E_NOTIMPL;
1179 }
1180
1181 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1182 {
1183     FIXME("(%p): stub\n", iface);
1184     return E_NOTIMPL;
1185 }
1186
1187 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1188 {
1189     FIXME("(%p): stub\n", iface);
1190     return E_NOTIMPL;
1191 }
1192
1193 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1194 {
1195     xmlreader *This = impl_from_IXmlReader(iface);
1196
1197     TRACE("(%p %p)\n", This, lineNumber);
1198
1199     if (!lineNumber) return E_INVALIDARG;
1200
1201     *lineNumber = This->line;
1202
1203     return S_OK;
1204 }
1205
1206 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1207 {
1208     xmlreader *This = impl_from_IXmlReader(iface);
1209
1210     TRACE("(%p %p)\n", This, linePosition);
1211
1212     if (!linePosition) return E_INVALIDARG;
1213
1214     *linePosition = This->pos;
1215
1216     return S_OK;
1217 }
1218
1219 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1220 {
1221     xmlreader *This = impl_from_IXmlReader(iface);
1222
1223     TRACE("(%p)->(%p)\n", This, count);
1224
1225     if (!count) return E_INVALIDARG;
1226
1227     *count = This->attr_count;
1228     return S_OK;
1229 }
1230
1231 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1232 {
1233     FIXME("(%p %p): stub\n", iface, depth);
1234     return E_NOTIMPL;
1235 }
1236
1237 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1238 {
1239     FIXME("(%p): stub\n", iface);
1240     return E_NOTIMPL;
1241 }
1242
1243 static const struct IXmlReaderVtbl xmlreader_vtbl =
1244 {
1245     xmlreader_QueryInterface,
1246     xmlreader_AddRef,
1247     xmlreader_Release,
1248     xmlreader_SetInput,
1249     xmlreader_GetProperty,
1250     xmlreader_SetProperty,
1251     xmlreader_Read,
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,
1261     xmlreader_GetValue,
1262     xmlreader_ReadValueChunk,
1263     xmlreader_GetBaseUri,
1264     xmlreader_IsDefault,
1265     xmlreader_IsEmptyElement,
1266     xmlreader_GetLineNumber,
1267     xmlreader_GetLinePosition,
1268     xmlreader_GetAttributeCount,
1269     xmlreader_GetDepth,
1270     xmlreader_IsEOF
1271 };
1272
1273 /** IXmlReaderInput **/
1274 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1275 {
1276     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1277
1278     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1279
1280     if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1281         IsEqualGUID(riid, &IID_IUnknown))
1282     {
1283         *ppvObject = iface;
1284     }
1285     else
1286     {
1287         WARN("interface %s not implemented\n", debugstr_guid(riid));
1288         return E_NOINTERFACE;
1289     }
1290
1291     IUnknown_AddRef(iface);
1292
1293     return S_OK;
1294 }
1295
1296 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1297 {
1298     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1299     ULONG ref = InterlockedIncrement(&This->ref);
1300     TRACE("(%p)->(%d)\n", This, ref);
1301     return ref;
1302 }
1303
1304 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1305 {
1306     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1307     LONG ref = InterlockedDecrement(&This->ref);
1308
1309     TRACE("(%p)->(%d)\n", This, ref);
1310
1311     if (ref == 0)
1312     {
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);
1320     }
1321
1322     return ref;
1323 }
1324
1325 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1326 {
1327     xmlreaderinput_QueryInterface,
1328     xmlreaderinput_AddRef,
1329     xmlreaderinput_Release
1330 };
1331
1332 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1333 {
1334     xmlreader *reader;
1335
1336     TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1337
1338     if (!IsEqualGUID(riid, &IID_IXmlReader))
1339     {
1340         ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1341         return E_FAIL;
1342     }
1343
1344     if (imalloc)
1345         reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1346     else
1347         reader = heap_alloc(sizeof(*reader));
1348     if(!reader) return E_OUTOFMEMORY;
1349
1350     reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1351     reader->ref = 1;
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;
1363
1364     *obj = &reader->IXmlReader_iface;
1365
1366     TRACE("returning iface %p\n", *obj);
1367
1368     return S_OK;
1369 }
1370
1371 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1372                                                     IMalloc *imalloc,
1373                                                     LPCWSTR encoding,
1374                                                     BOOL hint,
1375                                                     LPCWSTR base_uri,
1376                                                     IXmlReaderInput **ppInput)
1377 {
1378     xmlreaderinput *readerinput;
1379     HRESULT hr;
1380
1381     TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1382                                        hint, wine_dbgstr_w(base_uri), ppInput);
1383
1384     if (!stream || !ppInput) return E_INVALIDARG;
1385
1386     if (imalloc)
1387         readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1388     else
1389         readerinput = heap_alloc(sizeof(*readerinput));
1390     if(!readerinput) return E_OUTOFMEMORY;
1391
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);
1400
1401     hr = alloc_input_buffer(readerinput);
1402     if (hr != S_OK)
1403     {
1404         readerinput_free(readerinput, readerinput);
1405         return hr;
1406     }
1407     IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1408
1409     *ppInput = &readerinput->IXmlReaderInput_iface;
1410
1411     TRACE("returning iface %p\n", *ppInput);
1412
1413     return S_OK;
1414 }