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