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