mmdevapi: Implement (Un)RegisterEndpointNotificationCallback.
[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 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
48 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
49
50 static const WCHAR dblquoteW[] = {'\"',0};
51 static const WCHAR quoteW[] = {'\'',0};
52
53 struct xml_encoding_data
54 {
55     const WCHAR *name;
56     xml_encoding enc;
57     UINT cp;
58 };
59
60 static const struct xml_encoding_data xml_encoding_map[] = {
61     { utf16W, XmlEncoding_UTF16, ~0 },
62     { utf8W,  XmlEncoding_UTF8,  CP_UTF8 }
63 };
64
65 typedef struct
66 {
67     char *data;
68     char *cur;
69     unsigned int allocated;
70     unsigned int written;
71 } encoded_buffer;
72
73 typedef struct input_buffer input_buffer;
74
75 typedef struct _xmlreaderinput
76 {
77     IXmlReaderInput IXmlReaderInput_iface;
78     LONG ref;
79     /* reference passed on IXmlReaderInput creation, is kept when input is created */
80     IUnknown *input;
81     IMalloc *imalloc;
82     xml_encoding encoding;
83     BOOL hint;
84     WCHAR *baseuri;
85     /* stream reference set after SetInput() call from reader,
86        stored as sequential stream, cause currently
87        optimizations possible with IStream aren't implemented */
88     ISequentialStream *stream;
89     input_buffer *buffer;
90 } xmlreaderinput;
91
92 typedef struct
93 {
94     const WCHAR *str;
95     UINT len;
96 } strval;
97
98 struct attribute
99 {
100     struct list entry;
101     strval localname;
102     strval value;
103 };
104
105 typedef struct _xmlreader
106 {
107     IXmlReader IXmlReader_iface;
108     LONG ref;
109     xmlreaderinput *input;
110     IMalloc *imalloc;
111     XmlReadState state;
112     XmlNodeType nodetype;
113     DtdProcessing dtdmode;
114     UINT line, pos;           /* reader position in XML stream */
115     struct list attrs; /* attributes list for current node */
116     struct attribute *attr; /* current attribute */
117     UINT attr_count;
118 } xmlreader;
119
120 struct input_buffer
121 {
122     encoded_buffer utf16;
123     encoded_buffer encoded;
124     UINT code_page;
125     xmlreaderinput *input;
126 };
127
128 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
129 {
130     return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
131 }
132
133 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
134 {
135     return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
136 }
137
138 static inline void *m_alloc(IMalloc *imalloc, size_t len)
139 {
140     if (imalloc)
141         return IMalloc_Alloc(imalloc, len);
142     else
143         return heap_alloc(len);
144 }
145
146 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
147 {
148     if (imalloc)
149         return IMalloc_Realloc(imalloc, mem, len);
150     else
151         return heap_realloc(mem, len);
152 }
153
154 static inline void m_free(IMalloc *imalloc, void *mem)
155 {
156     if (imalloc)
157         IMalloc_Free(imalloc, mem);
158     else
159         heap_free(mem);
160 }
161
162 /* reader memory allocation functions */
163 static inline void *reader_alloc(xmlreader *reader, size_t len)
164 {
165     return m_alloc(reader->imalloc, len);
166 }
167
168 static inline void reader_free(xmlreader *reader, void *mem)
169 {
170     m_free(reader->imalloc, mem);
171 }
172
173 /* reader input memory allocation functions */
174 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
175 {
176     return m_alloc(input->imalloc, len);
177 }
178
179 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
180 {
181     return m_realloc(input->imalloc, mem, len);
182 }
183
184 static inline void readerinput_free(xmlreaderinput *input, void *mem)
185 {
186     m_free(input->imalloc, mem);
187 }
188
189 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
190 {
191     LPWSTR ret = NULL;
192
193     if(str) {
194         DWORD size;
195
196         size = (strlenW(str)+1)*sizeof(WCHAR);
197         ret = readerinput_alloc(input, size);
198         if (ret) memcpy(ret, str, size);
199     }
200
201     return ret;
202 }
203
204 static void reader_clear_attrs(xmlreader *reader)
205 {
206     struct attribute *attr, *attr2;
207     LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
208     {
209         reader_free(reader, attr);
210     }
211     list_init(&reader->attrs);
212     reader->attr_count = 0;
213 }
214
215 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
216    while we are on a node with attributes */
217 static HRESULT reader_add_attr(xmlreader *reader, strval *localname, strval *value)
218 {
219     struct attribute *attr;
220
221     attr = reader_alloc(reader, sizeof(*attr));
222     if (!attr) return E_OUTOFMEMORY;
223
224     attr->localname = *localname;
225     attr->value = *value;
226     list_add_tail(&reader->attrs, &attr->entry);
227     reader->attr_count++;
228
229     return S_OK;
230 }
231
232 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
233 {
234     const int initial_len = 0x2000;
235     buffer->data = readerinput_alloc(input, initial_len);
236     if (!buffer->data) return E_OUTOFMEMORY;
237
238     memset(buffer->data, 0, 4);
239     buffer->cur = buffer->data;
240     buffer->allocated = initial_len;
241     buffer->written = 0;
242
243     return S_OK;
244 }
245
246 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
247 {
248     readerinput_free(input, buffer->data);
249 }
250
251 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
252 {
253     if (encoding == XmlEncoding_Unknown)
254     {
255         FIXME("unsupported encoding %d\n", encoding);
256         return E_NOTIMPL;
257     }
258
259     *cp = xml_encoding_map[encoding].cp;
260
261     return S_OK;
262 }
263
264 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
265 {
266     int min, max, n, c;
267
268     if (!name) return XmlEncoding_Unknown;
269
270     min = 0;
271     max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
272
273     while (min <= max)
274     {
275         n = (min+max)/2;
276
277         if (len != -1)
278             c = strncmpiW(xml_encoding_map[n].name, name, len);
279         else
280             c = strcmpiW(xml_encoding_map[n].name, name);
281         if (!c)
282             return xml_encoding_map[n].enc;
283
284         if (c > 0)
285             max = n-1;
286         else
287             min = n+1;
288     }
289
290     return XmlEncoding_Unknown;
291 }
292
293 static HRESULT alloc_input_buffer(xmlreaderinput *input)
294 {
295     input_buffer *buffer;
296     HRESULT hr;
297
298     input->buffer = NULL;
299
300     buffer = readerinput_alloc(input, sizeof(*buffer));
301     if (!buffer) return E_OUTOFMEMORY;
302
303     buffer->input = input;
304     buffer->code_page = ~0; /* code page is unknown at this point */
305     hr = init_encoded_buffer(input, &buffer->utf16);
306     if (hr != S_OK) {
307         readerinput_free(input, buffer);
308         return hr;
309     }
310
311     hr = init_encoded_buffer(input, &buffer->encoded);
312     if (hr != S_OK) {
313         free_encoded_buffer(input, &buffer->utf16);
314         readerinput_free(input, buffer);
315         return hr;
316     }
317
318     input->buffer = buffer;
319     return S_OK;
320 }
321
322 static void free_input_buffer(input_buffer *buffer)
323 {
324     free_encoded_buffer(buffer->input, &buffer->encoded);
325     free_encoded_buffer(buffer->input, &buffer->utf16);
326     readerinput_free(buffer->input, buffer);
327 }
328
329 static void readerinput_release_stream(xmlreaderinput *readerinput)
330 {
331     if (readerinput->stream) {
332         ISequentialStream_Release(readerinput->stream);
333         readerinput->stream = NULL;
334     }
335 }
336
337 /* Queries already stored interface for IStream/ISequentialStream.
338    Interface supplied on creation will be overwritten */
339 static HRESULT readerinput_query_for_stream(xmlreaderinput *readerinput)
340 {
341     HRESULT hr;
342
343     readerinput_release_stream(readerinput);
344     hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
345     if (hr != S_OK)
346         hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
347
348     return hr;
349 }
350
351 /* reads a chunk to raw buffer */
352 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
353 {
354     encoded_buffer *buffer = &readerinput->buffer->encoded;
355     ULONG len = buffer->allocated - buffer->written, read;
356     HRESULT hr;
357
358     /* always try to get aligned to 4 bytes, so the only case we can get partialy read characters is
359        variable width encodings like UTF-8 */
360     len = (len + 3) & ~3;
361     /* try to use allocated space or grow */
362     if (buffer->allocated - buffer->written < len)
363     {
364         buffer->allocated *= 2;
365         buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
366         len = buffer->allocated - buffer->written;
367     }
368
369     hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
370     if (FAILED(hr)) return hr;
371     TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
372     buffer->written += read;
373
374     return hr;
375 }
376
377 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
378 static void readerinput_grow(xmlreaderinput *readerinput, int length)
379 {
380     encoded_buffer *buffer = &readerinput->buffer->utf16;
381
382     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
383     if (buffer->allocated < buffer->written + length + 4)
384     {
385         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
386         buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
387         buffer->allocated = grown_size;
388     }
389 }
390
391 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
392 {
393     encoded_buffer *buffer = &readerinput->buffer->encoded;
394     static char startA[] = {'<','?','x','m'};
395     static WCHAR startW[] = {'<','?'};
396     static char utf8bom[] = {0xef,0xbb,0xbf};
397     static char utf16lebom[] = {0xff,0xfe};
398
399     *enc = XmlEncoding_Unknown;
400
401     if (buffer->written <= 3) return MX_E_INPUTEND;
402
403     /* try start symbols if we have enough data to do that, input buffer should contain
404        first chunk already */
405     if (!memcmp(buffer->data, startA, sizeof(startA)))
406         *enc = XmlEncoding_UTF8;
407     else if (!memcmp(buffer->data, startW, sizeof(startW)))
408         *enc = XmlEncoding_UTF16;
409     /* try with BOM now */
410     else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
411     {
412         buffer->cur += sizeof(utf8bom);
413         *enc = XmlEncoding_UTF8;
414     }
415     else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
416     {
417         buffer->cur += sizeof(utf16lebom);
418         *enc = XmlEncoding_UTF16;
419     }
420
421     return S_OK;
422 }
423
424 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
425 {
426     encoded_buffer *buffer = &readerinput->buffer->encoded;
427     int len = buffer->written;
428
429     /* complete single byte char */
430     if (!(buffer->data[len-1] & 0x80)) return len;
431
432     /* find start byte of multibyte char */
433     while (--len && !(buffer->data[len] & 0xc0))
434         ;
435
436     return len;
437 }
438
439 /* returns byte length of complete char sequence for specified code page, */
440 static int readerinput_get_convlen(xmlreaderinput *readerinput, UINT cp)
441 {
442     encoded_buffer *buffer = &readerinput->buffer->encoded;
443     int len = buffer->written;
444
445     if (cp == CP_UTF8)
446         len = readerinput_get_utf8_convlen(readerinput);
447     else
448         len = buffer->written;
449
450     return len - (buffer->cur - buffer->data);
451 }
452
453 /* note that raw buffer content is kept */
454 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
455 {
456     encoded_buffer *src = &readerinput->buffer->encoded;
457     encoded_buffer *dest = &readerinput->buffer->utf16;
458     int len, dest_len;
459     HRESULT hr;
460     UINT cp;
461
462     hr = get_code_page(enc, &cp);
463     if (FAILED(hr)) return;
464
465     len = readerinput_get_convlen(readerinput, cp);
466
467     TRACE("switching to cp %d\n", cp);
468
469     /* just copy in this case */
470     if (enc == XmlEncoding_UTF16)
471     {
472         readerinput_grow(readerinput, len);
473         memcpy(dest->data, src->cur, len);
474         readerinput->buffer->code_page = cp;
475         return;
476     }
477
478     dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
479     readerinput_grow(readerinput, dest_len);
480     MultiByteToWideChar(cp, 0, src->cur, len, (WCHAR*)dest->data, dest_len);
481     dest->data[dest_len] = 0;
482     readerinput->buffer->code_page = cp;
483 }
484
485 static inline const WCHAR *reader_get_cur(xmlreader *reader)
486 {
487     return (WCHAR*)reader->input->buffer->utf16.cur;
488 }
489
490 static int reader_cmp(xmlreader *reader, const WCHAR *str)
491 {
492     const WCHAR *ptr = reader_get_cur(reader);
493     int i = 0;
494
495     return strncmpW(str, ptr, strlenW(str));
496
497     while (str[i]) {
498         if (ptr[i] != str[i]) return 0;
499         i++;
500     }
501
502     return 1;
503 }
504
505 /* moves cursor n WCHARs forward */
506 static void reader_skipn(xmlreader *reader, int n)
507 {
508     encoded_buffer *buffer = &reader->input->buffer->utf16;
509     const WCHAR *ptr = reader_get_cur(reader);
510
511     while (*ptr++ && n--)
512     {
513         buffer->cur += sizeof(WCHAR);
514         reader->pos++;
515     }
516 }
517
518 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
519 static int reader_skipspaces(xmlreader *reader)
520 {
521     encoded_buffer *buffer = &reader->input->buffer->utf16;
522     const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
523
524     while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')
525     {
526         buffer->cur += sizeof(WCHAR);
527         if (*ptr == '\r')
528             reader->pos = 0;
529         else if (*ptr == '\n')
530         {
531             reader->line++;
532             reader->pos = 0;
533         }
534         else
535             reader->pos++;
536         ptr++;
537     }
538
539     return ptr - start;
540 }
541
542 /* [26] VersionNum ::= '1.' [0-9]+ */
543 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
544 {
545     const WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
546     static const WCHAR onedotW[] = {'1','.',0};
547
548     if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
549     /* skip "1." */
550     reader_skipn(reader, 2);
551
552     ptr2 = ptr = reader_get_cur(reader);
553     while (*ptr >= '0' && *ptr <= '9')
554         ptr++;
555
556     if (ptr2 == ptr) return WC_E_DIGIT;
557     TRACE("version=%s\n", debugstr_wn(start, ptr-start));
558     val->str = start;
559     val->len = ptr-start;
560     reader_skipn(reader, ptr-ptr2);
561     return S_OK;
562 }
563
564 /* [25] Eq ::= S? '=' S? */
565 static HRESULT reader_parse_eq(xmlreader *reader)
566 {
567     static const WCHAR eqW[] = {'=',0};
568     reader_skipspaces(reader);
569     if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
570     /* skip '=' */
571     reader_skipn(reader, 1);
572     reader_skipspaces(reader);
573     return S_OK;
574 }
575
576 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
577 static HRESULT reader_parse_versioninfo(xmlreader *reader)
578 {
579     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
580     strval val, name;
581     HRESULT hr;
582
583     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
584
585     if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
586     name.str = reader_get_cur(reader);
587     name.len = 7;
588     /* skip 'version' */
589     reader_skipn(reader, 7);
590
591     hr = reader_parse_eq(reader);
592     if (FAILED(hr)) return hr;
593
594     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
595         return WC_E_QUOTE;
596     /* skip "'"|'"' */
597     reader_skipn(reader, 1);
598
599     hr = reader_parse_versionnum(reader, &val);
600     if (FAILED(hr)) return hr;
601
602     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
603         return WC_E_QUOTE;
604
605     /* skip "'"|'"' */
606     reader_skipn(reader, 1);
607
608     return reader_add_attr(reader, &name, &val);
609 }
610
611 /* ([A-Za-z0-9._] | '-') */
612 static inline int is_wchar_encname(WCHAR ch)
613 {
614     return ((ch >= 'A' && ch <= 'Z') ||
615             (ch >= 'a' && ch <= 'z') ||
616             (ch >= '0' && ch <= '9') ||
617             (ch == '.') || (ch == '_') ||
618             (ch == '-'));
619 }
620
621 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
622 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
623 {
624     const WCHAR *start = reader_get_cur(reader), *ptr;
625     xml_encoding enc;
626     int len;
627
628     if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
629         return WC_E_ENCNAME;
630
631     ptr = start;
632     while (is_wchar_encname(*++ptr))
633         ;
634
635     len = ptr - start;
636     enc = parse_encoding_name(start, len);
637     TRACE("encoding name %s\n", debugstr_wn(start, len));
638     val->str = start;
639     val->len = len;
640
641     if (enc == XmlEncoding_Unknown)
642         return WC_E_ENCNAME;
643
644     /* skip encoding name */
645     reader_skipn(reader, len);
646     return S_OK;
647 }
648
649 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
650 static HRESULT reader_parse_encdecl(xmlreader *reader)
651 {
652     static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
653     strval name, val;
654     HRESULT hr;
655
656     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
657
658     if (reader_cmp(reader, encodingW)) return S_FALSE;
659     name.str = reader_get_cur(reader);
660     name.len = 8;
661     /* skip 'encoding' */
662     reader_skipn(reader, 8);
663
664     hr = reader_parse_eq(reader);
665     if (FAILED(hr)) return hr;
666
667     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
668         return WC_E_QUOTE;
669     /* skip "'"|'"' */
670     reader_skipn(reader, 1);
671
672     hr = reader_parse_encname(reader, &val);
673     if (FAILED(hr)) return hr;
674
675     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
676         return WC_E_QUOTE;
677
678     /* skip "'"|'"' */
679     reader_skipn(reader, 1);
680
681     return reader_add_attr(reader, &name, &val);
682 }
683
684 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
685 static HRESULT reader_parse_sddecl(xmlreader *reader)
686 {
687     static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
688     static const WCHAR yesW[] = {'y','e','s',0};
689     static const WCHAR noW[] = {'n','o',0};
690     const WCHAR *start, *ptr;
691     strval name, val;
692     HRESULT hr;
693
694     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
695
696     if (reader_cmp(reader, standaloneW)) return S_FALSE;
697     name.str = reader_get_cur(reader);
698     name.len = 10;
699     /* skip 'standalone' */
700     reader_skipn(reader, 10);
701
702     hr = reader_parse_eq(reader);
703     if (FAILED(hr)) return hr;
704
705     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
706         return WC_E_QUOTE;
707     /* skip "'"|'"' */
708     reader_skipn(reader, 1);
709
710     if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
711         return WC_E_XMLDECL;
712
713     start = reader_get_cur(reader);
714     /* skip 'yes'|'no' */
715     reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
716     ptr = reader_get_cur(reader);
717     TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
718     val.str = start;
719     val.len = ptr-start;
720
721     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
722         return WC_E_QUOTE;
723     /* skip "'"|'"' */
724     reader_skipn(reader, 1);
725
726     return reader_add_attr(reader, &name, &val);
727 }
728
729 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
730 static HRESULT reader_parse_xmldecl(xmlreader *reader)
731 {
732     static const WCHAR xmldeclW[] = {'<','?','x','m','l',0};
733     static const WCHAR declcloseW[] = {'?','>',0};
734     HRESULT hr;
735
736     /* check if we have "<?xml" */
737     if (reader_cmp(reader, xmldeclW)) return S_FALSE;
738
739     reader_skipn(reader, 5);
740     hr = reader_parse_versioninfo(reader);
741     if (FAILED(hr))
742         return hr;
743
744     hr = reader_parse_encdecl(reader);
745     if (FAILED(hr))
746         return hr;
747
748     hr = reader_parse_sddecl(reader);
749     if (FAILED(hr))
750         return hr;
751
752     reader_skipspaces(reader);
753     if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
754     reader_skipn(reader, 2);
755
756     return S_OK;
757 }
758
759 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
760 {
761     xmlreader *This = impl_from_IXmlReader(iface);
762
763     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
764
765     if (IsEqualGUID(riid, &IID_IUnknown) ||
766         IsEqualGUID(riid, &IID_IXmlReader))
767     {
768         *ppvObject = iface;
769     }
770     else
771     {
772         FIXME("interface %s not implemented\n", debugstr_guid(riid));
773         return E_NOINTERFACE;
774     }
775
776     IXmlReader_AddRef(iface);
777
778     return S_OK;
779 }
780
781 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
782 {
783     xmlreader *This = impl_from_IXmlReader(iface);
784     ULONG ref = InterlockedIncrement(&This->ref);
785     TRACE("(%p)->(%d)\n", This, ref);
786     return ref;
787 }
788
789 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
790 {
791     xmlreader *This = impl_from_IXmlReader(iface);
792     LONG ref = InterlockedDecrement(&This->ref);
793
794     TRACE("(%p)->(%d)\n", This, ref);
795
796     if (ref == 0)
797     {
798         IMalloc *imalloc = This->imalloc;
799         if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
800         reader_clear_attrs(This);
801         reader_free(This, This);
802         if (imalloc) IMalloc_Release(imalloc);
803     }
804
805     return ref;
806 }
807
808 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
809 {
810     xmlreader *This = impl_from_IXmlReader(iface);
811     HRESULT hr;
812
813     TRACE("(%p %p)\n", This, input);
814
815     if (This->input)
816     {
817         readerinput_release_stream(This->input);
818         IUnknown_Release(&This->input->IXmlReaderInput_iface);
819         This->input = NULL;
820     }
821
822     This->line = This->pos = 0;
823
824     /* just reset current input */
825     if (!input)
826     {
827         This->state = XmlReadState_Initial;
828         return S_OK;
829     }
830
831     /* now try IXmlReaderInput, ISequentialStream, IStream */
832     hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
833     if (hr != S_OK)
834     {
835         IXmlReaderInput *readerinput;
836
837         /* create IXmlReaderInput basing on supplied interface */
838         hr = CreateXmlReaderInputWithEncodingName(input,
839                                          NULL, NULL, FALSE, NULL, &readerinput);
840         if (hr != S_OK) return hr;
841         This->input = impl_from_IXmlReaderInput(readerinput);
842     }
843
844     /* set stream for supplied IXmlReaderInput */
845     hr = readerinput_query_for_stream(This->input);
846     if (hr == S_OK)
847         This->state = XmlReadState_Initial;
848
849     return hr;
850 }
851
852 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
853 {
854     xmlreader *This = impl_from_IXmlReader(iface);
855
856     TRACE("(%p %u %p)\n", This, property, value);
857
858     if (!value) return E_INVALIDARG;
859
860     switch (property)
861     {
862         case XmlReaderProperty_DtdProcessing:
863             *value = This->dtdmode;
864             break;
865         case XmlReaderProperty_ReadState:
866             *value = This->state;
867             break;
868         default:
869             FIXME("Unimplemented property (%u)\n", property);
870             return E_NOTIMPL;
871     }
872
873     return S_OK;
874 }
875
876 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
877 {
878     xmlreader *This = impl_from_IXmlReader(iface);
879
880     TRACE("(%p %u %lu)\n", iface, property, value);
881
882     switch (property)
883     {
884         case XmlReaderProperty_DtdProcessing:
885             if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
886             This->dtdmode = value;
887             break;
888         default:
889             FIXME("Unimplemented property (%u)\n", property);
890             return E_NOTIMPL;
891     }
892
893     return S_OK;
894 }
895
896 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *node_type)
897 {
898     xmlreader *This = impl_from_IXmlReader(iface);
899
900     FIXME("(%p)->(%p): stub\n", This, node_type);
901
902     if (This->state == XmlReadState_Closed) return S_FALSE;
903
904     /* if it's a first call for a new input we need to detect stream encoding */
905     if (This->state == XmlReadState_Initial)
906     {
907         xml_encoding enc;
908         HRESULT hr;
909
910         hr = readerinput_growraw(This->input);
911         if (FAILED(hr)) return hr;
912
913         /* try to detect encoding by BOM or data and set input code page */
914         hr = readerinput_detectencoding(This->input, &enc);
915         TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
916         if (FAILED(hr)) return hr;
917
918         /* always switch first time cause we have to put something in */
919         readerinput_switchencoding(This->input, enc);
920
921         /* parse xml declaration */
922         hr = reader_parse_xmldecl(This);
923         if (FAILED(hr)) return hr;
924
925         if (hr == S_OK)
926         {
927             This->state = XmlReadState_Interactive;
928             This->nodetype = *node_type = XmlNodeType_XmlDeclaration;
929             return S_OK;
930         }
931     }
932
933     return E_NOTIMPL;
934 }
935
936 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
937 {
938     xmlreader *This = impl_from_IXmlReader(iface);
939     TRACE("(%p)->(%p)\n", This, node_type);
940
941     /* When we're on attribute always return attribute type, container node type is kept.
942        Note that container is not necessarily an element, and attribute doesn't mean it's
943        an attribute in XML spec terms. */
944     *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
945     return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
946 }
947
948 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
949 {
950     xmlreader *This = impl_from_IXmlReader(iface);
951
952     TRACE("(%p)\n", This);
953
954     if (!This->attr_count) return S_FALSE;
955     This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
956     return S_OK;
957 }
958
959 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
960 {
961     xmlreader *This = impl_from_IXmlReader(iface);
962     const struct list *next;
963
964     TRACE("(%p)\n", This);
965
966     if (!This->attr_count) return S_FALSE;
967
968     if (!This->attr)
969         return IXmlReader_MoveToFirstAttribute(iface);
970
971     next = list_next(&This->attrs, &This->attr->entry);
972     if (next)
973         This->attr = LIST_ENTRY(next, struct attribute, entry);
974
975     return next ? S_OK : S_FALSE;
976 }
977
978 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
979                                                       LPCWSTR local_name,
980                                                       LPCWSTR namespaceUri)
981 {
982     FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
983     return E_NOTIMPL;
984 }
985
986 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
987 {
988     xmlreader *This = impl_from_IXmlReader(iface);
989
990     TRACE("(%p)\n", This);
991
992     if (!This->attr_count) return S_FALSE;
993     This->attr = NULL;
994     return S_OK;
995 }
996
997 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *qualifiedName,
998                                                  UINT *qualifiedName_length)
999 {
1000     FIXME("(%p %p %p): stub\n", iface, qualifiedName, qualifiedName_length);
1001     return E_NOTIMPL;
1002 }
1003
1004 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1005                                                 LPCWSTR *namespaceUri,
1006                                                 UINT *namespaceUri_length)
1007 {
1008     FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1009     return E_NOTIMPL;
1010 }
1011
1012 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface,
1013                                              LPCWSTR *local_name,
1014                                              UINT *local_name_length)
1015 {
1016     FIXME("(%p %p %p): stub\n", iface, local_name, local_name_length);
1017     return E_NOTIMPL;
1018 }
1019
1020 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1021                                           LPCWSTR *prefix,
1022                                           UINT *prefix_length)
1023 {
1024     FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1025     return E_NOTIMPL;
1026 }
1027
1028 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface,
1029                                          LPCWSTR *value,
1030                                          UINT *value_length)
1031 {
1032     FIXME("(%p %p %p): stub\n", iface, value, value_length);
1033     return E_NOTIMPL;
1034 }
1035
1036 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1037                                                WCHAR *buffer,
1038                                                UINT   chunk_size,
1039                                                UINT  *read)
1040 {
1041     FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1042     return E_NOTIMPL;
1043 }
1044
1045 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1046                                            LPCWSTR *baseUri,
1047                                            UINT *baseUri_length)
1048 {
1049     FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1050     return E_NOTIMPL;
1051 }
1052
1053 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1054 {
1055     FIXME("(%p): stub\n", iface);
1056     return E_NOTIMPL;
1057 }
1058
1059 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1060 {
1061     FIXME("(%p): stub\n", iface);
1062     return E_NOTIMPL;
1063 }
1064
1065 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1066 {
1067     xmlreader *This = impl_from_IXmlReader(iface);
1068
1069     TRACE("(%p %p)\n", This, lineNumber);
1070
1071     if (!lineNumber) return E_INVALIDARG;
1072
1073     *lineNumber = This->line;
1074
1075     return S_OK;
1076 }
1077
1078 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1079 {
1080     xmlreader *This = impl_from_IXmlReader(iface);
1081
1082     TRACE("(%p %p)\n", This, linePosition);
1083
1084     if (!linePosition) return E_INVALIDARG;
1085
1086     *linePosition = This->pos;
1087
1088     return S_OK;
1089 }
1090
1091 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1092 {
1093     xmlreader *This = impl_from_IXmlReader(iface);
1094
1095     TRACE("(%p)->(%p)\n", This, count);
1096
1097     if (!count) return E_INVALIDARG;
1098
1099     *count = This->attr_count;
1100     return S_OK;
1101 }
1102
1103 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1104 {
1105     FIXME("(%p %p): stub\n", iface, depth);
1106     return E_NOTIMPL;
1107 }
1108
1109 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1110 {
1111     FIXME("(%p): stub\n", iface);
1112     return E_NOTIMPL;
1113 }
1114
1115 static const struct IXmlReaderVtbl xmlreader_vtbl =
1116 {
1117     xmlreader_QueryInterface,
1118     xmlreader_AddRef,
1119     xmlreader_Release,
1120     xmlreader_SetInput,
1121     xmlreader_GetProperty,
1122     xmlreader_SetProperty,
1123     xmlreader_Read,
1124     xmlreader_GetNodeType,
1125     xmlreader_MoveToFirstAttribute,
1126     xmlreader_MoveToNextAttribute,
1127     xmlreader_MoveToAttributeByName,
1128     xmlreader_MoveToElement,
1129     xmlreader_GetQualifiedName,
1130     xmlreader_GetNamespaceUri,
1131     xmlreader_GetLocalName,
1132     xmlreader_GetPrefix,
1133     xmlreader_GetValue,
1134     xmlreader_ReadValueChunk,
1135     xmlreader_GetBaseUri,
1136     xmlreader_IsDefault,
1137     xmlreader_IsEmptyElement,
1138     xmlreader_GetLineNumber,
1139     xmlreader_GetLinePosition,
1140     xmlreader_GetAttributeCount,
1141     xmlreader_GetDepth,
1142     xmlreader_IsEOF
1143 };
1144
1145 /** IXmlReaderInput **/
1146 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1147 {
1148     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1149
1150     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1151
1152     if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1153         IsEqualGUID(riid, &IID_IUnknown))
1154     {
1155         *ppvObject = iface;
1156     }
1157     else
1158     {
1159         WARN("interface %s not implemented\n", debugstr_guid(riid));
1160         return E_NOINTERFACE;
1161     }
1162
1163     IUnknown_AddRef(iface);
1164
1165     return S_OK;
1166 }
1167
1168 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1169 {
1170     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1171     ULONG ref = InterlockedIncrement(&This->ref);
1172     TRACE("(%p)->(%d)\n", This, ref);
1173     return ref;
1174 }
1175
1176 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1177 {
1178     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1179     LONG ref = InterlockedDecrement(&This->ref);
1180
1181     TRACE("(%p)->(%d)\n", This, ref);
1182
1183     if (ref == 0)
1184     {
1185         IMalloc *imalloc = This->imalloc;
1186         if (This->input) IUnknown_Release(This->input);
1187         if (This->stream) ISequentialStream_Release(This->stream);
1188         if (This->buffer) free_input_buffer(This->buffer);
1189         readerinput_free(This, This->baseuri);
1190         readerinput_free(This, This);
1191         if (imalloc) IMalloc_Release(imalloc);
1192     }
1193
1194     return ref;
1195 }
1196
1197 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1198 {
1199     xmlreaderinput_QueryInterface,
1200     xmlreaderinput_AddRef,
1201     xmlreaderinput_Release
1202 };
1203
1204 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1205 {
1206     xmlreader *reader;
1207
1208     TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1209
1210     if (!IsEqualGUID(riid, &IID_IXmlReader))
1211     {
1212         ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1213         return E_FAIL;
1214     }
1215
1216     if (imalloc)
1217         reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1218     else
1219         reader = heap_alloc(sizeof(*reader));
1220     if(!reader) return E_OUTOFMEMORY;
1221
1222     reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1223     reader->ref = 1;
1224     reader->input = NULL;
1225     reader->state = XmlReadState_Closed;
1226     reader->dtdmode = DtdProcessing_Prohibit;
1227     reader->line  = reader->pos = 0;
1228     reader->imalloc = imalloc;
1229     if (imalloc) IMalloc_AddRef(imalloc);
1230     reader->nodetype = XmlNodeType_None;
1231     list_init(&reader->attrs);
1232     reader->attr_count = 0;
1233     reader->attr = NULL;
1234
1235     *obj = &reader->IXmlReader_iface;
1236
1237     TRACE("returning iface %p\n", *obj);
1238
1239     return S_OK;
1240 }
1241
1242 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1243                                                     IMalloc *imalloc,
1244                                                     LPCWSTR encoding,
1245                                                     BOOL hint,
1246                                                     LPCWSTR base_uri,
1247                                                     IXmlReaderInput **ppInput)
1248 {
1249     xmlreaderinput *readerinput;
1250     HRESULT hr;
1251
1252     TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1253                                        hint, wine_dbgstr_w(base_uri), ppInput);
1254
1255     if (!stream || !ppInput) return E_INVALIDARG;
1256
1257     if (imalloc)
1258         readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1259     else
1260         readerinput = heap_alloc(sizeof(*readerinput));
1261     if(!readerinput) return E_OUTOFMEMORY;
1262
1263     readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinput_vtbl;
1264     readerinput->ref = 1;
1265     readerinput->imalloc = imalloc;
1266     readerinput->stream = NULL;
1267     if (imalloc) IMalloc_AddRef(imalloc);
1268     readerinput->encoding = parse_encoding_name(encoding, -1);
1269     readerinput->hint = hint;
1270     readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
1271
1272     hr = alloc_input_buffer(readerinput);
1273     if (hr != S_OK)
1274     {
1275         readerinput_free(readerinput, readerinput);
1276         return hr;
1277     }
1278     IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1279
1280     *ppInput = &readerinput->IXmlReaderInput_iface;
1281
1282     TRACE("returning iface %p\n", *ppInput);
1283
1284     return S_OK;
1285 }