Release 1.6-rc2.
[wine/testsucceed.git] / dlls / xmllite / reader.c
blob5256c35a2d50f71d8ee5fce7779a9190710866cd
1 /*
2 * IXmlReader implementation
4 * Copyright 2010, 2012-2013 Nikolay Sivov
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.
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.
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
21 #define COBJMACROS
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"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlReaderInput, 0x0b3ccc9b, 0x9214, 0x428b, 0xa2, 0xae, 0xef, 0x3a, 0xa8, 0x71, 0xaf, 0xda);
40 typedef enum
42 XmlEncoding_UTF16,
43 XmlEncoding_UTF8,
44 XmlEncoding_Unknown
45 } xml_encoding;
47 typedef enum
49 XmlReadInState_Initial,
50 XmlReadInState_XmlDecl,
51 XmlReadInState_Misc_DTD,
52 XmlReadInState_DTD,
53 XmlReadInState_DTD_Misc,
54 XmlReadInState_Element,
55 XmlReadInState_Content,
56 XmlReadInState_MiscEnd
57 } XmlReaderInternalState;
59 /* This state denotes where parsing was interrupted by input problem.
60 Reader resumes parsing using this information. */
61 typedef enum
63 XmlReadResumeState_Initial,
64 XmlReadResumeState_PITarget,
65 XmlReadResumeState_PIBody,
66 XmlReadResumeState_CDATA,
67 XmlReadResumeState_Comment,
68 XmlReadResumeState_STag
69 } XmlReaderResumeState;
71 /* saved pointer index to resume from particular input position */
72 typedef enum
74 XmlReadResume_Name, /* PITarget, name for NCName, prefix for QName */
75 XmlReadResume_Local, /* local for QName */
76 XmlReadResume_Body, /* PI body, comment text, CDATA text */
77 XmlReadResume_Last
78 } XmlReaderResume;
80 typedef enum
82 StringValue_LocalName,
83 StringValue_QualifiedName,
84 StringValue_Value,
85 StringValue_Last
86 } XmlReaderStringValue;
88 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
89 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
91 static const WCHAR dblquoteW[] = {'\"',0};
92 static const WCHAR quoteW[] = {'\'',0};
93 static const WCHAR ltW[] = {'<',0};
94 static const WCHAR gtW[] = {'>',0};
95 static const WCHAR commentW[] = {'<','!','-','-',0};
96 static const WCHAR piW[] = {'<','?',0};
98 struct xml_encoding_data
100 const WCHAR *name;
101 xml_encoding enc;
102 UINT cp;
105 static const struct xml_encoding_data xml_encoding_map[] = {
106 { utf16W, XmlEncoding_UTF16, ~0 },
107 { utf8W, XmlEncoding_UTF8, CP_UTF8 }
110 typedef struct
112 char *data;
113 char *cur;
114 unsigned int allocated;
115 unsigned int written;
116 } encoded_buffer;
118 typedef struct input_buffer input_buffer;
120 typedef struct
122 IXmlReaderInput IXmlReaderInput_iface;
123 LONG ref;
124 /* reference passed on IXmlReaderInput creation, is kept when input is created */
125 IUnknown *input;
126 IMalloc *imalloc;
127 xml_encoding encoding;
128 BOOL hint;
129 WCHAR *baseuri;
130 /* stream reference set after SetInput() call from reader,
131 stored as sequential stream, cause currently
132 optimizations possible with IStream aren't implemented */
133 ISequentialStream *stream;
134 input_buffer *buffer;
135 unsigned int pending : 1;
136 } xmlreaderinput;
138 static const struct IUnknownVtbl xmlreaderinputvtbl;
140 /* Structure to hold parsed string of specific length.
142 Reader stores node value as 'start' pointer, on request
143 a null-terminated version of it is allocated.
145 To init a strval variable use reader_init_strval(),
146 to set strval as a reader value use reader_set_strval().
148 typedef struct
150 WCHAR *start; /* input position where value starts */
151 UINT len; /* length in WCHARs, altered after ReadValueChunk */
152 WCHAR *str; /* allocated null-terminated string */
153 } strval;
155 static WCHAR emptyW[] = {0};
156 static const strval strval_empty = {emptyW, 0, emptyW};
158 struct attribute
160 struct list entry;
161 strval localname;
162 strval value;
165 struct element
167 struct list entry;
168 strval qname;
171 typedef struct
173 IXmlReader IXmlReader_iface;
174 LONG ref;
175 xmlreaderinput *input;
176 IMalloc *imalloc;
177 XmlReadState state;
178 XmlReaderInternalState instate;
179 XmlReaderResumeState resumestate;
180 XmlNodeType nodetype;
181 DtdProcessing dtdmode;
182 UINT line, pos; /* reader position in XML stream */
183 struct list attrs; /* attributes list for current node */
184 struct attribute *attr; /* current attribute */
185 UINT attr_count;
186 struct list elements;
187 strval strvalues[StringValue_Last];
188 UINT depth;
189 WCHAR *resume[XmlReadResume_Last]; /* pointers used to resume reader */
190 } xmlreader;
192 struct input_buffer
194 encoded_buffer utf16;
195 encoded_buffer encoded;
196 UINT code_page;
197 xmlreaderinput *input;
200 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
202 return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
205 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
207 return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
210 static inline void *m_alloc(IMalloc *imalloc, size_t len)
212 if (imalloc)
213 return IMalloc_Alloc(imalloc, len);
214 else
215 return heap_alloc(len);
218 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
220 if (imalloc)
221 return IMalloc_Realloc(imalloc, mem, len);
222 else
223 return heap_realloc(mem, len);
226 static inline void m_free(IMalloc *imalloc, void *mem)
228 if (imalloc)
229 IMalloc_Free(imalloc, mem);
230 else
231 heap_free(mem);
234 /* reader memory allocation functions */
235 static inline void *reader_alloc(xmlreader *reader, size_t len)
237 return m_alloc(reader->imalloc, len);
240 static inline void reader_free(xmlreader *reader, void *mem)
242 m_free(reader->imalloc, mem);
245 static HRESULT reader_strvaldup(xmlreader *reader, const strval *src, strval *dest)
247 *dest = *src;
249 if (src->str != strval_empty.str)
251 dest->str = reader_alloc(reader, (dest->len+1)*sizeof(WCHAR));
252 if (!dest->str) return E_OUTOFMEMORY;
253 memcpy(dest->str, src->str, dest->len*sizeof(WCHAR));
254 dest->str[dest->len] = 0;
257 return S_OK;
260 /* reader input memory allocation functions */
261 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
263 return m_alloc(input->imalloc, len);
266 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
268 return m_realloc(input->imalloc, mem, len);
271 static inline void readerinput_free(xmlreaderinput *input, void *mem)
273 m_free(input->imalloc, mem);
276 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
278 LPWSTR ret = NULL;
280 if(str) {
281 DWORD size;
283 size = (strlenW(str)+1)*sizeof(WCHAR);
284 ret = readerinput_alloc(input, size);
285 if (ret) memcpy(ret, str, size);
288 return ret;
291 static void reader_clear_attrs(xmlreader *reader)
293 struct attribute *attr, *attr2;
294 LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
296 reader_free(reader, attr);
298 list_init(&reader->attrs);
299 reader->attr_count = 0;
302 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
303 while we are on a node with attributes */
304 static HRESULT reader_add_attr(xmlreader *reader, strval *localname, strval *value)
306 struct attribute *attr;
308 attr = reader_alloc(reader, sizeof(*attr));
309 if (!attr) return E_OUTOFMEMORY;
311 attr->localname = *localname;
312 attr->value = *value;
313 list_add_tail(&reader->attrs, &attr->entry);
314 reader->attr_count++;
316 return S_OK;
319 /* This one frees stored string value if needed */
320 static void reader_free_strvalued(xmlreader *reader, strval *v)
322 if (v->str != strval_empty.str)
324 reader_free(reader, v->str);
325 *v = strval_empty;
329 static inline void reader_init_strvalue(WCHAR *str, UINT len, strval *v)
331 v->start = v->str = str;
332 v->len = len;
335 static void reader_free_strvalue(xmlreader *reader, XmlReaderStringValue type)
337 reader_free_strvalued(reader, &reader->strvalues[type]);
340 static void reader_free_strvalues(xmlreader *reader)
342 int type;
343 for (type = 0; type < StringValue_Last; type++)
344 reader_free_strvalue(reader, type);
347 /* This helper should only be used to test if strings are the same,
348 it doesn't try to sort. */
349 static inline int strval_eq(const strval *str1, const strval *str2)
351 if (str1->len != str2->len) return 0;
352 return !memcmp(str1->str, str2->str, str1->len*sizeof(WCHAR));
355 static void reader_clear_elements(xmlreader *reader)
357 struct element *elem, *elem2;
358 LIST_FOR_EACH_ENTRY_SAFE(elem, elem2, &reader->elements, struct element, entry)
360 reader_free_strvalued(reader, &elem->qname);
361 reader_free(reader, elem);
363 list_init(&reader->elements);
366 static HRESULT reader_inc_depth(xmlreader *reader)
368 /* FIXME: handle XmlReaderProperty_MaxElementDepth property */
369 reader->depth++;
370 return S_OK;
373 static HRESULT reader_push_element(xmlreader *reader, strval *qname)
375 struct element *elem;
376 HRESULT hr;
378 elem = reader_alloc(reader, sizeof(*elem));
379 if (!elem) return E_OUTOFMEMORY;
381 hr = reader_strvaldup(reader, qname, &elem->qname);
382 if (FAILED(hr)) {
383 reader_free(reader, elem);
384 return hr;
387 if (!list_empty(&reader->elements))
389 hr = reader_inc_depth(reader);
390 if (FAILED(hr)) {
391 reader_free(reader, elem);
392 return hr;
396 list_add_head(&reader->elements, &elem->entry);
397 return hr;
400 static void reader_pop_element(xmlreader *reader)
402 struct element *elem = LIST_ENTRY(list_head(&reader->elements), struct element, entry);
404 if (elem)
406 list_remove(&elem->entry);
407 reader_free_strvalued(reader, &elem->qname);
408 reader_free(reader, elem);
412 /* Always make a copy, cause strings are supposed to be null terminated. Null pointer for 'value'
413 means node value is to be determined. */
414 static void reader_set_strvalue(xmlreader *reader, XmlReaderStringValue type, const strval *value)
416 strval *v = &reader->strvalues[type];
418 reader_free_strvalue(reader, type);
419 if (!value)
421 v->str = NULL;
422 v->start = NULL;
423 v->len = 0;
424 return;
427 if (value->str == strval_empty.str)
428 *v = *value;
429 else
431 if (type == StringValue_Value)
433 /* defer allocation for value string */
434 v->str = NULL;
435 v->start = value->start;
436 v->len = value->len;
438 else
440 v->str = reader_alloc(reader, (value->len + 1)*sizeof(WCHAR));
441 memcpy(v->str, value->start, value->len*sizeof(WCHAR));
442 v->str[value->len] = 0;
443 v->len = value->len;
448 static inline int is_reader_pending(xmlreader *reader)
450 return reader->input->pending;
453 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
455 const int initial_len = 0x2000;
456 buffer->data = readerinput_alloc(input, initial_len);
457 if (!buffer->data) return E_OUTOFMEMORY;
459 memset(buffer->data, 0, 4);
460 buffer->cur = buffer->data;
461 buffer->allocated = initial_len;
462 buffer->written = 0;
464 return S_OK;
467 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
469 readerinput_free(input, buffer->data);
472 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
474 if (encoding == XmlEncoding_Unknown)
476 FIXME("unsupported encoding %d\n", encoding);
477 return E_NOTIMPL;
480 *cp = xml_encoding_map[encoding].cp;
482 return S_OK;
485 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
487 int min, max, n, c;
489 if (!name) return XmlEncoding_Unknown;
491 min = 0;
492 max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
494 while (min <= max)
496 n = (min+max)/2;
498 if (len != -1)
499 c = strncmpiW(xml_encoding_map[n].name, name, len);
500 else
501 c = strcmpiW(xml_encoding_map[n].name, name);
502 if (!c)
503 return xml_encoding_map[n].enc;
505 if (c > 0)
506 max = n-1;
507 else
508 min = n+1;
511 return XmlEncoding_Unknown;
514 static HRESULT alloc_input_buffer(xmlreaderinput *input)
516 input_buffer *buffer;
517 HRESULT hr;
519 input->buffer = NULL;
521 buffer = readerinput_alloc(input, sizeof(*buffer));
522 if (!buffer) return E_OUTOFMEMORY;
524 buffer->input = input;
525 buffer->code_page = ~0; /* code page is unknown at this point */
526 hr = init_encoded_buffer(input, &buffer->utf16);
527 if (hr != S_OK) {
528 readerinput_free(input, buffer);
529 return hr;
532 hr = init_encoded_buffer(input, &buffer->encoded);
533 if (hr != S_OK) {
534 free_encoded_buffer(input, &buffer->utf16);
535 readerinput_free(input, buffer);
536 return hr;
539 input->buffer = buffer;
540 return S_OK;
543 static void free_input_buffer(input_buffer *buffer)
545 free_encoded_buffer(buffer->input, &buffer->encoded);
546 free_encoded_buffer(buffer->input, &buffer->utf16);
547 readerinput_free(buffer->input, buffer);
550 static void readerinput_release_stream(xmlreaderinput *readerinput)
552 if (readerinput->stream) {
553 ISequentialStream_Release(readerinput->stream);
554 readerinput->stream = NULL;
558 /* Queries already stored interface for IStream/ISequentialStream.
559 Interface supplied on creation will be overwritten */
560 static HRESULT readerinput_query_for_stream(xmlreaderinput *readerinput)
562 HRESULT hr;
564 readerinput_release_stream(readerinput);
565 hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
566 if (hr != S_OK)
567 hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
569 return hr;
572 /* reads a chunk to raw buffer */
573 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
575 encoded_buffer *buffer = &readerinput->buffer->encoded;
576 /* to make sure aligned length won't exceed allocated length */
577 ULONG len = buffer->allocated - buffer->written - 4;
578 ULONG read;
579 HRESULT hr;
581 /* always try to get aligned to 4 bytes, so the only case we can get partially read characters is
582 variable width encodings like UTF-8 */
583 len = (len + 3) & ~3;
584 /* try to use allocated space or grow */
585 if (buffer->allocated - buffer->written < len)
587 buffer->allocated *= 2;
588 buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
589 len = buffer->allocated - buffer->written;
592 read = 0;
593 hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
594 TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
595 readerinput->pending = hr == E_PENDING;
596 if (FAILED(hr)) return hr;
597 buffer->written += read;
599 return hr;
602 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
603 static void readerinput_grow(xmlreaderinput *readerinput, int length)
605 encoded_buffer *buffer = &readerinput->buffer->utf16;
607 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
608 if (buffer->allocated < buffer->written + length + 4)
610 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
611 buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
612 buffer->allocated = grown_size;
616 static inline int readerinput_is_utf8(xmlreaderinput *readerinput)
618 static char startA[] = {'<','?'};
619 static char commentA[] = {'<','!'};
620 encoded_buffer *buffer = &readerinput->buffer->encoded;
621 unsigned char *ptr = (unsigned char*)buffer->data;
623 return !memcmp(buffer->data, startA, sizeof(startA)) ||
624 !memcmp(buffer->data, commentA, sizeof(commentA)) ||
625 /* test start byte */
626 (ptr[0] == '<' &&
628 (ptr[1] && (ptr[1] <= 0x7f)) ||
629 (buffer->data[1] >> 5) == 0x6 || /* 2 bytes */
630 (buffer->data[1] >> 4) == 0xe || /* 3 bytes */
631 (buffer->data[1] >> 3) == 0x1e) /* 4 bytes */
635 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
637 encoded_buffer *buffer = &readerinput->buffer->encoded;
638 static WCHAR startW[] = {'<','?'};
639 static WCHAR commentW[] = {'<','!'};
640 static char utf8bom[] = {0xef,0xbb,0xbf};
641 static char utf16lebom[] = {0xff,0xfe};
643 *enc = XmlEncoding_Unknown;
645 if (buffer->written <= 3)
647 HRESULT hr = readerinput_growraw(readerinput);
648 if (FAILED(hr)) return hr;
649 if (buffer->written <= 3) return MX_E_INPUTEND;
652 /* try start symbols if we have enough data to do that, input buffer should contain
653 first chunk already */
654 if (readerinput_is_utf8(readerinput))
655 *enc = XmlEncoding_UTF8;
656 else if (!memcmp(buffer->data, startW, sizeof(startW)) ||
657 !memcmp(buffer->data, commentW, sizeof(commentW)))
658 *enc = XmlEncoding_UTF16;
659 /* try with BOM now */
660 else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
662 buffer->cur += sizeof(utf8bom);
663 *enc = XmlEncoding_UTF8;
665 else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
667 buffer->cur += sizeof(utf16lebom);
668 *enc = XmlEncoding_UTF16;
671 return S_OK;
674 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
676 encoded_buffer *buffer = &readerinput->buffer->encoded;
677 int len = buffer->written;
679 /* complete single byte char */
680 if (!(buffer->data[len-1] & 0x80)) return len;
682 /* find start byte of multibyte char */
683 while (--len && !(buffer->data[len] & 0xc0))
686 return len;
689 /* Returns byte length of complete char sequence for buffer code page,
690 it's relative to current buffer position which is currently used for BOM handling
691 only. */
692 static int readerinput_get_convlen(xmlreaderinput *readerinput)
694 encoded_buffer *buffer = &readerinput->buffer->encoded;
695 int len;
697 if (readerinput->buffer->code_page == CP_UTF8)
698 len = readerinput_get_utf8_convlen(readerinput);
699 else
700 len = buffer->written;
702 TRACE("%d\n", len - (int)(buffer->cur - buffer->data));
703 return len - (buffer->cur - buffer->data);
706 /* It's possible that raw buffer has some leftovers from last conversion - some char
707 sequence that doesn't represent a full code point. Length argument should be calculated with
708 readerinput_get_convlen(), if it's -1 it will be calculated here. */
709 static void readerinput_shrinkraw(xmlreaderinput *readerinput, int len)
711 encoded_buffer *buffer = &readerinput->buffer->encoded;
713 if (len == -1)
714 len = readerinput_get_convlen(readerinput);
716 memmove(buffer->data, buffer->cur + (buffer->written - len), len);
717 /* everything below cur is lost too */
718 buffer->written -= len + (buffer->cur - buffer->data);
719 /* after this point we don't need cur pointer really,
720 it's used only to mark where actual data begins when first chunk is read */
721 buffer->cur = buffer->data;
724 /* note that raw buffer content is kept */
725 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
727 encoded_buffer *src = &readerinput->buffer->encoded;
728 encoded_buffer *dest = &readerinput->buffer->utf16;
729 int len, dest_len;
730 HRESULT hr;
731 WCHAR *ptr;
732 UINT cp;
734 hr = get_code_page(enc, &cp);
735 if (FAILED(hr)) return;
737 readerinput->buffer->code_page = cp;
738 len = readerinput_get_convlen(readerinput);
740 TRACE("switching to cp %d\n", cp);
742 /* just copy in this case */
743 if (enc == XmlEncoding_UTF16)
745 readerinput_grow(readerinput, len);
746 memcpy(dest->data, src->cur, len);
747 dest->written += len*sizeof(WCHAR);
748 return;
751 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
752 readerinput_grow(readerinput, dest_len);
753 ptr = (WCHAR*)dest->data;
754 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
755 ptr[dest_len] = 0;
756 dest->written += dest_len*sizeof(WCHAR);
759 /* shrinks parsed data a buffer begins with */
760 static void reader_shrink(xmlreader *reader)
762 encoded_buffer *buffer = &reader->input->buffer->utf16;
764 /* avoid to move too often using threshold shrink length */
765 if (buffer->cur - buffer->data > buffer->written / 2)
767 buffer->written -= buffer->cur - buffer->data;
768 memmove(buffer->data, buffer->cur, buffer->written);
769 buffer->cur = buffer->data;
770 *(WCHAR*)&buffer->cur[buffer->written] = 0;
774 /* This is a normal way for reader to get new data converted from raw buffer to utf16 buffer.
775 It won't attempt to shrink but will grow destination buffer if needed */
776 static HRESULT reader_more(xmlreader *reader)
778 xmlreaderinput *readerinput = reader->input;
779 encoded_buffer *src = &readerinput->buffer->encoded;
780 encoded_buffer *dest = &readerinput->buffer->utf16;
781 UINT cp = readerinput->buffer->code_page;
782 int len, dest_len;
783 HRESULT hr;
784 WCHAR *ptr;
786 /* get some raw data from stream first */
787 hr = readerinput_growraw(readerinput);
788 len = readerinput_get_convlen(readerinput);
790 /* just copy for UTF-16 case */
791 if (cp == ~0)
793 readerinput_grow(readerinput, len);
794 memcpy(dest->data, src->cur, len);
795 dest->written += len*sizeof(WCHAR);
796 return hr;
799 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
800 readerinput_grow(readerinput, dest_len);
801 ptr = (WCHAR*)dest->data;
802 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
803 ptr[dest_len] = 0;
804 dest->written += dest_len*sizeof(WCHAR);
805 /* get rid of processed data */
806 readerinput_shrinkraw(readerinput, len);
808 return hr;
811 static inline WCHAR *reader_get_cur(xmlreader *reader)
813 WCHAR *ptr = (WCHAR*)reader->input->buffer->utf16.cur;
814 if (!*ptr) reader_more(reader);
815 return ptr;
818 static int reader_cmp(xmlreader *reader, const WCHAR *str)
820 const WCHAR *ptr = reader_get_cur(reader);
821 return strncmpW(str, ptr, strlenW(str));
824 /* moves cursor n WCHARs forward */
825 static void reader_skipn(xmlreader *reader, int n)
827 encoded_buffer *buffer = &reader->input->buffer->utf16;
828 const WCHAR *ptr = reader_get_cur(reader);
830 while (*ptr++ && n--)
832 buffer->cur += sizeof(WCHAR);
833 reader->pos++;
837 static inline int is_wchar_space(WCHAR ch)
839 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
842 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
843 static int reader_skipspaces(xmlreader *reader)
845 encoded_buffer *buffer = &reader->input->buffer->utf16;
846 const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
848 while (is_wchar_space(*ptr))
850 buffer->cur += sizeof(WCHAR);
851 if (*ptr == '\r')
852 reader->pos = 0;
853 else if (*ptr == '\n')
855 reader->line++;
856 reader->pos = 0;
858 else
859 reader->pos++;
860 ptr++;
863 return ptr - start;
866 /* [26] VersionNum ::= '1.' [0-9]+ */
867 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
869 WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
870 static const WCHAR onedotW[] = {'1','.',0};
872 if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
873 /* skip "1." */
874 reader_skipn(reader, 2);
876 ptr2 = ptr = reader_get_cur(reader);
877 while (*ptr >= '0' && *ptr <= '9')
878 ptr++;
880 if (ptr2 == ptr) return WC_E_DIGIT;
881 TRACE("version=%s\n", debugstr_wn(start, ptr-start));
882 reader_init_strvalue(start, ptr-start, val);
883 reader_skipn(reader, ptr-ptr2);
884 return S_OK;
887 /* [25] Eq ::= S? '=' S? */
888 static HRESULT reader_parse_eq(xmlreader *reader)
890 static const WCHAR eqW[] = {'=',0};
891 reader_skipspaces(reader);
892 if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
893 /* skip '=' */
894 reader_skipn(reader, 1);
895 reader_skipspaces(reader);
896 return S_OK;
899 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
900 static HRESULT reader_parse_versioninfo(xmlreader *reader)
902 static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
903 strval val, name;
904 HRESULT hr;
906 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
908 if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
909 reader_init_strvalue(reader_get_cur(reader), 7, &name);
910 /* skip 'version' */
911 reader_skipn(reader, 7);
913 hr = reader_parse_eq(reader);
914 if (FAILED(hr)) return hr;
916 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
917 return WC_E_QUOTE;
918 /* skip "'"|'"' */
919 reader_skipn(reader, 1);
921 hr = reader_parse_versionnum(reader, &val);
922 if (FAILED(hr)) return hr;
924 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
925 return WC_E_QUOTE;
927 /* skip "'"|'"' */
928 reader_skipn(reader, 1);
930 return reader_add_attr(reader, &name, &val);
933 /* ([A-Za-z0-9._] | '-') */
934 static inline int is_wchar_encname(WCHAR ch)
936 return ((ch >= 'A' && ch <= 'Z') ||
937 (ch >= 'a' && ch <= 'z') ||
938 (ch >= '0' && ch <= '9') ||
939 (ch == '.') || (ch == '_') ||
940 (ch == '-'));
943 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
944 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
946 WCHAR *start = reader_get_cur(reader), *ptr;
947 xml_encoding enc;
948 int len;
950 if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
951 return WC_E_ENCNAME;
953 ptr = start;
954 while (is_wchar_encname(*++ptr))
957 len = ptr - start;
958 enc = parse_encoding_name(start, len);
959 TRACE("encoding name %s\n", debugstr_wn(start, len));
960 val->str = start;
961 val->len = len;
963 if (enc == XmlEncoding_Unknown)
964 return WC_E_ENCNAME;
966 /* skip encoding name */
967 reader_skipn(reader, len);
968 return S_OK;
971 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
972 static HRESULT reader_parse_encdecl(xmlreader *reader)
974 static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
975 strval name, val;
976 HRESULT hr;
978 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
980 if (reader_cmp(reader, encodingW)) return S_FALSE;
981 name.str = reader_get_cur(reader);
982 name.len = 8;
983 /* skip 'encoding' */
984 reader_skipn(reader, 8);
986 hr = reader_parse_eq(reader);
987 if (FAILED(hr)) return hr;
989 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
990 return WC_E_QUOTE;
991 /* skip "'"|'"' */
992 reader_skipn(reader, 1);
994 hr = reader_parse_encname(reader, &val);
995 if (FAILED(hr)) return hr;
997 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
998 return WC_E_QUOTE;
1000 /* skip "'"|'"' */
1001 reader_skipn(reader, 1);
1003 return reader_add_attr(reader, &name, &val);
1006 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
1007 static HRESULT reader_parse_sddecl(xmlreader *reader)
1009 static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
1010 static const WCHAR yesW[] = {'y','e','s',0};
1011 static const WCHAR noW[] = {'n','o',0};
1012 WCHAR *start, *ptr;
1013 strval name, val;
1014 HRESULT hr;
1016 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
1018 if (reader_cmp(reader, standaloneW)) return S_FALSE;
1019 reader_init_strvalue(reader_get_cur(reader), 10, &name);
1020 /* skip 'standalone' */
1021 reader_skipn(reader, 10);
1023 hr = reader_parse_eq(reader);
1024 if (FAILED(hr)) return hr;
1026 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
1027 return WC_E_QUOTE;
1028 /* skip "'"|'"' */
1029 reader_skipn(reader, 1);
1031 if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
1032 return WC_E_XMLDECL;
1034 start = reader_get_cur(reader);
1035 /* skip 'yes'|'no' */
1036 reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
1037 ptr = reader_get_cur(reader);
1038 TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
1039 val.str = val.start = start;
1040 val.len = ptr-start;
1042 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
1043 return WC_E_QUOTE;
1044 /* skip "'"|'"' */
1045 reader_skipn(reader, 1);
1047 return reader_add_attr(reader, &name, &val);
1050 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
1051 static HRESULT reader_parse_xmldecl(xmlreader *reader)
1053 static const WCHAR xmldeclW[] = {'<','?','x','m','l',' ',0};
1054 static const WCHAR declcloseW[] = {'?','>',0};
1055 HRESULT hr;
1057 /* check if we have "<?xml " */
1058 if (reader_cmp(reader, xmldeclW)) return S_FALSE;
1060 reader_skipn(reader, 5);
1061 hr = reader_parse_versioninfo(reader);
1062 if (FAILED(hr))
1063 return hr;
1065 hr = reader_parse_encdecl(reader);
1066 if (FAILED(hr))
1067 return hr;
1069 hr = reader_parse_sddecl(reader);
1070 if (FAILED(hr))
1071 return hr;
1073 reader_skipspaces(reader);
1074 if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
1075 reader_skipn(reader, 2);
1077 reader->nodetype = XmlNodeType_XmlDeclaration;
1078 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1079 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1080 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1082 return S_OK;
1085 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
1086 static HRESULT reader_parse_comment(xmlreader *reader)
1088 WCHAR *start, *ptr;
1090 if (reader->resume[XmlReadResume_Body])
1092 start = reader->resume[XmlReadResume_Body];
1093 ptr = reader_get_cur(reader);
1095 else
1097 /* skip '<!--' */
1098 reader_skipn(reader, 4);
1099 reader_shrink(reader);
1100 ptr = start = reader_get_cur(reader);
1101 reader->nodetype = XmlNodeType_Comment;
1102 reader->resume[XmlReadResume_Body] = start;
1103 reader->resumestate = XmlReadResumeState_Comment;
1104 reader_set_strvalue(reader, StringValue_LocalName, NULL);
1105 reader_set_strvalue(reader, StringValue_QualifiedName, NULL);
1106 reader_set_strvalue(reader, StringValue_Value, NULL);
1109 /* will exit when there's no more data, it won't attempt to
1110 read more from stream */
1111 while (*ptr)
1113 if (ptr[0] == '-')
1115 if (ptr[1] == '-')
1117 if (ptr[2] == '>')
1119 strval value;
1121 TRACE("%s\n", debugstr_wn(start, ptr-start));
1122 /* skip '-->' */
1123 reader_skipn(reader, 3);
1124 reader_init_strvalue(start, ptr-start, &value);
1125 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1126 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1127 reader_set_strvalue(reader, StringValue_Value, &value);
1128 reader->resume[XmlReadResume_Body] = NULL;
1129 reader->resumestate = XmlReadResumeState_Initial;
1130 return S_OK;
1132 else
1133 return WC_E_COMMENT;
1135 else
1136 ptr++;
1138 else
1140 reader_skipn(reader, 1);
1141 ptr++;
1145 return S_OK;
1148 /* [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] */
1149 static inline int is_char(WCHAR ch)
1151 return (ch == '\t') || (ch == '\r') || (ch == '\n') ||
1152 (ch >= 0x20 && ch <= 0xd7ff) ||
1153 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
1154 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
1155 (ch >= 0xe000 && ch <= 0xfffd);
1158 /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1159 static inline int is_pubchar(WCHAR ch)
1161 return (ch == ' ') ||
1162 (ch >= 'a' && ch <= 'z') ||
1163 (ch >= 'A' && ch <= 'Z') ||
1164 (ch >= '0' && ch <= '9') ||
1165 (ch >= '-' && ch <= ';') || /* '()*+,-./:; */
1166 (ch == '=') || (ch == '?') ||
1167 (ch == '@') || (ch == '!') ||
1168 (ch >= '#' && ch <= '%') || /* #$% */
1169 (ch == '_') || (ch == '\r') || (ch == '\n');
1172 static inline int is_namestartchar(WCHAR ch)
1174 return (ch == ':') || (ch >= 'A' && ch <= 'Z') ||
1175 (ch == '_') || (ch >= 'a' && ch <= 'z') ||
1176 (ch >= 0xc0 && ch <= 0xd6) ||
1177 (ch >= 0xd8 && ch <= 0xf6) ||
1178 (ch >= 0xf8 && ch <= 0x2ff) ||
1179 (ch >= 0x370 && ch <= 0x37d) ||
1180 (ch >= 0x37f && ch <= 0x1fff) ||
1181 (ch >= 0x200c && ch <= 0x200d) ||
1182 (ch >= 0x2070 && ch <= 0x218f) ||
1183 (ch >= 0x2c00 && ch <= 0x2fef) ||
1184 (ch >= 0x3001 && ch <= 0xd7ff) ||
1185 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
1186 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
1187 (ch >= 0xf900 && ch <= 0xfdcf) ||
1188 (ch >= 0xfdf0 && ch <= 0xfffd);
1191 /* [4 NS] NCName ::= Name - (Char* ':' Char*) */
1192 static inline int is_ncnamechar(WCHAR ch)
1194 return (ch >= 'A' && ch <= 'Z') ||
1195 (ch == '_') || (ch >= 'a' && ch <= 'z') ||
1196 (ch == '-') || (ch == '.') ||
1197 (ch >= '0' && ch <= '9') ||
1198 (ch == 0xb7) ||
1199 (ch >= 0xc0 && ch <= 0xd6) ||
1200 (ch >= 0xd8 && ch <= 0xf6) ||
1201 (ch >= 0xf8 && ch <= 0x2ff) ||
1202 (ch >= 0x300 && ch <= 0x36f) ||
1203 (ch >= 0x370 && ch <= 0x37d) ||
1204 (ch >= 0x37f && ch <= 0x1fff) ||
1205 (ch >= 0x200c && ch <= 0x200d) ||
1206 (ch >= 0x203f && ch <= 0x2040) ||
1207 (ch >= 0x2070 && ch <= 0x218f) ||
1208 (ch >= 0x2c00 && ch <= 0x2fef) ||
1209 (ch >= 0x3001 && ch <= 0xd7ff) ||
1210 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
1211 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
1212 (ch >= 0xf900 && ch <= 0xfdcf) ||
1213 (ch >= 0xfdf0 && ch <= 0xfffd);
1216 static inline int is_namechar(WCHAR ch)
1218 return (ch == ':') || is_ncnamechar(ch);
1221 /* [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
1222 [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
1223 [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
1224 [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
1225 [5] Name ::= NameStartChar (NameChar)* */
1226 static HRESULT reader_parse_name(xmlreader *reader, strval *name)
1228 WCHAR *ptr, *start;
1230 if (reader->resume[XmlReadResume_Name])
1232 start = reader->resume[XmlReadResume_Name];
1233 ptr = reader_get_cur(reader);
1235 else
1237 ptr = start = reader_get_cur(reader);
1238 if (!is_namestartchar(*ptr)) return WC_E_NAMECHARACTER;
1241 while (is_namechar(*ptr))
1243 reader_skipn(reader, 1);
1244 ptr = reader_get_cur(reader);
1247 if (is_reader_pending(reader))
1249 reader->resume[XmlReadResume_Name] = start;
1250 return E_PENDING;
1252 else
1253 reader->resume[XmlReadResume_Name] = NULL;
1255 TRACE("name %s:%d\n", debugstr_wn(start, ptr-start), (int)(ptr-start));
1256 reader_init_strvalue(start, ptr-start, name);
1258 return S_OK;
1261 /* [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) */
1262 static HRESULT reader_parse_pitarget(xmlreader *reader, strval *target)
1264 static const WCHAR xmlW[] = {'x','m','l'};
1265 strval name;
1266 HRESULT hr;
1267 UINT i;
1269 hr = reader_parse_name(reader, &name);
1270 if (FAILED(hr)) return is_reader_pending(reader) ? E_PENDING : WC_E_PI;
1272 /* now that we got name check for illegal content */
1273 if (name.len == 3 && !strncmpiW(name.str, xmlW, 3))
1274 return WC_E_LEADINGXML;
1276 /* PITarget can't be a qualified name */
1277 for (i = 0; i < name.len; i++)
1278 if (name.str[i] == ':')
1279 return i ? NC_E_NAMECOLON : WC_E_PI;
1281 TRACE("pitarget %s:%d\n", debugstr_wn(name.str, name.len), name.len);
1282 *target = name;
1283 return S_OK;
1286 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
1287 static HRESULT reader_parse_pi(xmlreader *reader)
1289 WCHAR *ptr, *start;
1290 strval target;
1291 HRESULT hr;
1293 switch (reader->resumestate)
1295 case XmlReadResumeState_Initial:
1296 /* skip '<?' */
1297 reader_skipn(reader, 2);
1298 reader_shrink(reader);
1299 reader->resumestate = XmlReadResumeState_PITarget;
1300 case XmlReadResumeState_PITarget:
1301 hr = reader_parse_pitarget(reader, &target);
1302 if (FAILED(hr)) return hr;
1303 reader_set_strvalue(reader, StringValue_LocalName, &target);
1304 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1305 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1306 reader->resumestate = XmlReadResumeState_PIBody;
1307 default:
1311 ptr = reader_get_cur(reader);
1312 /* exit earlier if there's no content */
1313 if (ptr[0] == '?' && ptr[1] == '>')
1315 /* skip '?>' */
1316 reader_skipn(reader, 2);
1317 reader->nodetype = XmlNodeType_ProcessingInstruction;
1318 reader->resumestate = XmlReadResumeState_Initial;
1319 return S_OK;
1322 if (!reader->resume[XmlReadResume_Body])
1324 /* now at least a single space char should be there */
1325 if (!is_wchar_space(*ptr)) return WC_E_WHITESPACE;
1326 reader_skipspaces(reader);
1327 ptr = start = reader_get_cur(reader);
1328 reader->resume[XmlReadResume_Body] = start;
1330 else
1332 start = reader->resume[XmlReadResume_Body];
1333 ptr = reader_get_cur(reader);
1336 while (*ptr)
1338 if (ptr[0] == '?')
1340 if (ptr[1] == '>')
1342 strval value;
1344 TRACE("%s\n", debugstr_wn(start, ptr-start));
1345 /* skip '?>' */
1346 reader_skipn(reader, 2);
1347 reader->nodetype = XmlNodeType_ProcessingInstruction;
1348 reader->resumestate = XmlReadResumeState_Initial;
1349 reader->resume[XmlReadResume_Body] = NULL;
1350 reader_init_strvalue(start, ptr-start, &value);
1351 reader_set_strvalue(reader, StringValue_Value, &value);
1352 return S_OK;
1354 else
1356 ptr++;
1357 reader_more(reader);
1360 else
1362 reader_skipn(reader, 1);
1363 ptr = reader_get_cur(reader);
1367 return S_OK;
1370 /* This one is used to parse significant whitespace nodes, like in Misc production */
1371 static HRESULT reader_parse_whitespace(xmlreader *reader)
1373 WCHAR *start, *ptr;
1375 reader_shrink(reader);
1376 start = reader_get_cur(reader);
1378 reader_skipspaces(reader);
1379 ptr = reader_get_cur(reader);
1380 TRACE("%s\n", debugstr_wn(start, ptr-start));
1382 reader->nodetype = XmlNodeType_Whitespace;
1383 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1384 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1385 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1386 return S_OK;
1389 /* [27] Misc ::= Comment | PI | S */
1390 static HRESULT reader_parse_misc(xmlreader *reader)
1392 HRESULT hr = S_FALSE;
1394 if (reader->resumestate != XmlReadResumeState_Initial)
1396 hr = reader_more(reader);
1397 if (FAILED(hr)) return hr;
1399 /* finish current node */
1400 switch (reader->resumestate)
1402 case XmlReadResumeState_PITarget:
1403 case XmlReadResumeState_PIBody:
1404 return reader_parse_pi(reader);
1405 case XmlReadResumeState_Comment:
1406 return reader_parse_comment(reader);
1407 default:
1408 ERR("unknown resume state %d\n", reader->resumestate);
1412 while (1)
1414 const WCHAR *cur = reader_get_cur(reader);
1416 if (is_wchar_space(*cur))
1417 hr = reader_parse_whitespace(reader);
1418 else if (!reader_cmp(reader, commentW))
1419 hr = reader_parse_comment(reader);
1420 else if (!reader_cmp(reader, piW))
1421 hr = reader_parse_pi(reader);
1422 else
1423 break;
1425 if (hr != S_FALSE) return hr;
1428 return hr;
1431 /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
1432 static HRESULT reader_parse_sys_literal(xmlreader *reader, strval *literal)
1434 WCHAR *start = reader_get_cur(reader), *cur, quote;
1436 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1438 quote = *start;
1439 reader_skipn(reader, 1);
1441 cur = start = reader_get_cur(reader);
1442 while (is_char(*cur) && *cur != quote)
1444 reader_skipn(reader, 1);
1445 cur = reader_get_cur(reader);
1447 if (*cur == quote) reader_skipn(reader, 1);
1449 literal->str = start;
1450 literal->len = cur-start;
1451 TRACE("%s\n", debugstr_wn(start, cur-start));
1452 return S_OK;
1455 /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1456 [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1457 static HRESULT reader_parse_pub_literal(xmlreader *reader, strval *literal)
1459 WCHAR *start = reader_get_cur(reader), *cur, quote;
1461 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1463 quote = *start;
1464 reader_skipn(reader, 1);
1466 cur = start;
1467 while (is_pubchar(*cur) && *cur != quote)
1469 reader_skipn(reader, 1);
1470 cur = reader_get_cur(reader);
1473 reader_init_strvalue(start, cur-start, literal);
1474 TRACE("%s\n", debugstr_wn(start, cur-start));
1475 return S_OK;
1478 /* [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral */
1479 static HRESULT reader_parse_externalid(xmlreader *reader)
1481 static WCHAR systemW[] = {'S','Y','S','T','E','M',0};
1482 static WCHAR publicW[] = {'P','U','B','L','I','C',0};
1483 strval name;
1484 HRESULT hr;
1485 int cnt;
1487 if (reader_cmp(reader, systemW))
1489 if (reader_cmp(reader, publicW))
1490 return S_FALSE;
1491 else
1493 strval pub;
1495 /* public id */
1496 reader_skipn(reader, 6);
1497 cnt = reader_skipspaces(reader);
1498 if (!cnt) return WC_E_WHITESPACE;
1500 hr = reader_parse_pub_literal(reader, &pub);
1501 if (FAILED(hr)) return hr;
1503 reader_init_strvalue(publicW, strlenW(publicW), &name);
1504 return reader_add_attr(reader, &name, &pub);
1507 else
1509 strval sys;
1511 /* system id */
1512 reader_skipn(reader, 6);
1513 cnt = reader_skipspaces(reader);
1514 if (!cnt) return WC_E_WHITESPACE;
1516 hr = reader_parse_sys_literal(reader, &sys);
1517 if (FAILED(hr)) return hr;
1519 reader_init_strvalue(systemW, strlenW(systemW), &name);
1520 return reader_add_attr(reader, &name, &sys);
1523 return hr;
1526 /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */
1527 static HRESULT reader_parse_dtd(xmlreader *reader)
1529 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',0};
1530 strval name;
1531 WCHAR *cur;
1532 HRESULT hr;
1534 /* check if we have "<!DOCTYPE" */
1535 if (reader_cmp(reader, doctypeW)) return S_FALSE;
1536 reader_shrink(reader);
1538 /* DTD processing is not allowed by default */
1539 if (reader->dtdmode == DtdProcessing_Prohibit) return WC_E_DTDPROHIBITED;
1541 reader_skipn(reader, 9);
1542 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
1544 /* name */
1545 hr = reader_parse_name(reader, &name);
1546 if (FAILED(hr)) return WC_E_DECLDOCTYPE;
1548 reader_skipspaces(reader);
1550 hr = reader_parse_externalid(reader);
1551 if (FAILED(hr)) return hr;
1553 reader_skipspaces(reader);
1555 cur = reader_get_cur(reader);
1556 if (*cur != '>')
1558 FIXME("internal subset parsing not implemented\n");
1559 return E_NOTIMPL;
1562 /* skip '>' */
1563 reader_skipn(reader, 1);
1565 reader->nodetype = XmlNodeType_DocumentType;
1566 reader_set_strvalue(reader, StringValue_LocalName, &name);
1567 reader_set_strvalue(reader, StringValue_QualifiedName, &name);
1569 return S_OK;
1572 /* [11 NS] LocalPart ::= NCName */
1573 static HRESULT reader_parse_local(xmlreader *reader, strval *local)
1575 WCHAR *ptr, *start;
1577 if (reader->resume[XmlReadResume_Local])
1579 start = reader->resume[XmlReadResume_Local];
1580 ptr = reader_get_cur(reader);
1582 else
1584 ptr = start = reader_get_cur(reader);
1587 while (is_ncnamechar(*ptr))
1589 reader_skipn(reader, 1);
1590 ptr = reader_get_cur(reader);
1593 if (is_reader_pending(reader))
1595 reader->resume[XmlReadResume_Local] = start;
1596 return E_PENDING;
1598 else
1599 reader->resume[XmlReadResume_Local] = NULL;
1601 reader_init_strvalue(start, ptr-start, local);
1603 return S_OK;
1606 /* [7 NS] QName ::= PrefixedName | UnprefixedName
1607 [8 NS] PrefixedName ::= Prefix ':' LocalPart
1608 [9 NS] UnprefixedName ::= LocalPart
1609 [10 NS] Prefix ::= NCName */
1610 static HRESULT reader_parse_qname(xmlreader *reader, strval *prefix, strval *local, strval *qname)
1612 WCHAR *ptr, *start;
1613 HRESULT hr;
1615 if (reader->resume[XmlReadResume_Name])
1617 start = reader->resume[XmlReadResume_Name];
1618 ptr = reader_get_cur(reader);
1620 else
1622 ptr = start = reader_get_cur(reader);
1623 reader->resume[XmlReadResume_Name] = start;
1624 if (!is_ncnamechar(*ptr)) return NC_E_QNAMECHARACTER;
1627 if (reader->resume[XmlReadResume_Local])
1629 hr = reader_parse_local(reader, local);
1630 if (FAILED(hr)) return hr;
1632 reader_init_strvalue(reader->resume[XmlReadResume_Name],
1633 local->start - reader->resume[XmlReadResume_Name] - 1,
1634 prefix);
1636 else
1638 /* skip prefix part */
1639 while (is_ncnamechar(*ptr))
1641 reader_skipn(reader, 1);
1642 ptr = reader_get_cur(reader);
1645 if (is_reader_pending(reader)) return E_PENDING;
1647 /* got a qualified name */
1648 if (*ptr == ':')
1650 reader_init_strvalue(start, ptr-start, prefix);
1652 /* skip ':' */
1653 reader_skipn(reader, 1);
1654 hr = reader_parse_local(reader, local);
1655 if (FAILED(hr)) return hr;
1657 else
1659 reader_init_strvalue(reader->resume[XmlReadResume_Name], ptr-reader->resume[XmlReadResume_Name], local);
1660 reader_init_strvalue(NULL, 0, prefix);
1664 reader_init_strvalue(start, ptr-start, local);
1666 if (prefix->len)
1667 TRACE("qname %s:%s\n", debugstr_wn(prefix->start, prefix->len), debugstr_wn(local->start, local->len));
1668 else
1669 TRACE("ncname %s\n", debugstr_wn(local->start, local->len));
1671 reader_init_strvalue(prefix->start ? prefix->start : local->start,
1672 /* count ':' too */
1673 (prefix->len ? prefix->len + 1 : 0) + local->len,
1674 qname);
1676 reader->resume[XmlReadResume_Name] = NULL;
1677 reader->resume[XmlReadResume_Local] = NULL;
1679 return S_OK;
1682 /* [12 NS] STag ::= '<' QName (S Attribute)* S? '>'
1683 [14 NS] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>' */
1684 static HRESULT reader_parse_stag(xmlreader *reader, strval *prefix, strval *local, strval *qname, int *empty)
1686 static const WCHAR endW[] = {'/','>',0};
1687 HRESULT hr;
1689 hr = reader_parse_qname(reader, prefix, local, qname);
1690 if (FAILED(hr)) return hr;
1692 reader_skipspaces(reader);
1694 /* empty element */
1695 if ((*empty = !reader_cmp(reader, endW)))
1697 /* skip '/>' */
1698 reader_skipn(reader, 2);
1699 return S_OK;
1702 /* got a start tag */
1703 if (!reader_cmp(reader, gtW))
1705 /* skip '>' */
1706 reader_skipn(reader, 1);
1707 return reader_push_element(reader, qname);
1710 FIXME("only empty elements/start tags without attribute list supported\n");
1711 return E_NOTIMPL;
1714 /* [39] element ::= EmptyElemTag | STag content ETag */
1715 static HRESULT reader_parse_element(xmlreader *reader)
1717 HRESULT hr;
1719 switch (reader->resumestate)
1721 case XmlReadResumeState_Initial:
1722 /* check if we are really on element */
1723 if (reader_cmp(reader, ltW)) return S_FALSE;
1725 /* skip '<' */
1726 reader_skipn(reader, 1);
1728 reader_shrink(reader);
1729 reader->resumestate = XmlReadResumeState_STag;
1730 case XmlReadResumeState_STag:
1732 strval qname, prefix, local;
1733 int empty = 0;
1735 /* this handles empty elements too */
1736 hr = reader_parse_stag(reader, &prefix, &local, &qname, &empty);
1737 if (FAILED(hr)) return hr;
1739 /* FIXME: need to check for defined namespace to reject invalid prefix,
1740 currently reject all prefixes */
1741 if (prefix.len) return NC_E_UNDECLAREDPREFIX;
1743 /* if we got empty element and stack is empty go straight to Misc */
1744 if (empty && list_empty(&reader->elements))
1745 reader->instate = XmlReadInState_MiscEnd;
1746 else
1747 reader->instate = XmlReadInState_Content;
1749 reader->nodetype = XmlNodeType_Element;
1750 reader->resumestate = XmlReadResumeState_Initial;
1751 reader_set_strvalue(reader, StringValue_LocalName, &local);
1752 reader_set_strvalue(reader, StringValue_QualifiedName, &qname);
1753 break;
1755 default:
1756 hr = E_FAIL;
1759 return hr;
1762 /* [13 NS] ETag ::= '</' QName S? '>' */
1763 static HRESULT reader_parse_endtag(xmlreader *reader)
1765 strval prefix, local, qname;
1766 struct element *elem;
1767 HRESULT hr;
1769 /* skip '</' */
1770 reader_skipn(reader, 2);
1772 hr = reader_parse_qname(reader, &prefix, &local, &qname);
1773 if (FAILED(hr)) return hr;
1775 reader_skipspaces(reader);
1777 if (reader_cmp(reader, gtW)) return WC_E_GREATERTHAN;
1779 /* skip '>' */
1780 reader_skipn(reader, 1);
1782 /* Element stack should never be empty at this point, cause we shouldn't get to
1783 content parsing if it's empty. */
1784 elem = LIST_ENTRY(list_head(&reader->elements), struct element, entry);
1785 if (!strval_eq(&elem->qname, &qname)) return WC_E_ELEMENTMATCH;
1787 reader_pop_element(reader);
1789 reader->nodetype = XmlNodeType_EndElement;
1790 reader_set_strvalue(reader, StringValue_LocalName, &local);
1791 reader_set_strvalue(reader, StringValue_QualifiedName, &qname);
1793 return S_OK;
1796 /* [18] CDSect ::= CDStart CData CDEnd
1797 [19] CDStart ::= '<![CDATA['
1798 [20] CData ::= (Char* - (Char* ']]>' Char*))
1799 [21] CDEnd ::= ']]>' */
1800 static HRESULT reader_parse_cdata(xmlreader *reader)
1802 WCHAR *start, *ptr;
1804 if (reader->resume[XmlReadResume_Body])
1806 start = reader->resume[XmlReadResume_Body];
1807 ptr = reader_get_cur(reader);
1809 else
1811 /* skip markup '<![CDATA[' */
1812 reader_skipn(reader, 9);
1813 reader_shrink(reader);
1814 ptr = start = reader_get_cur(reader);
1815 reader->nodetype = XmlNodeType_CDATA;
1816 reader->resume[XmlReadResume_Body] = start;
1817 reader->resumestate = XmlReadResumeState_CDATA;
1818 reader_set_strvalue(reader, StringValue_LocalName, NULL);
1819 reader_set_strvalue(reader, StringValue_QualifiedName, NULL);
1820 reader_set_strvalue(reader, StringValue_Value, NULL);
1823 while (*ptr)
1825 if (*ptr == ']' && *(ptr+1) == ']' && *(ptr+2) == '>')
1827 strval value;
1829 TRACE("%s\n", debugstr_wn(start, ptr-start));
1830 /* skip ']]>' */
1831 reader_skipn(reader, 3);
1832 reader_init_strvalue(start, ptr-start, &value);
1833 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1834 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1835 reader_set_strvalue(reader, StringValue_Value, &value);
1836 reader->resume[XmlReadResume_Body] = NULL;
1837 reader->resumestate = XmlReadResumeState_Initial;
1838 return S_OK;
1840 else
1842 /* Value normalization is not fully implemented, rules are:
1844 - single '\r' -> '\n';
1845 - sequence '\r\n' -> '\n', in this case value length changes;
1847 if (*ptr == '\r') *ptr = '\n';
1848 reader_skipn(reader, 1);
1849 ptr++;
1853 return S_OK;
1856 /* [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1857 [67] Reference ::= EntityRef | CharRef
1858 [68] EntityRef ::= '&' Name ';' */
1859 static HRESULT reader_parse_reference(xmlreader *reader)
1861 FIXME("References not supported\n");
1862 return E_NOTIMPL;
1865 /* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) */
1866 static HRESULT reader_parse_chardata(xmlreader *reader)
1868 FIXME("CharData not supported\n");
1869 return E_NOTIMPL;
1872 /* [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* */
1873 static HRESULT reader_parse_content(xmlreader *reader)
1875 static const WCHAR cdstartW[] = {'<','!','[','C','D','A','T','A','[',0};
1876 static const WCHAR etagW[] = {'<','/',0};
1877 static const WCHAR ampW[] = {'&',0};
1879 if (reader->resumestate != XmlReadResumeState_Initial)
1881 switch (reader->resumestate)
1883 case XmlReadResumeState_CDATA:
1884 return reader_parse_cdata(reader);
1885 case XmlReadResumeState_Comment:
1886 return reader_parse_comment(reader);
1887 case XmlReadResumeState_PIBody:
1888 case XmlReadResumeState_PITarget:
1889 return reader_parse_pi(reader);
1890 default:
1891 ERR("unknown resume state %d\n", reader->resumestate);
1895 reader_shrink(reader);
1897 /* handle end tag here, it indicates end of content as well */
1898 if (!reader_cmp(reader, etagW))
1899 return reader_parse_endtag(reader);
1901 if (!reader_cmp(reader, commentW))
1902 return reader_parse_comment(reader);
1904 if (!reader_cmp(reader, piW))
1905 return reader_parse_pi(reader);
1907 if (!reader_cmp(reader, cdstartW))
1908 return reader_parse_cdata(reader);
1910 if (!reader_cmp(reader, ampW))
1911 return reader_parse_reference(reader);
1913 if (!reader_cmp(reader, ltW))
1914 return reader_parse_element(reader);
1916 /* what's left must be CharData */
1917 return reader_parse_chardata(reader);
1920 static HRESULT reader_parse_nextnode(xmlreader *reader)
1922 HRESULT hr;
1924 while (1)
1926 switch (reader->instate)
1928 /* if it's a first call for a new input we need to detect stream encoding */
1929 case XmlReadInState_Initial:
1931 xml_encoding enc;
1933 hr = readerinput_growraw(reader->input);
1934 if (FAILED(hr)) return hr;
1936 /* try to detect encoding by BOM or data and set input code page */
1937 hr = readerinput_detectencoding(reader->input, &enc);
1938 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
1939 if (FAILED(hr)) return hr;
1941 /* always switch first time cause we have to put something in */
1942 readerinput_switchencoding(reader->input, enc);
1944 /* parse xml declaration */
1945 hr = reader_parse_xmldecl(reader);
1946 if (FAILED(hr)) return hr;
1948 readerinput_shrinkraw(reader->input, -1);
1949 reader->instate = XmlReadInState_Misc_DTD;
1950 if (hr == S_OK) return hr;
1952 break;
1953 case XmlReadInState_Misc_DTD:
1954 hr = reader_parse_misc(reader);
1955 if (FAILED(hr)) return hr;
1957 if (hr == S_FALSE)
1958 reader->instate = XmlReadInState_DTD;
1959 else
1960 return hr;
1961 break;
1962 case XmlReadInState_DTD:
1963 hr = reader_parse_dtd(reader);
1964 if (FAILED(hr)) return hr;
1966 if (hr == S_OK)
1968 reader->instate = XmlReadInState_DTD_Misc;
1969 return hr;
1971 else
1972 reader->instate = XmlReadInState_Element;
1973 break;
1974 case XmlReadInState_DTD_Misc:
1975 hr = reader_parse_misc(reader);
1976 if (FAILED(hr)) return hr;
1978 if (hr == S_FALSE)
1979 reader->instate = XmlReadInState_Element;
1980 else
1981 return hr;
1982 break;
1983 case XmlReadInState_Element:
1984 return reader_parse_element(reader);
1985 case XmlReadInState_Content:
1986 return reader_parse_content(reader);
1987 default:
1988 FIXME("internal state %d not handled\n", reader->instate);
1989 return E_NOTIMPL;
1993 return E_NOTIMPL;
1996 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
1998 xmlreader *This = impl_from_IXmlReader(iface);
2000 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
2002 if (IsEqualGUID(riid, &IID_IUnknown) ||
2003 IsEqualGUID(riid, &IID_IXmlReader))
2005 *ppvObject = iface;
2007 else
2009 FIXME("interface %s not implemented\n", debugstr_guid(riid));
2010 *ppvObject = NULL;
2011 return E_NOINTERFACE;
2014 IXmlReader_AddRef(iface);
2016 return S_OK;
2019 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
2021 xmlreader *This = impl_from_IXmlReader(iface);
2022 ULONG ref = InterlockedIncrement(&This->ref);
2023 TRACE("(%p)->(%d)\n", This, ref);
2024 return ref;
2027 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
2029 xmlreader *This = impl_from_IXmlReader(iface);
2030 LONG ref = InterlockedDecrement(&This->ref);
2032 TRACE("(%p)->(%d)\n", This, ref);
2034 if (ref == 0)
2036 IMalloc *imalloc = This->imalloc;
2037 if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
2038 reader_clear_attrs(This);
2039 reader_clear_elements(This);
2040 reader_free_strvalues(This);
2041 reader_free(This, This);
2042 if (imalloc) IMalloc_Release(imalloc);
2045 return ref;
2048 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
2050 xmlreader *This = impl_from_IXmlReader(iface);
2051 IXmlReaderInput *readerinput;
2052 HRESULT hr;
2054 TRACE("(%p)->(%p)\n", This, input);
2056 if (This->input)
2058 readerinput_release_stream(This->input);
2059 IUnknown_Release(&This->input->IXmlReaderInput_iface);
2060 This->input = NULL;
2063 This->line = This->pos = 0;
2064 reader_clear_elements(This);
2065 This->depth = 0;
2066 This->resumestate = XmlReadResumeState_Initial;
2067 memset(This->resume, 0, sizeof(This->resume));
2069 /* just reset current input */
2070 if (!input)
2072 This->state = XmlReadState_Initial;
2073 return S_OK;
2076 /* now try IXmlReaderInput, ISequentialStream, IStream */
2077 hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&readerinput);
2078 if (hr == S_OK)
2080 if (readerinput->lpVtbl == &xmlreaderinputvtbl)
2081 This->input = impl_from_IXmlReaderInput(readerinput);
2082 else
2084 ERR("got external IXmlReaderInput implementation: %p, vtbl=%p\n",
2085 readerinput, readerinput->lpVtbl);
2086 IUnknown_Release(readerinput);
2087 return E_FAIL;
2092 if (hr != S_OK || !readerinput)
2094 /* create IXmlReaderInput basing on supplied interface */
2095 hr = CreateXmlReaderInputWithEncodingName(input,
2096 NULL, NULL, FALSE, NULL, &readerinput);
2097 if (hr != S_OK) return hr;
2098 This->input = impl_from_IXmlReaderInput(readerinput);
2101 /* set stream for supplied IXmlReaderInput */
2102 hr = readerinput_query_for_stream(This->input);
2103 if (hr == S_OK)
2105 This->state = XmlReadState_Initial;
2106 This->instate = XmlReadInState_Initial;
2109 return hr;
2112 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
2114 xmlreader *This = impl_from_IXmlReader(iface);
2116 TRACE("(%p %u %p)\n", This, property, value);
2118 if (!value) return E_INVALIDARG;
2120 switch (property)
2122 case XmlReaderProperty_DtdProcessing:
2123 *value = This->dtdmode;
2124 break;
2125 case XmlReaderProperty_ReadState:
2126 *value = This->state;
2127 break;
2128 default:
2129 FIXME("Unimplemented property (%u)\n", property);
2130 return E_NOTIMPL;
2133 return S_OK;
2136 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
2138 xmlreader *This = impl_from_IXmlReader(iface);
2140 TRACE("(%p %u %lu)\n", iface, property, value);
2142 switch (property)
2144 case XmlReaderProperty_DtdProcessing:
2145 if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
2146 This->dtdmode = value;
2147 break;
2148 default:
2149 FIXME("Unimplemented property (%u)\n", property);
2150 return E_NOTIMPL;
2153 return S_OK;
2156 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
2158 xmlreader *This = impl_from_IXmlReader(iface);
2159 XmlNodeType oldtype = This->nodetype;
2160 HRESULT hr;
2162 TRACE("(%p)->(%p)\n", This, nodetype);
2164 if (This->state == XmlReadState_Closed) return S_FALSE;
2166 hr = reader_parse_nextnode(This);
2167 if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
2168 This->state = XmlReadState_Interactive;
2169 if (hr == S_OK) *nodetype = This->nodetype;
2171 return hr;
2174 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
2176 xmlreader *This = impl_from_IXmlReader(iface);
2177 TRACE("(%p)->(%p)\n", This, node_type);
2179 /* When we're on attribute always return attribute type, container node type is kept.
2180 Note that container is not necessarily an element, and attribute doesn't mean it's
2181 an attribute in XML spec terms. */
2182 *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
2183 return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
2186 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
2188 xmlreader *This = impl_from_IXmlReader(iface);
2190 TRACE("(%p)\n", This);
2192 if (!This->attr_count) return S_FALSE;
2193 This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
2194 return S_OK;
2197 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
2199 xmlreader *This = impl_from_IXmlReader(iface);
2200 const struct list *next;
2202 TRACE("(%p)\n", This);
2204 if (!This->attr_count) return S_FALSE;
2206 if (!This->attr)
2207 return IXmlReader_MoveToFirstAttribute(iface);
2209 next = list_next(&This->attrs, &This->attr->entry);
2210 if (next)
2211 This->attr = LIST_ENTRY(next, struct attribute, entry);
2213 return next ? S_OK : S_FALSE;
2216 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
2217 LPCWSTR local_name,
2218 LPCWSTR namespaceUri)
2220 FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
2221 return E_NOTIMPL;
2224 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
2226 xmlreader *This = impl_from_IXmlReader(iface);
2228 TRACE("(%p)\n", This);
2230 if (!This->attr_count) return S_FALSE;
2231 This->attr = NULL;
2232 return S_OK;
2235 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *name, UINT *len)
2237 xmlreader *This = impl_from_IXmlReader(iface);
2239 TRACE("(%p)->(%p %p)\n", This, name, len);
2240 *name = This->strvalues[StringValue_QualifiedName].str;
2241 *len = This->strvalues[StringValue_QualifiedName].len;
2242 return S_OK;
2245 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
2246 LPCWSTR *namespaceUri,
2247 UINT *namespaceUri_length)
2249 FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
2250 return E_NOTIMPL;
2253 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface, LPCWSTR *name, UINT *len)
2255 xmlreader *This = impl_from_IXmlReader(iface);
2257 TRACE("(%p)->(%p %p)\n", This, name, len);
2258 *name = This->strvalues[StringValue_LocalName].str;
2259 if (len) *len = This->strvalues[StringValue_LocalName].len;
2260 return S_OK;
2263 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
2264 LPCWSTR *prefix,
2265 UINT *prefix_length)
2267 FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
2268 return E_NOTIMPL;
2271 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, const WCHAR **value, UINT *len)
2273 xmlreader *reader = impl_from_IXmlReader(iface);
2274 strval *val = &reader->strvalues[StringValue_Value];
2276 TRACE("(%p)->(%p %p)\n", reader, value, len);
2278 *value = NULL;
2280 if ((reader->nodetype == XmlNodeType_Comment && !val->str) || is_reader_pending(reader))
2282 XmlNodeType type;
2283 HRESULT hr;
2285 hr = IXmlReader_Read(iface, &type);
2286 if (FAILED(hr)) return hr;
2288 /* return if still pending, partially read values are not reported */
2289 if (is_reader_pending(reader)) return E_PENDING;
2292 if (!val->str)
2294 val->str = reader_alloc(reader, (val->len+1)*sizeof(WCHAR));
2295 if (!val->str) return E_OUTOFMEMORY;
2296 memcpy(val->str, val->start, val->len*sizeof(WCHAR));
2297 val->str[val->len] = 0;
2300 *value = val->str;
2301 if (len) *len = val->len;
2302 return S_OK;
2305 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface, WCHAR *buffer, UINT chunk_size, UINT *read)
2307 xmlreader *reader = impl_from_IXmlReader(iface);
2308 strval *val = &reader->strvalues[StringValue_Value];
2309 UINT len;
2311 TRACE("(%p)->(%p %u %p)\n", reader, buffer, chunk_size, read);
2313 /* Value is already allocated, chunked reads are not possible. */
2314 if (val->str) return S_FALSE;
2316 if (val->len)
2318 len = min(chunk_size, val->len);
2319 memcpy(buffer, val->start, len);
2320 val->start += len;
2321 val->len -= len;
2322 if (read) *read = len;
2325 return S_OK;
2328 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
2329 LPCWSTR *baseUri,
2330 UINT *baseUri_length)
2332 FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
2333 return E_NOTIMPL;
2336 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
2338 FIXME("(%p): stub\n", iface);
2339 return E_NOTIMPL;
2342 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
2344 FIXME("(%p): stub\n", iface);
2345 return E_NOTIMPL;
2348 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
2350 xmlreader *This = impl_from_IXmlReader(iface);
2352 TRACE("(%p %p)\n", This, lineNumber);
2354 if (!lineNumber) return E_INVALIDARG;
2356 *lineNumber = This->line;
2358 return S_OK;
2361 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
2363 xmlreader *This = impl_from_IXmlReader(iface);
2365 TRACE("(%p %p)\n", This, linePosition);
2367 if (!linePosition) return E_INVALIDARG;
2369 *linePosition = This->pos;
2371 return S_OK;
2374 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
2376 xmlreader *This = impl_from_IXmlReader(iface);
2378 TRACE("(%p)->(%p)\n", This, count);
2380 if (!count) return E_INVALIDARG;
2382 *count = This->attr_count;
2383 return S_OK;
2386 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
2388 xmlreader *This = impl_from_IXmlReader(iface);
2389 TRACE("(%p)->(%p)\n", This, depth);
2390 *depth = This->depth;
2391 return S_OK;
2394 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
2396 FIXME("(%p): stub\n", iface);
2397 return E_NOTIMPL;
2400 static const struct IXmlReaderVtbl xmlreader_vtbl =
2402 xmlreader_QueryInterface,
2403 xmlreader_AddRef,
2404 xmlreader_Release,
2405 xmlreader_SetInput,
2406 xmlreader_GetProperty,
2407 xmlreader_SetProperty,
2408 xmlreader_Read,
2409 xmlreader_GetNodeType,
2410 xmlreader_MoveToFirstAttribute,
2411 xmlreader_MoveToNextAttribute,
2412 xmlreader_MoveToAttributeByName,
2413 xmlreader_MoveToElement,
2414 xmlreader_GetQualifiedName,
2415 xmlreader_GetNamespaceUri,
2416 xmlreader_GetLocalName,
2417 xmlreader_GetPrefix,
2418 xmlreader_GetValue,
2419 xmlreader_ReadValueChunk,
2420 xmlreader_GetBaseUri,
2421 xmlreader_IsDefault,
2422 xmlreader_IsEmptyElement,
2423 xmlreader_GetLineNumber,
2424 xmlreader_GetLinePosition,
2425 xmlreader_GetAttributeCount,
2426 xmlreader_GetDepth,
2427 xmlreader_IsEOF
2430 /** IXmlReaderInput **/
2431 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
2433 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2435 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
2437 if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
2438 IsEqualGUID(riid, &IID_IUnknown))
2440 *ppvObject = iface;
2442 else
2444 WARN("interface %s not implemented\n", debugstr_guid(riid));
2445 *ppvObject = NULL;
2446 return E_NOINTERFACE;
2449 IUnknown_AddRef(iface);
2451 return S_OK;
2454 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
2456 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2457 ULONG ref = InterlockedIncrement(&This->ref);
2458 TRACE("(%p)->(%d)\n", This, ref);
2459 return ref;
2462 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
2464 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2465 LONG ref = InterlockedDecrement(&This->ref);
2467 TRACE("(%p)->(%d)\n", This, ref);
2469 if (ref == 0)
2471 IMalloc *imalloc = This->imalloc;
2472 if (This->input) IUnknown_Release(This->input);
2473 if (This->stream) ISequentialStream_Release(This->stream);
2474 if (This->buffer) free_input_buffer(This->buffer);
2475 readerinput_free(This, This->baseuri);
2476 readerinput_free(This, This);
2477 if (imalloc) IMalloc_Release(imalloc);
2480 return ref;
2483 static const struct IUnknownVtbl xmlreaderinputvtbl =
2485 xmlreaderinput_QueryInterface,
2486 xmlreaderinput_AddRef,
2487 xmlreaderinput_Release
2490 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
2492 xmlreader *reader;
2493 int i;
2495 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
2497 if (!IsEqualGUID(riid, &IID_IXmlReader))
2499 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
2500 return E_FAIL;
2503 if (imalloc)
2504 reader = IMalloc_Alloc(imalloc, sizeof(*reader));
2505 else
2506 reader = heap_alloc(sizeof(*reader));
2507 if(!reader) return E_OUTOFMEMORY;
2509 reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
2510 reader->ref = 1;
2511 reader->input = NULL;
2512 reader->state = XmlReadState_Closed;
2513 reader->instate = XmlReadInState_Initial;
2514 reader->resumestate = XmlReadResumeState_Initial;
2515 reader->dtdmode = DtdProcessing_Prohibit;
2516 reader->line = reader->pos = 0;
2517 reader->imalloc = imalloc;
2518 if (imalloc) IMalloc_AddRef(imalloc);
2519 reader->nodetype = XmlNodeType_None;
2520 list_init(&reader->attrs);
2521 reader->attr_count = 0;
2522 reader->attr = NULL;
2523 list_init(&reader->elements);
2524 reader->depth = 0;
2525 memset(reader->resume, 0, sizeof(reader->resume));
2527 for (i = 0; i < StringValue_Last; i++)
2528 reader->strvalues[i] = strval_empty;
2530 *obj = &reader->IXmlReader_iface;
2532 TRACE("returning iface %p\n", *obj);
2534 return S_OK;
2537 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
2538 IMalloc *imalloc,
2539 LPCWSTR encoding,
2540 BOOL hint,
2541 LPCWSTR base_uri,
2542 IXmlReaderInput **ppInput)
2544 xmlreaderinput *readerinput;
2545 HRESULT hr;
2547 TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
2548 hint, wine_dbgstr_w(base_uri), ppInput);
2550 if (!stream || !ppInput) return E_INVALIDARG;
2552 if (imalloc)
2553 readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
2554 else
2555 readerinput = heap_alloc(sizeof(*readerinput));
2556 if(!readerinput) return E_OUTOFMEMORY;
2558 readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinputvtbl;
2559 readerinput->ref = 1;
2560 readerinput->imalloc = imalloc;
2561 readerinput->stream = NULL;
2562 if (imalloc) IMalloc_AddRef(imalloc);
2563 readerinput->encoding = parse_encoding_name(encoding, -1);
2564 readerinput->hint = hint;
2565 readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
2566 readerinput->pending = 0;
2568 hr = alloc_input_buffer(readerinput);
2569 if (hr != S_OK)
2571 readerinput_free(readerinput, readerinput->baseuri);
2572 readerinput_free(readerinput, readerinput);
2573 if (imalloc) IMalloc_Release(imalloc);
2574 return hr;
2576 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
2578 *ppInput = &readerinput->IXmlReaderInput_iface;
2580 TRACE("returning iface %p\n", *ppInput);
2582 return S_OK;