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