wmiutils: Implement IWbemPath::SetClassName.
[wine] / dlls / xmllite / reader.c
1 /*
2  * IXmlReader implementation
3  *
4  * Copyright 2010, 2012-2013 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     XmlReadInState_DTD_Misc,
54     XmlReadInState_Element
55 } XmlReaderInternalState;
56
57 typedef enum
58 {
59     StringValue_LocalName,
60     StringValue_QualifiedName,
61     StringValue_Value,
62     StringValue_Last
63 } XmlReaderStringValue;
64
65 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
66 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
67
68 static const WCHAR dblquoteW[] = {'\"',0};
69 static const WCHAR quoteW[] = {'\'',0};
70
71 struct xml_encoding_data
72 {
73     const WCHAR *name;
74     xml_encoding enc;
75     UINT cp;
76 };
77
78 static const struct xml_encoding_data xml_encoding_map[] = {
79     { utf16W, XmlEncoding_UTF16, ~0 },
80     { utf8W,  XmlEncoding_UTF8,  CP_UTF8 }
81 };
82
83 typedef struct
84 {
85     char *data;
86     char *cur;
87     unsigned int allocated;
88     unsigned int written;
89 } encoded_buffer;
90
91 typedef struct input_buffer input_buffer;
92
93 typedef struct
94 {
95     IXmlReaderInput IXmlReaderInput_iface;
96     LONG ref;
97     /* reference passed on IXmlReaderInput creation, is kept when input is created */
98     IUnknown *input;
99     IMalloc *imalloc;
100     xml_encoding encoding;
101     BOOL hint;
102     WCHAR *baseuri;
103     /* stream reference set after SetInput() call from reader,
104        stored as sequential stream, cause currently
105        optimizations possible with IStream aren't implemented */
106     ISequentialStream *stream;
107     input_buffer *buffer;
108 } xmlreaderinput;
109
110 typedef struct
111 {
112     WCHAR *str;
113     UINT len;
114 } strval;
115
116 static WCHAR emptyW[] = {0};
117 static const strval strval_empty = {emptyW, 0};
118
119 struct attribute
120 {
121     struct list entry;
122     strval localname;
123     strval value;
124 };
125
126 typedef struct
127 {
128     IXmlReader IXmlReader_iface;
129     LONG ref;
130     xmlreaderinput *input;
131     IMalloc *imalloc;
132     XmlReadState state;
133     XmlReaderInternalState instate;
134     XmlNodeType nodetype;
135     DtdProcessing dtdmode;
136     UINT line, pos;           /* reader position in XML stream */
137     struct list attrs; /* attributes list for current node */
138     struct attribute *attr; /* current attribute */
139     UINT attr_count;
140     strval strvalues[StringValue_Last];
141 } xmlreader;
142
143 struct input_buffer
144 {
145     encoded_buffer utf16;
146     encoded_buffer encoded;
147     UINT code_page;
148     xmlreaderinput *input;
149 };
150
151 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
152 {
153     return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
154 }
155
156 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
157 {
158     return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
159 }
160
161 static inline void *m_alloc(IMalloc *imalloc, size_t len)
162 {
163     if (imalloc)
164         return IMalloc_Alloc(imalloc, len);
165     else
166         return heap_alloc(len);
167 }
168
169 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
170 {
171     if (imalloc)
172         return IMalloc_Realloc(imalloc, mem, len);
173     else
174         return heap_realloc(mem, len);
175 }
176
177 static inline void m_free(IMalloc *imalloc, void *mem)
178 {
179     if (imalloc)
180         IMalloc_Free(imalloc, mem);
181     else
182         heap_free(mem);
183 }
184
185 /* reader memory allocation functions */
186 static inline void *reader_alloc(xmlreader *reader, size_t len)
187 {
188     return m_alloc(reader->imalloc, len);
189 }
190
191 static inline void reader_free(xmlreader *reader, void *mem)
192 {
193     m_free(reader->imalloc, mem);
194 }
195
196 /* reader input memory allocation functions */
197 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
198 {
199     return m_alloc(input->imalloc, len);
200 }
201
202 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
203 {
204     return m_realloc(input->imalloc, mem, len);
205 }
206
207 static inline void readerinput_free(xmlreaderinput *input, void *mem)
208 {
209     m_free(input->imalloc, mem);
210 }
211
212 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
213 {
214     LPWSTR ret = NULL;
215
216     if(str) {
217         DWORD size;
218
219         size = (strlenW(str)+1)*sizeof(WCHAR);
220         ret = readerinput_alloc(input, size);
221         if (ret) memcpy(ret, str, size);
222     }
223
224     return ret;
225 }
226
227 static void reader_clear_attrs(xmlreader *reader)
228 {
229     struct attribute *attr, *attr2;
230     LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
231     {
232         reader_free(reader, attr);
233     }
234     list_init(&reader->attrs);
235     reader->attr_count = 0;
236 }
237
238 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
239    while we are on a node with attributes */
240 static HRESULT reader_add_attr(xmlreader *reader, strval *localname, strval *value)
241 {
242     struct attribute *attr;
243
244     attr = reader_alloc(reader, sizeof(*attr));
245     if (!attr) return E_OUTOFMEMORY;
246
247     attr->localname = *localname;
248     attr->value = *value;
249     list_add_tail(&reader->attrs, &attr->entry);
250     reader->attr_count++;
251
252     return S_OK;
253 }
254
255 static void reader_free_strvalue(xmlreader *reader, XmlReaderStringValue type)
256 {
257     strval *v = &reader->strvalues[type];
258
259     if (v->str != strval_empty.str)
260     {
261         reader_free(reader, v->str);
262         *v = strval_empty;
263     }
264 }
265
266 static void reader_free_strvalues(xmlreader *reader)
267 {
268     int type;
269     for (type = 0; type < StringValue_Last; type++)
270         reader_free_strvalue(reader, type);
271 }
272
273 /* always make a copy, cause strings are supposed to be null terminated */
274 static void reader_set_strvalue(xmlreader *reader, XmlReaderStringValue type, const strval *value)
275 {
276     strval *v = &reader->strvalues[type];
277
278     reader_free_strvalue(reader, type);
279     if (value->str == strval_empty.str)
280         *v = *value;
281     else
282     {
283         v->str = reader_alloc(reader, (value->len + 1)*sizeof(WCHAR));
284         memcpy(v->str, value->str, value->len*sizeof(WCHAR));
285         v->str[value->len] = 0;
286         v->len = value->len;
287     }
288 }
289
290 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
291 {
292     const int initial_len = 0x2000;
293     buffer->data = readerinput_alloc(input, initial_len);
294     if (!buffer->data) return E_OUTOFMEMORY;
295
296     memset(buffer->data, 0, 4);
297     buffer->cur = buffer->data;
298     buffer->allocated = initial_len;
299     buffer->written = 0;
300
301     return S_OK;
302 }
303
304 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
305 {
306     readerinput_free(input, buffer->data);
307 }
308
309 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
310 {
311     if (encoding == XmlEncoding_Unknown)
312     {
313         FIXME("unsupported encoding %d\n", encoding);
314         return E_NOTIMPL;
315     }
316
317     *cp = xml_encoding_map[encoding].cp;
318
319     return S_OK;
320 }
321
322 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
323 {
324     int min, max, n, c;
325
326     if (!name) return XmlEncoding_Unknown;
327
328     min = 0;
329     max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
330
331     while (min <= max)
332     {
333         n = (min+max)/2;
334
335         if (len != -1)
336             c = strncmpiW(xml_encoding_map[n].name, name, len);
337         else
338             c = strcmpiW(xml_encoding_map[n].name, name);
339         if (!c)
340             return xml_encoding_map[n].enc;
341
342         if (c > 0)
343             max = n-1;
344         else
345             min = n+1;
346     }
347
348     return XmlEncoding_Unknown;
349 }
350
351 static HRESULT alloc_input_buffer(xmlreaderinput *input)
352 {
353     input_buffer *buffer;
354     HRESULT hr;
355
356     input->buffer = NULL;
357
358     buffer = readerinput_alloc(input, sizeof(*buffer));
359     if (!buffer) return E_OUTOFMEMORY;
360
361     buffer->input = input;
362     buffer->code_page = ~0; /* code page is unknown at this point */
363     hr = init_encoded_buffer(input, &buffer->utf16);
364     if (hr != S_OK) {
365         readerinput_free(input, buffer);
366         return hr;
367     }
368
369     hr = init_encoded_buffer(input, &buffer->encoded);
370     if (hr != S_OK) {
371         free_encoded_buffer(input, &buffer->utf16);
372         readerinput_free(input, buffer);
373         return hr;
374     }
375
376     input->buffer = buffer;
377     return S_OK;
378 }
379
380 static void free_input_buffer(input_buffer *buffer)
381 {
382     free_encoded_buffer(buffer->input, &buffer->encoded);
383     free_encoded_buffer(buffer->input, &buffer->utf16);
384     readerinput_free(buffer->input, buffer);
385 }
386
387 static void readerinput_release_stream(xmlreaderinput *readerinput)
388 {
389     if (readerinput->stream) {
390         ISequentialStream_Release(readerinput->stream);
391         readerinput->stream = NULL;
392     }
393 }
394
395 /* Queries already stored interface for IStream/ISequentialStream.
396    Interface supplied on creation will be overwritten */
397 static HRESULT readerinput_query_for_stream(xmlreaderinput *readerinput)
398 {
399     HRESULT hr;
400
401     readerinput_release_stream(readerinput);
402     hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
403     if (hr != S_OK)
404         hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
405
406     return hr;
407 }
408
409 /* reads a chunk to raw buffer */
410 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
411 {
412     encoded_buffer *buffer = &readerinput->buffer->encoded;
413     /* to make sure aligned length won't exceed allocated length */
414     ULONG len = buffer->allocated - buffer->written - 4;
415     ULONG read;
416     HRESULT hr;
417
418     /* always try to get aligned to 4 bytes, so the only case we can get partially read characters is
419        variable width encodings like UTF-8 */
420     len = (len + 3) & ~3;
421     /* try to use allocated space or grow */
422     if (buffer->allocated - buffer->written < len)
423     {
424         buffer->allocated *= 2;
425         buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
426         len = buffer->allocated - buffer->written;
427     }
428
429     hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
430     if (FAILED(hr)) return hr;
431     TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
432     buffer->written += read;
433
434     return hr;
435 }
436
437 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
438 static void readerinput_grow(xmlreaderinput *readerinput, int length)
439 {
440     encoded_buffer *buffer = &readerinput->buffer->utf16;
441
442     /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
443     if (buffer->allocated < buffer->written + length + 4)
444     {
445         int grown_size = max(2*buffer->allocated, buffer->allocated + length);
446         buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
447         buffer->allocated = grown_size;
448     }
449 }
450
451 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
452 {
453     encoded_buffer *buffer = &readerinput->buffer->encoded;
454     static char startA[] = {'<','?'};
455     static char commentA[] = {'<','!'};
456     static WCHAR startW[] = {'<','?'};
457     static WCHAR commentW[] = {'<','!'};
458     static char utf8bom[] = {0xef,0xbb,0xbf};
459     static char utf16lebom[] = {0xff,0xfe};
460
461     *enc = XmlEncoding_Unknown;
462
463     if (buffer->written <= 3) return MX_E_INPUTEND;
464
465     /* try start symbols if we have enough data to do that, input buffer should contain
466        first chunk already */
467     if (!memcmp(buffer->data, startA, sizeof(startA)) ||
468         !memcmp(buffer->data, commentA, sizeof(commentA)))
469         *enc = XmlEncoding_UTF8;
470     else if (!memcmp(buffer->data, startW, sizeof(startW)) ||
471              !memcmp(buffer->data, commentW, sizeof(commentW)))
472         *enc = XmlEncoding_UTF16;
473     /* try with BOM now */
474     else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
475     {
476         buffer->cur += sizeof(utf8bom);
477         *enc = XmlEncoding_UTF8;
478     }
479     else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
480     {
481         buffer->cur += sizeof(utf16lebom);
482         *enc = XmlEncoding_UTF16;
483     }
484
485     return S_OK;
486 }
487
488 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
489 {
490     encoded_buffer *buffer = &readerinput->buffer->encoded;
491     int len = buffer->written;
492
493     /* complete single byte char */
494     if (!(buffer->data[len-1] & 0x80)) return len;
495
496     /* find start byte of multibyte char */
497     while (--len && !(buffer->data[len] & 0xc0))
498         ;
499
500     return len;
501 }
502
503 /* Returns byte length of complete char sequence for buffer code page,
504    it's relative to current buffer position which is currently used for BOM handling
505    only. */
506 static int readerinput_get_convlen(xmlreaderinput *readerinput)
507 {
508     encoded_buffer *buffer = &readerinput->buffer->encoded;
509     int len;
510
511     if (readerinput->buffer->code_page == CP_UTF8)
512         len = readerinput_get_utf8_convlen(readerinput);
513     else
514         len = buffer->written;
515
516     TRACE("%d\n", len - (int)(buffer->cur - buffer->data));
517     return len - (buffer->cur - buffer->data);
518 }
519
520 /* It's possible that raw buffer has some leftovers from last conversion - some char
521    sequence that doesn't represent a full code point. Length argument should be calculated with
522    readerinput_get_convlen(), if it's -1 it will be calculated here. */
523 static void readerinput_shrinkraw(xmlreaderinput *readerinput, int len)
524 {
525     encoded_buffer *buffer = &readerinput->buffer->encoded;
526
527     if (len == -1)
528         len = readerinput_get_convlen(readerinput);
529
530     memmove(buffer->data, buffer->cur + (buffer->written - len), len);
531     /* everything below cur is lost too */
532     buffer->written -= len + (buffer->cur - buffer->data);
533     /* after this point we don't need cur pointer really,
534        it's used only to mark where actual data begins when first chunk is read */
535     buffer->cur = buffer->data;
536 }
537
538 /* note that raw buffer content is kept */
539 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
540 {
541     encoded_buffer *src = &readerinput->buffer->encoded;
542     encoded_buffer *dest = &readerinput->buffer->utf16;
543     int len, dest_len;
544     HRESULT hr;
545     WCHAR *ptr;
546     UINT cp;
547
548     hr = get_code_page(enc, &cp);
549     if (FAILED(hr)) return;
550
551     readerinput->buffer->code_page = cp;
552     len = readerinput_get_convlen(readerinput);
553
554     TRACE("switching to cp %d\n", cp);
555
556     /* just copy in this case */
557     if (enc == XmlEncoding_UTF16)
558     {
559         readerinput_grow(readerinput, len);
560         memcpy(dest->data, src->cur, len);
561         dest->written += len*sizeof(WCHAR);
562         return;
563     }
564
565     dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
566     readerinput_grow(readerinput, dest_len);
567     ptr = (WCHAR*)dest->data;
568     MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
569     ptr[dest_len] = 0;
570     dest->written += dest_len*sizeof(WCHAR);
571 }
572
573 /* shrinks parsed data a buffer begins with */
574 static void reader_shrink(xmlreader *reader)
575 {
576     encoded_buffer *buffer = &reader->input->buffer->utf16;
577
578     /* avoid to move too often using threshold shrink length */
579     if (buffer->cur - buffer->data > buffer->written / 2)
580     {
581         buffer->written -= buffer->cur - buffer->data;
582         memmove(buffer->data, buffer->cur, buffer->written);
583         buffer->cur = buffer->data;
584         *(WCHAR*)&buffer->cur[buffer->written] = 0;
585     }
586 }
587
588 /* This is a normal way for reader to get new data converted from raw buffer to utf16 buffer.
589    It won't attempt to shrink but will grow destination buffer if needed */
590 static void reader_more(xmlreader *reader)
591 {
592     xmlreaderinput *readerinput = reader->input;
593     encoded_buffer *src = &readerinput->buffer->encoded;
594     encoded_buffer *dest = &readerinput->buffer->utf16;
595     UINT cp = readerinput->buffer->code_page;
596     int len, dest_len;
597     WCHAR *ptr;
598
599     /* get some raw data from stream first */
600     readerinput_growraw(readerinput);
601     len = readerinput_get_convlen(readerinput);
602
603     /* just copy for UTF-16 case */
604     if (cp == ~0)
605     {
606         readerinput_grow(readerinput, len);
607         memcpy(dest->data, src->cur, len);
608         dest->written += len*sizeof(WCHAR);
609         return;
610     }
611
612     dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
613     readerinput_grow(readerinput, dest_len);
614     ptr = (WCHAR*)dest->data;
615     MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
616     ptr[dest_len] = 0;
617     dest->written += dest_len*sizeof(WCHAR);
618     /* get rid of processed data */
619     readerinput_shrinkraw(readerinput, len);
620 }
621
622 static inline WCHAR *reader_get_cur(xmlreader *reader)
623 {
624     WCHAR *ptr = (WCHAR*)reader->input->buffer->utf16.cur;
625     if (!*ptr) reader_more(reader);
626     return ptr;
627 }
628
629 static int reader_cmp(xmlreader *reader, const WCHAR *str)
630 {
631     const WCHAR *ptr = reader_get_cur(reader);
632     return strncmpW(str, ptr, strlenW(str));
633 }
634
635 /* moves cursor n WCHARs forward */
636 static void reader_skipn(xmlreader *reader, int n)
637 {
638     encoded_buffer *buffer = &reader->input->buffer->utf16;
639     const WCHAR *ptr = reader_get_cur(reader);
640
641     while (*ptr++ && n--)
642     {
643         buffer->cur += sizeof(WCHAR);
644         reader->pos++;
645     }
646 }
647
648 static inline int is_wchar_space(WCHAR ch)
649 {
650     return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
651 }
652
653 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
654 static int reader_skipspaces(xmlreader *reader)
655 {
656     encoded_buffer *buffer = &reader->input->buffer->utf16;
657     const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
658
659     while (is_wchar_space(*ptr))
660     {
661         buffer->cur += sizeof(WCHAR);
662         if (*ptr == '\r')
663             reader->pos = 0;
664         else if (*ptr == '\n')
665         {
666             reader->line++;
667             reader->pos = 0;
668         }
669         else
670             reader->pos++;
671         ptr++;
672     }
673
674     return ptr - start;
675 }
676
677 /* [26] VersionNum ::= '1.' [0-9]+ */
678 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
679 {
680     WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
681     static const WCHAR onedotW[] = {'1','.',0};
682
683     if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
684     /* skip "1." */
685     reader_skipn(reader, 2);
686
687     ptr2 = ptr = reader_get_cur(reader);
688     while (*ptr >= '0' && *ptr <= '9')
689         ptr++;
690
691     if (ptr2 == ptr) return WC_E_DIGIT;
692     TRACE("version=%s\n", debugstr_wn(start, ptr-start));
693     val->str = start;
694     val->len = ptr-start;
695     reader_skipn(reader, ptr-ptr2);
696     return S_OK;
697 }
698
699 /* [25] Eq ::= S? '=' S? */
700 static HRESULT reader_parse_eq(xmlreader *reader)
701 {
702     static const WCHAR eqW[] = {'=',0};
703     reader_skipspaces(reader);
704     if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
705     /* skip '=' */
706     reader_skipn(reader, 1);
707     reader_skipspaces(reader);
708     return S_OK;
709 }
710
711 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
712 static HRESULT reader_parse_versioninfo(xmlreader *reader)
713 {
714     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
715     strval val, name;
716     HRESULT hr;
717
718     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
719
720     if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
721     name.str = reader_get_cur(reader);
722     name.len = 7;
723     /* skip 'version' */
724     reader_skipn(reader, 7);
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     hr = reader_parse_versionnum(reader, &val);
735     if (FAILED(hr)) return hr;
736
737     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
738         return WC_E_QUOTE;
739
740     /* skip "'"|'"' */
741     reader_skipn(reader, 1);
742
743     return reader_add_attr(reader, &name, &val);
744 }
745
746 /* ([A-Za-z0-9._] | '-') */
747 static inline int is_wchar_encname(WCHAR ch)
748 {
749     return ((ch >= 'A' && ch <= 'Z') ||
750             (ch >= 'a' && ch <= 'z') ||
751             (ch >= '0' && ch <= '9') ||
752             (ch == '.') || (ch == '_') ||
753             (ch == '-'));
754 }
755
756 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
757 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
758 {
759     WCHAR *start = reader_get_cur(reader), *ptr;
760     xml_encoding enc;
761     int len;
762
763     if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
764         return WC_E_ENCNAME;
765
766     ptr = start;
767     while (is_wchar_encname(*++ptr))
768         ;
769
770     len = ptr - start;
771     enc = parse_encoding_name(start, len);
772     TRACE("encoding name %s\n", debugstr_wn(start, len));
773     val->str = start;
774     val->len = len;
775
776     if (enc == XmlEncoding_Unknown)
777         return WC_E_ENCNAME;
778
779     /* skip encoding name */
780     reader_skipn(reader, len);
781     return S_OK;
782 }
783
784 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
785 static HRESULT reader_parse_encdecl(xmlreader *reader)
786 {
787     static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
788     strval name, val;
789     HRESULT hr;
790
791     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
792
793     if (reader_cmp(reader, encodingW)) return S_FALSE;
794     name.str = reader_get_cur(reader);
795     name.len = 8;
796     /* skip 'encoding' */
797     reader_skipn(reader, 8);
798
799     hr = reader_parse_eq(reader);
800     if (FAILED(hr)) return hr;
801
802     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
803         return WC_E_QUOTE;
804     /* skip "'"|'"' */
805     reader_skipn(reader, 1);
806
807     hr = reader_parse_encname(reader, &val);
808     if (FAILED(hr)) return hr;
809
810     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
811         return WC_E_QUOTE;
812
813     /* skip "'"|'"' */
814     reader_skipn(reader, 1);
815
816     return reader_add_attr(reader, &name, &val);
817 }
818
819 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
820 static HRESULT reader_parse_sddecl(xmlreader *reader)
821 {
822     static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
823     static const WCHAR yesW[] = {'y','e','s',0};
824     static const WCHAR noW[] = {'n','o',0};
825     WCHAR *start, *ptr;
826     strval name, val;
827     HRESULT hr;
828
829     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
830
831     if (reader_cmp(reader, standaloneW)) return S_FALSE;
832     name.str = reader_get_cur(reader);
833     name.len = 10;
834     /* skip 'standalone' */
835     reader_skipn(reader, 10);
836
837     hr = reader_parse_eq(reader);
838     if (FAILED(hr)) return hr;
839
840     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
841         return WC_E_QUOTE;
842     /* skip "'"|'"' */
843     reader_skipn(reader, 1);
844
845     if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
846         return WC_E_XMLDECL;
847
848     start = reader_get_cur(reader);
849     /* skip 'yes'|'no' */
850     reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
851     ptr = reader_get_cur(reader);
852     TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
853     val.str = start;
854     val.len = ptr-start;
855
856     if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
857         return WC_E_QUOTE;
858     /* skip "'"|'"' */
859     reader_skipn(reader, 1);
860
861     return reader_add_attr(reader, &name, &val);
862 }
863
864 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
865 static HRESULT reader_parse_xmldecl(xmlreader *reader)
866 {
867     static const WCHAR xmldeclW[] = {'<','?','x','m','l',' ',0};
868     static const WCHAR declcloseW[] = {'?','>',0};
869     HRESULT hr;
870
871     /* check if we have "<?xml " */
872     if (reader_cmp(reader, xmldeclW)) return S_FALSE;
873
874     reader_skipn(reader, 5);
875     hr = reader_parse_versioninfo(reader);
876     if (FAILED(hr))
877         return hr;
878
879     hr = reader_parse_encdecl(reader);
880     if (FAILED(hr))
881         return hr;
882
883     hr = reader_parse_sddecl(reader);
884     if (FAILED(hr))
885         return hr;
886
887     reader_skipspaces(reader);
888     if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
889     reader_skipn(reader, 2);
890
891     reader->nodetype = XmlNodeType_XmlDeclaration;
892     reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
893     reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
894     reader_set_strvalue(reader, StringValue_Value, &strval_empty);
895
896     return S_OK;
897 }
898
899 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
900 static HRESULT reader_parse_comment(xmlreader *reader)
901 {
902     WCHAR *start, *ptr;
903
904     /* skip '<!--' */
905     reader_skipn(reader, 4);
906     reader_shrink(reader);
907     ptr = start = reader_get_cur(reader);
908
909     while (*ptr)
910     {
911         if (ptr[0] == '-')
912         {
913             if (ptr[1] == '-')
914             {
915                 if (ptr[2] == '>')
916                 {
917                     strval value = { start, ptr-start };
918
919                     TRACE("%s\n", debugstr_wn(start, ptr-start));
920                     /* skip '-->' */
921                     reader_skipn(reader, 3);
922                     reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
923                     reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
924                     reader_set_strvalue(reader, StringValue_Value, &value);
925                     reader->nodetype = XmlNodeType_Comment;
926                     return S_OK;
927                 }
928                 else
929                     return WC_E_COMMENT;
930             }
931             else
932             {
933                 ptr++;
934                 reader_more(reader);
935             }
936         }
937         else
938         {
939             reader_skipn(reader, 1);
940             ptr = reader_get_cur(reader);
941         }
942     }
943
944     return MX_E_INPUTEND;
945 }
946
947 /* [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] */
948 static inline int is_char(WCHAR ch)
949 {
950     return (ch == '\t') || (ch == '\r') || (ch == '\n') ||
951            (ch >= 0x20 && ch <= 0xd7ff) ||
952            (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
953            (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
954            (ch >= 0xe000 && ch <= 0xfffd);
955 }
956
957 /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
958 static inline int is_pubchar(WCHAR ch)
959 {
960     return (ch == ' ') ||
961            (ch >= 'a' && ch <= 'z') ||
962            (ch >= 'A' && ch <= 'Z') ||
963            (ch >= '0' && ch <= '9') ||
964            (ch >= '-' && ch <= ';') || /* '()*+,-./:; */
965            (ch == '=') || (ch == '?') ||
966            (ch == '@') || (ch == '!') ||
967            (ch >= '#' && ch <= '%') || /* #$% */
968            (ch == '_') || (ch == '\r') || (ch == '\n');
969 }
970
971 static inline int is_namestartchar(WCHAR ch)
972 {
973     return (ch == ':') || (ch >= 'A' && ch <= 'Z') ||
974            (ch == '_') || (ch >= 'a' && ch <= 'z') ||
975            (ch >= 0xc0   && ch <= 0xd6)   ||
976            (ch >= 0xd8   && ch <= 0xf6)   ||
977            (ch >= 0xf8   && ch <= 0x2ff)  ||
978            (ch >= 0x370  && ch <= 0x37d)  ||
979            (ch >= 0x37f  && ch <= 0x1fff) ||
980            (ch >= 0x200c && ch <= 0x200d) ||
981            (ch >= 0x2070 && ch <= 0x218f) ||
982            (ch >= 0x2c00 && ch <= 0x2fef) ||
983            (ch >= 0x3001 && ch <= 0xd7ff) ||
984            (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
985            (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
986            (ch >= 0xf900 && ch <= 0xfdcf) ||
987            (ch >= 0xfdf0 && ch <= 0xfffd);
988 }
989
990 static inline int is_namechar(WCHAR ch)
991 {
992     return (ch == ':') || (ch >= 'A' && ch <= 'Z') ||
993            (ch == '_') || (ch >= 'a' && ch <= 'z') ||
994            (ch == '-') || (ch == '.') ||
995            (ch >= '0'    && ch <= '9')    ||
996            (ch == 0xb7)                   ||
997            (ch >= 0xc0   && ch <= 0xd6)   ||
998            (ch >= 0xd8   && ch <= 0xf6)   ||
999            (ch >= 0xf8   && ch <= 0x2ff)  ||
1000            (ch >= 0x300  && ch <= 0x36f)  ||
1001            (ch >= 0x370  && ch <= 0x37d)  ||
1002            (ch >= 0x37f  && ch <= 0x1fff) ||
1003            (ch >= 0x200c && ch <= 0x200d) ||
1004            (ch >= 0x203f && ch <= 0x2040) ||
1005            (ch >= 0x2070 && ch <= 0x218f) ||
1006            (ch >= 0x2c00 && ch <= 0x2fef) ||
1007            (ch >= 0x3001 && ch <= 0xd7ff) ||
1008            (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
1009            (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
1010            (ch >= 0xf900 && ch <= 0xfdcf) ||
1011            (ch >= 0xfdf0 && ch <= 0xfffd);
1012 }
1013
1014 /* [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
1015                             [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
1016                             [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
1017    [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
1018    [5]  Name     ::= NameStartChar (NameChar)* */
1019 static HRESULT reader_parse_name(xmlreader *reader, strval *name)
1020 {
1021     WCHAR *ptr, *start = reader_get_cur(reader);
1022
1023     ptr = start;
1024     if (!is_namestartchar(*ptr)) return WC_E_NAMECHARACTER;
1025
1026     while (is_namechar(*ptr))
1027     {
1028         reader_skipn(reader, 1);
1029         ptr = reader_get_cur(reader);
1030     }
1031
1032     TRACE("name %s:%d\n", debugstr_wn(start, ptr-start), (int)(ptr-start));
1033     name->str = start;
1034     name->len = ptr-start;
1035
1036     return S_OK;
1037 }
1038
1039 /* [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) */
1040 static HRESULT reader_parse_pitarget(xmlreader *reader, strval *target)
1041 {
1042     static const WCHAR xmlW[] = {'x','m','l'};
1043     strval name;
1044     HRESULT hr;
1045     int i;
1046
1047     hr = reader_parse_name(reader, &name);
1048     if (FAILED(hr)) return WC_E_PI;
1049
1050     /* now that we got name check for illegal content */
1051     if (name.len == 3 && !strncmpiW(name.str, xmlW, 3))
1052         return WC_E_LEADINGXML;
1053
1054     /* PITarget can't be a qualified name */
1055     for (i = 0; i < name.len; i++)
1056         if (name.str[i] == ':')
1057             return i ? NC_E_NAMECOLON : WC_E_PI;
1058
1059     TRACE("pitarget %s:%d\n", debugstr_wn(name.str, name.len), name.len);
1060     *target = name;
1061     return S_OK;
1062 }
1063
1064 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
1065 static HRESULT reader_parse_pi(xmlreader *reader)
1066 {
1067     WCHAR *ptr, *start;
1068     strval target;
1069     HRESULT hr;
1070
1071     /* skip '<?' */
1072     reader_skipn(reader, 2);
1073     reader_shrink(reader);
1074
1075     hr = reader_parse_pitarget(reader, &target);
1076     if (FAILED(hr)) return hr;
1077
1078     ptr = reader_get_cur(reader);
1079     /* exit earlier if there's no content */
1080     if (ptr[0] == '?' && ptr[1] == '>')
1081     {
1082         /* skip '?>' */
1083         reader_skipn(reader, 2);
1084         reader->nodetype = XmlNodeType_ProcessingInstruction;
1085         reader_set_strvalue(reader, StringValue_LocalName, &target);
1086         reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1087         reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1088         return S_OK;
1089     }
1090
1091     /* now at least a single space char should be there */
1092     if (!is_wchar_space(*ptr)) return WC_E_WHITESPACE;
1093     reader_skipspaces(reader);
1094
1095     ptr = start = reader_get_cur(reader);
1096
1097     while (*ptr)
1098     {
1099         if (ptr[0] == '?')
1100         {
1101             if (ptr[1] == '>')
1102             {
1103                 strval value = { start, ptr-start };
1104
1105                 TRACE("%s\n", debugstr_wn(start, ptr-start));
1106                 /* skip '?>' */
1107                 reader_skipn(reader, 2);
1108                 reader->nodetype = XmlNodeType_ProcessingInstruction;
1109                 reader_set_strvalue(reader, StringValue_LocalName, &target);
1110                 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1111                 reader_set_strvalue(reader, StringValue_Value, &value);
1112                 return S_OK;
1113             }
1114             else
1115             {
1116                 ptr++;
1117                 reader_more(reader);
1118             }
1119         }
1120         else
1121         {
1122             reader_skipn(reader, 1);
1123             ptr = reader_get_cur(reader);
1124         }
1125     }
1126
1127     return S_OK;
1128 }
1129
1130 /* This one is used to parse significant whitespace nodes, like in Misc production */
1131 static HRESULT reader_parse_whitespace(xmlreader *reader)
1132 {
1133     WCHAR *start, *ptr;
1134
1135     reader_shrink(reader);
1136     start = reader_get_cur(reader);
1137
1138     reader_skipspaces(reader);
1139     ptr = reader_get_cur(reader);
1140     TRACE("%s\n", debugstr_wn(start, ptr-start));
1141
1142     reader->nodetype = XmlNodeType_Whitespace;
1143     reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1144     reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1145     reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1146     return S_OK;
1147 }
1148
1149 /* [27] Misc ::= Comment | PI | S */
1150 static HRESULT reader_parse_misc(xmlreader *reader)
1151 {
1152     HRESULT hr = S_FALSE;
1153
1154     while (1)
1155     {
1156         static const WCHAR commentW[] = {'<','!','-','-',0};
1157         static const WCHAR piW[] = {'<','?',0};
1158         const WCHAR *cur = reader_get_cur(reader);
1159
1160         if (is_wchar_space(*cur))
1161             hr = reader_parse_whitespace(reader);
1162         else if (!reader_cmp(reader, commentW))
1163             hr = reader_parse_comment(reader);
1164         else if (!reader_cmp(reader, piW))
1165             hr = reader_parse_pi(reader);
1166         else
1167             break;
1168
1169         if (hr != S_FALSE) return hr;
1170     }
1171
1172     return hr;
1173 }
1174
1175 /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
1176 static HRESULT reader_parse_sys_literal(xmlreader *reader, strval *literal)
1177 {
1178     WCHAR *start = reader_get_cur(reader), *cur, quote;
1179
1180     if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1181
1182     quote = *start;
1183     reader_skipn(reader, 1);
1184
1185     cur = start = reader_get_cur(reader);
1186     while (is_char(*cur) && *cur != quote)
1187     {
1188         reader_skipn(reader, 1);
1189         cur = reader_get_cur(reader);
1190     }
1191     if (*cur == quote) reader_skipn(reader, 1);
1192
1193     literal->str = start;
1194     literal->len = cur-start;
1195     TRACE("%s\n", debugstr_wn(start, cur-start));
1196     return S_OK;
1197 }
1198
1199 /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1200    [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1201 static HRESULT reader_parse_pub_literal(xmlreader *reader, strval *literal)
1202 {
1203     WCHAR *start = reader_get_cur(reader), *cur, quote;
1204
1205     if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1206
1207     quote = *start;
1208     reader_skipn(reader, 1);
1209
1210     cur = start;
1211     while (is_pubchar(*cur) && *cur != quote)
1212     {
1213         reader_skipn(reader, 1);
1214         cur = reader_get_cur(reader);
1215     }
1216
1217     literal->str = start;
1218     literal->len = cur-start;
1219     TRACE("%s\n", debugstr_wn(start, cur-start));
1220     return S_OK;
1221 }
1222
1223 /* [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral */
1224 static HRESULT reader_parse_externalid(xmlreader *reader)
1225 {
1226     static WCHAR systemW[] = {'S','Y','S','T','E','M',0};
1227     static WCHAR publicW[] = {'P','U','B','L','I','C',0};
1228     strval name;
1229     HRESULT hr;
1230     int cnt;
1231
1232     if (reader_cmp(reader, systemW))
1233     {
1234         if (reader_cmp(reader, publicW))
1235             return S_FALSE;
1236         else
1237         {
1238             strval pub;
1239
1240             /* public id */
1241             reader_skipn(reader, 6);
1242             cnt = reader_skipspaces(reader);
1243             if (!cnt) return WC_E_WHITESPACE;
1244
1245             hr = reader_parse_pub_literal(reader, &pub);
1246             if (FAILED(hr)) return hr;
1247
1248             name.str = publicW;
1249             name.len = strlenW(publicW);
1250             return reader_add_attr(reader, &name, &pub);
1251         }
1252     }
1253     else
1254     {
1255         strval sys;
1256
1257         /* system id */
1258         reader_skipn(reader, 6);
1259         cnt = reader_skipspaces(reader);
1260         if (!cnt) return WC_E_WHITESPACE;
1261
1262         hr = reader_parse_sys_literal(reader, &sys);
1263         if (FAILED(hr)) return hr;
1264
1265         name.str = systemW;
1266         name.len = strlenW(systemW);
1267         return reader_add_attr(reader, &name, &sys);
1268     }
1269
1270     return hr;
1271 }
1272
1273 /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */
1274 static HRESULT reader_parse_dtd(xmlreader *reader)
1275 {
1276     static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',0};
1277     strval name;
1278     WCHAR *cur;
1279     HRESULT hr;
1280
1281     /* check if we have "<!DOCTYPE" */
1282     if (reader_cmp(reader, doctypeW)) return S_FALSE;
1283     reader_shrink(reader);
1284
1285     /* DTD processing is not allowed by default */
1286     if (reader->dtdmode == DtdProcessing_Prohibit) return WC_E_DTDPROHIBITED;
1287
1288     reader_skipn(reader, 9);
1289     if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
1290
1291     /* name */
1292     hr = reader_parse_name(reader, &name);
1293     if (FAILED(hr)) return WC_E_DECLDOCTYPE;
1294
1295     reader_skipspaces(reader);
1296
1297     hr = reader_parse_externalid(reader);
1298     if (FAILED(hr)) return hr;
1299
1300     reader_skipspaces(reader);
1301
1302     cur = reader_get_cur(reader);
1303     if (*cur != '>')
1304     {
1305         FIXME("internal subset parsing not implemented\n");
1306         return E_NOTIMPL;
1307     }
1308
1309     /* skip '>' */
1310     reader_skipn(reader, 1);
1311
1312     reader->nodetype = XmlNodeType_DocumentType;
1313     reader_set_strvalue(reader, StringValue_LocalName, &name);
1314     reader_set_strvalue(reader, StringValue_QualifiedName, &name);
1315
1316     return S_OK;
1317 }
1318
1319 /* [39] element ::= EmptyElemTag | STag content ETag */
1320 static HRESULT reader_parse_element(xmlreader *reader)
1321 {
1322     FIXME("element parsing not implemented\n");
1323     return E_NOTIMPL;
1324 }
1325
1326 static HRESULT reader_parse_nextnode(xmlreader *reader)
1327 {
1328     HRESULT hr;
1329
1330     while (1)
1331     {
1332         switch (reader->instate)
1333         {
1334         /* if it's a first call for a new input we need to detect stream encoding */
1335         case XmlReadInState_Initial:
1336             {
1337                 xml_encoding enc;
1338
1339                 hr = readerinput_growraw(reader->input);
1340                 if (FAILED(hr)) return hr;
1341
1342                 /* try to detect encoding by BOM or data and set input code page */
1343                 hr = readerinput_detectencoding(reader->input, &enc);
1344                 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
1345                 if (FAILED(hr)) return hr;
1346
1347                 /* always switch first time cause we have to put something in */
1348                 readerinput_switchencoding(reader->input, enc);
1349
1350                 /* parse xml declaration */
1351                 hr = reader_parse_xmldecl(reader);
1352                 if (FAILED(hr)) return hr;
1353
1354                 readerinput_shrinkraw(reader->input, -1);
1355                 reader->instate = XmlReadInState_Misc_DTD;
1356                 if (hr == S_OK) return hr;
1357             }
1358             break;
1359         case XmlReadInState_Misc_DTD:
1360             hr = reader_parse_misc(reader);
1361             if (FAILED(hr)) return hr;
1362
1363             if (hr == S_FALSE)
1364                 reader->instate = XmlReadInState_DTD;
1365             else
1366                 return hr;
1367             break;
1368         case XmlReadInState_DTD:
1369             hr = reader_parse_dtd(reader);
1370             if (FAILED(hr)) return hr;
1371
1372             if (hr == S_OK)
1373             {
1374                 reader->instate = XmlReadInState_DTD_Misc;
1375                 return hr;
1376             }
1377             else
1378                 reader->instate = XmlReadInState_Element;
1379             break;
1380         case XmlReadInState_DTD_Misc:
1381             hr = reader_parse_misc(reader);
1382             if (FAILED(hr)) return hr;
1383
1384             if (hr == S_FALSE)
1385                 reader->instate = XmlReadInState_Element;
1386             else
1387                 return hr;
1388             break;
1389         case XmlReadInState_Element:
1390             hr = reader_parse_element(reader);
1391             if (FAILED(hr)) return hr;
1392             break;
1393         default:
1394             FIXME("internal state %d not handled\n", reader->instate);
1395             return E_NOTIMPL;
1396         }
1397     }
1398
1399     return E_NOTIMPL;
1400 }
1401
1402 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
1403 {
1404     xmlreader *This = impl_from_IXmlReader(iface);
1405
1406     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1407
1408     if (IsEqualGUID(riid, &IID_IUnknown) ||
1409         IsEqualGUID(riid, &IID_IXmlReader))
1410     {
1411         *ppvObject = iface;
1412     }
1413     else
1414     {
1415         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1416         return E_NOINTERFACE;
1417     }
1418
1419     IXmlReader_AddRef(iface);
1420
1421     return S_OK;
1422 }
1423
1424 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
1425 {
1426     xmlreader *This = impl_from_IXmlReader(iface);
1427     ULONG ref = InterlockedIncrement(&This->ref);
1428     TRACE("(%p)->(%d)\n", This, ref);
1429     return ref;
1430 }
1431
1432 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
1433 {
1434     xmlreader *This = impl_from_IXmlReader(iface);
1435     LONG ref = InterlockedDecrement(&This->ref);
1436
1437     TRACE("(%p)->(%d)\n", This, ref);
1438
1439     if (ref == 0)
1440     {
1441         IMalloc *imalloc = This->imalloc;
1442         if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
1443         reader_clear_attrs(This);
1444         reader_free_strvalues(This);
1445         reader_free(This, This);
1446         if (imalloc) IMalloc_Release(imalloc);
1447     }
1448
1449     return ref;
1450 }
1451
1452 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
1453 {
1454     xmlreader *This = impl_from_IXmlReader(iface);
1455     HRESULT hr;
1456
1457     TRACE("(%p)->(%p)\n", This, input);
1458
1459     if (This->input)
1460     {
1461         readerinput_release_stream(This->input);
1462         IUnknown_Release(&This->input->IXmlReaderInput_iface);
1463         This->input = NULL;
1464     }
1465
1466     This->line = This->pos = 0;
1467
1468     /* just reset current input */
1469     if (!input)
1470     {
1471         This->state = XmlReadState_Initial;
1472         return S_OK;
1473     }
1474
1475     /* now try IXmlReaderInput, ISequentialStream, IStream */
1476     hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
1477     if (hr != S_OK)
1478     {
1479         IXmlReaderInput *readerinput;
1480
1481         /* create IXmlReaderInput basing on supplied interface */
1482         hr = CreateXmlReaderInputWithEncodingName(input,
1483                                          NULL, NULL, FALSE, NULL, &readerinput);
1484         if (hr != S_OK) return hr;
1485         This->input = impl_from_IXmlReaderInput(readerinput);
1486     }
1487
1488     /* set stream for supplied IXmlReaderInput */
1489     hr = readerinput_query_for_stream(This->input);
1490     if (hr == S_OK)
1491     {
1492         This->state = XmlReadState_Initial;
1493         This->instate = XmlReadInState_Initial;
1494     }
1495
1496     return hr;
1497 }
1498
1499 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1500 {
1501     xmlreader *This = impl_from_IXmlReader(iface);
1502
1503     TRACE("(%p %u %p)\n", This, property, value);
1504
1505     if (!value) return E_INVALIDARG;
1506
1507     switch (property)
1508     {
1509         case XmlReaderProperty_DtdProcessing:
1510             *value = This->dtdmode;
1511             break;
1512         case XmlReaderProperty_ReadState:
1513             *value = This->state;
1514             break;
1515         default:
1516             FIXME("Unimplemented property (%u)\n", property);
1517             return E_NOTIMPL;
1518     }
1519
1520     return S_OK;
1521 }
1522
1523 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1524 {
1525     xmlreader *This = impl_from_IXmlReader(iface);
1526
1527     TRACE("(%p %u %lu)\n", iface, property, value);
1528
1529     switch (property)
1530     {
1531         case XmlReaderProperty_DtdProcessing:
1532             if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1533             This->dtdmode = value;
1534             break;
1535         default:
1536             FIXME("Unimplemented property (%u)\n", property);
1537             return E_NOTIMPL;
1538     }
1539
1540     return S_OK;
1541 }
1542
1543 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1544 {
1545     xmlreader *This = impl_from_IXmlReader(iface);
1546     XmlNodeType oldtype = This->nodetype;
1547     HRESULT hr;
1548
1549     TRACE("(%p)->(%p)\n", This, nodetype);
1550
1551     if (This->state == XmlReadState_Closed) return S_FALSE;
1552
1553     hr = reader_parse_nextnode(This);
1554     if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
1555         This->state = XmlReadState_Interactive;
1556     if (hr == S_OK) *nodetype = This->nodetype;
1557
1558     return hr;
1559 }
1560
1561 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1562 {
1563     xmlreader *This = impl_from_IXmlReader(iface);
1564     TRACE("(%p)->(%p)\n", This, node_type);
1565
1566     /* When we're on attribute always return attribute type, container node type is kept.
1567        Note that container is not necessarily an element, and attribute doesn't mean it's
1568        an attribute in XML spec terms. */
1569     *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
1570     return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
1571 }
1572
1573 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1574 {
1575     xmlreader *This = impl_from_IXmlReader(iface);
1576
1577     TRACE("(%p)\n", This);
1578
1579     if (!This->attr_count) return S_FALSE;
1580     This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1581     return S_OK;
1582 }
1583
1584 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1585 {
1586     xmlreader *This = impl_from_IXmlReader(iface);
1587     const struct list *next;
1588
1589     TRACE("(%p)\n", This);
1590
1591     if (!This->attr_count) return S_FALSE;
1592
1593     if (!This->attr)
1594         return IXmlReader_MoveToFirstAttribute(iface);
1595
1596     next = list_next(&This->attrs, &This->attr->entry);
1597     if (next)
1598         This->attr = LIST_ENTRY(next, struct attribute, entry);
1599
1600     return next ? S_OK : S_FALSE;
1601 }
1602
1603 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1604                                                       LPCWSTR local_name,
1605                                                       LPCWSTR namespaceUri)
1606 {
1607     FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1608     return E_NOTIMPL;
1609 }
1610
1611 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1612 {
1613     xmlreader *This = impl_from_IXmlReader(iface);
1614
1615     TRACE("(%p)\n", This);
1616
1617     if (!This->attr_count) return S_FALSE;
1618     This->attr = NULL;
1619     return S_OK;
1620 }
1621
1622 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *name, UINT *len)
1623 {
1624     xmlreader *This = impl_from_IXmlReader(iface);
1625
1626     TRACE("(%p)->(%p %p)\n", This, name, len);
1627     *name = This->strvalues[StringValue_QualifiedName].str;
1628     *len  = This->strvalues[StringValue_QualifiedName].len;
1629     return S_OK;
1630 }
1631
1632 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1633                                                 LPCWSTR *namespaceUri,
1634                                                 UINT *namespaceUri_length)
1635 {
1636     FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1637     return E_NOTIMPL;
1638 }
1639
1640 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface, LPCWSTR *name, UINT *len)
1641 {
1642     xmlreader *This = impl_from_IXmlReader(iface);
1643
1644     TRACE("(%p)->(%p %p)\n", This, name, len);
1645     *name = This->strvalues[StringValue_LocalName].str;
1646     *len  = This->strvalues[StringValue_LocalName].len;
1647     return S_OK;
1648 }
1649
1650 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1651                                           LPCWSTR *prefix,
1652                                           UINT *prefix_length)
1653 {
1654     FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1655     return E_NOTIMPL;
1656 }
1657
1658 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, LPCWSTR *value, UINT *len)
1659 {
1660     xmlreader *This = impl_from_IXmlReader(iface);
1661
1662     TRACE("(%p)->(%p %p)\n", This, value, len);
1663     *value = This->strvalues[StringValue_Value].str;
1664     if (len) *len = This->strvalues[StringValue_Value].len;
1665     return S_OK;
1666 }
1667
1668 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1669                                                WCHAR *buffer,
1670                                                UINT   chunk_size,
1671                                                UINT  *read)
1672 {
1673     FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1674     return E_NOTIMPL;
1675 }
1676
1677 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1678                                            LPCWSTR *baseUri,
1679                                            UINT *baseUri_length)
1680 {
1681     FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1682     return E_NOTIMPL;
1683 }
1684
1685 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1686 {
1687     FIXME("(%p): stub\n", iface);
1688     return E_NOTIMPL;
1689 }
1690
1691 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1692 {
1693     FIXME("(%p): stub\n", iface);
1694     return E_NOTIMPL;
1695 }
1696
1697 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1698 {
1699     xmlreader *This = impl_from_IXmlReader(iface);
1700
1701     TRACE("(%p %p)\n", This, lineNumber);
1702
1703     if (!lineNumber) return E_INVALIDARG;
1704
1705     *lineNumber = This->line;
1706
1707     return S_OK;
1708 }
1709
1710 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1711 {
1712     xmlreader *This = impl_from_IXmlReader(iface);
1713
1714     TRACE("(%p %p)\n", This, linePosition);
1715
1716     if (!linePosition) return E_INVALIDARG;
1717
1718     *linePosition = This->pos;
1719
1720     return S_OK;
1721 }
1722
1723 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1724 {
1725     xmlreader *This = impl_from_IXmlReader(iface);
1726
1727     TRACE("(%p)->(%p)\n", This, count);
1728
1729     if (!count) return E_INVALIDARG;
1730
1731     *count = This->attr_count;
1732     return S_OK;
1733 }
1734
1735 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1736 {
1737     FIXME("(%p %p): stub\n", iface, depth);
1738     return E_NOTIMPL;
1739 }
1740
1741 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1742 {
1743     FIXME("(%p): stub\n", iface);
1744     return E_NOTIMPL;
1745 }
1746
1747 static const struct IXmlReaderVtbl xmlreader_vtbl =
1748 {
1749     xmlreader_QueryInterface,
1750     xmlreader_AddRef,
1751     xmlreader_Release,
1752     xmlreader_SetInput,
1753     xmlreader_GetProperty,
1754     xmlreader_SetProperty,
1755     xmlreader_Read,
1756     xmlreader_GetNodeType,
1757     xmlreader_MoveToFirstAttribute,
1758     xmlreader_MoveToNextAttribute,
1759     xmlreader_MoveToAttributeByName,
1760     xmlreader_MoveToElement,
1761     xmlreader_GetQualifiedName,
1762     xmlreader_GetNamespaceUri,
1763     xmlreader_GetLocalName,
1764     xmlreader_GetPrefix,
1765     xmlreader_GetValue,
1766     xmlreader_ReadValueChunk,
1767     xmlreader_GetBaseUri,
1768     xmlreader_IsDefault,
1769     xmlreader_IsEmptyElement,
1770     xmlreader_GetLineNumber,
1771     xmlreader_GetLinePosition,
1772     xmlreader_GetAttributeCount,
1773     xmlreader_GetDepth,
1774     xmlreader_IsEOF
1775 };
1776
1777 /** IXmlReaderInput **/
1778 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1779 {
1780     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1781
1782     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1783
1784     if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1785         IsEqualGUID(riid, &IID_IUnknown))
1786     {
1787         *ppvObject = iface;
1788     }
1789     else
1790     {
1791         WARN("interface %s not implemented\n", debugstr_guid(riid));
1792         return E_NOINTERFACE;
1793     }
1794
1795     IUnknown_AddRef(iface);
1796
1797     return S_OK;
1798 }
1799
1800 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1801 {
1802     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1803     ULONG ref = InterlockedIncrement(&This->ref);
1804     TRACE("(%p)->(%d)\n", This, ref);
1805     return ref;
1806 }
1807
1808 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1809 {
1810     xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1811     LONG ref = InterlockedDecrement(&This->ref);
1812
1813     TRACE("(%p)->(%d)\n", This, ref);
1814
1815     if (ref == 0)
1816     {
1817         IMalloc *imalloc = This->imalloc;
1818         if (This->input) IUnknown_Release(This->input);
1819         if (This->stream) ISequentialStream_Release(This->stream);
1820         if (This->buffer) free_input_buffer(This->buffer);
1821         readerinput_free(This, This->baseuri);
1822         readerinput_free(This, This);
1823         if (imalloc) IMalloc_Release(imalloc);
1824     }
1825
1826     return ref;
1827 }
1828
1829 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1830 {
1831     xmlreaderinput_QueryInterface,
1832     xmlreaderinput_AddRef,
1833     xmlreaderinput_Release
1834 };
1835
1836 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1837 {
1838     xmlreader *reader;
1839     int i;
1840
1841     TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1842
1843     if (!IsEqualGUID(riid, &IID_IXmlReader))
1844     {
1845         ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1846         return E_FAIL;
1847     }
1848
1849     if (imalloc)
1850         reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1851     else
1852         reader = heap_alloc(sizeof(*reader));
1853     if(!reader) return E_OUTOFMEMORY;
1854
1855     reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1856     reader->ref = 1;
1857     reader->input = NULL;
1858     reader->state = XmlReadState_Closed;
1859     reader->instate = XmlReadInState_Initial;
1860     reader->dtdmode = DtdProcessing_Prohibit;
1861     reader->line  = reader->pos = 0;
1862     reader->imalloc = imalloc;
1863     if (imalloc) IMalloc_AddRef(imalloc);
1864     reader->nodetype = XmlNodeType_None;
1865     list_init(&reader->attrs);
1866     reader->attr_count = 0;
1867     reader->attr = NULL;
1868
1869     for (i = 0; i < StringValue_Last; i++)
1870         reader->strvalues[i] = strval_empty;
1871
1872     *obj = &reader->IXmlReader_iface;
1873
1874     TRACE("returning iface %p\n", *obj);
1875
1876     return S_OK;
1877 }
1878
1879 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1880                                                     IMalloc *imalloc,
1881                                                     LPCWSTR encoding,
1882                                                     BOOL hint,
1883                                                     LPCWSTR base_uri,
1884                                                     IXmlReaderInput **ppInput)
1885 {
1886     xmlreaderinput *readerinput;
1887     HRESULT hr;
1888
1889     TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1890                                        hint, wine_dbgstr_w(base_uri), ppInput);
1891
1892     if (!stream || !ppInput) return E_INVALIDARG;
1893
1894     if (imalloc)
1895         readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1896     else
1897         readerinput = heap_alloc(sizeof(*readerinput));
1898     if(!readerinput) return E_OUTOFMEMORY;
1899
1900     readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinput_vtbl;
1901     readerinput->ref = 1;
1902     readerinput->imalloc = imalloc;
1903     readerinput->stream = NULL;
1904     if (imalloc) IMalloc_AddRef(imalloc);
1905     readerinput->encoding = parse_encoding_name(encoding, -1);
1906     readerinput->hint = hint;
1907     readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
1908
1909     hr = alloc_input_buffer(readerinput);
1910     if (hr != S_OK)
1911     {
1912         readerinput_free(readerinput, readerinput);
1913         return hr;
1914     }
1915     IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1916
1917     *ppInput = &readerinput->IXmlReaderInput_iface;
1918
1919     TRACE("returning iface %p\n", *ppInput);
1920
1921     return S_OK;
1922 }