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