ws2_32: Close the dest socket (Coverity).
[wine.git] / dlls / xmllite / reader.c
blobed4f97e7c6b262ead290110ef55af0d4b1d288c0
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->resumestate = XmlReadResumeState_PIBody;
1304 default:
1308 ptr = reader_get_cur(reader);
1309 /* exit earlier if there's no content */
1310 if (ptr[0] == '?' && ptr[1] == '>')
1312 /* skip '?>' */
1313 reader_skipn(reader, 2);
1314 reader->nodetype = XmlNodeType_ProcessingInstruction;
1315 reader->resumestate = XmlReadResumeState_Initial;
1316 reader_set_strvalue(reader, StringValue_LocalName, &target);
1317 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1318 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
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_LocalName, &target);
1352 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1353 reader_set_strvalue(reader, StringValue_Value, &value);
1354 return S_OK;
1356 else
1358 ptr++;
1359 reader_more(reader);
1362 else
1364 reader_skipn(reader, 1);
1365 ptr = reader_get_cur(reader);
1369 return S_OK;
1372 /* This one is used to parse significant whitespace nodes, like in Misc production */
1373 static HRESULT reader_parse_whitespace(xmlreader *reader)
1375 WCHAR *start, *ptr;
1377 reader_shrink(reader);
1378 start = reader_get_cur(reader);
1380 reader_skipspaces(reader);
1381 ptr = reader_get_cur(reader);
1382 TRACE("%s\n", debugstr_wn(start, ptr-start));
1384 reader->nodetype = XmlNodeType_Whitespace;
1385 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1386 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1387 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1388 return S_OK;
1391 /* [27] Misc ::= Comment | PI | S */
1392 static HRESULT reader_parse_misc(xmlreader *reader)
1394 HRESULT hr = S_FALSE;
1396 if (reader->resumestate != XmlReadResumeState_Initial)
1398 hr = reader_more(reader);
1399 if (FAILED(hr)) return hr;
1401 /* finish current node */
1402 switch (reader->resumestate)
1404 case XmlReadResumeState_PITarget:
1405 case XmlReadResumeState_PIBody:
1406 return reader_parse_pi(reader);
1407 case XmlReadResumeState_Comment:
1408 return reader_parse_comment(reader);
1409 default:
1410 ERR("unknown resume state %d\n", reader->resumestate);
1414 while (1)
1416 const WCHAR *cur = reader_get_cur(reader);
1418 if (is_wchar_space(*cur))
1419 hr = reader_parse_whitespace(reader);
1420 else if (!reader_cmp(reader, commentW))
1421 hr = reader_parse_comment(reader);
1422 else if (!reader_cmp(reader, piW))
1423 hr = reader_parse_pi(reader);
1424 else
1425 break;
1427 if (hr != S_FALSE) return hr;
1430 return hr;
1433 /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
1434 static HRESULT reader_parse_sys_literal(xmlreader *reader, strval *literal)
1436 WCHAR *start = reader_get_cur(reader), *cur, quote;
1438 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1440 quote = *start;
1441 reader_skipn(reader, 1);
1443 cur = start = reader_get_cur(reader);
1444 while (is_char(*cur) && *cur != quote)
1446 reader_skipn(reader, 1);
1447 cur = reader_get_cur(reader);
1449 if (*cur == quote) reader_skipn(reader, 1);
1451 literal->str = start;
1452 literal->len = cur-start;
1453 TRACE("%s\n", debugstr_wn(start, cur-start));
1454 return S_OK;
1457 /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1458 [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1459 static HRESULT reader_parse_pub_literal(xmlreader *reader, strval *literal)
1461 WCHAR *start = reader_get_cur(reader), *cur, quote;
1463 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1465 quote = *start;
1466 reader_skipn(reader, 1);
1468 cur = start;
1469 while (is_pubchar(*cur) && *cur != quote)
1471 reader_skipn(reader, 1);
1472 cur = reader_get_cur(reader);
1475 reader_init_strvalue(start, cur-start, literal);
1476 TRACE("%s\n", debugstr_wn(start, cur-start));
1477 return S_OK;
1480 /* [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral */
1481 static HRESULT reader_parse_externalid(xmlreader *reader)
1483 static WCHAR systemW[] = {'S','Y','S','T','E','M',0};
1484 static WCHAR publicW[] = {'P','U','B','L','I','C',0};
1485 strval name;
1486 HRESULT hr;
1487 int cnt;
1489 if (reader_cmp(reader, systemW))
1491 if (reader_cmp(reader, publicW))
1492 return S_FALSE;
1493 else
1495 strval pub;
1497 /* public id */
1498 reader_skipn(reader, 6);
1499 cnt = reader_skipspaces(reader);
1500 if (!cnt) return WC_E_WHITESPACE;
1502 hr = reader_parse_pub_literal(reader, &pub);
1503 if (FAILED(hr)) return hr;
1505 reader_init_strvalue(publicW, strlenW(publicW), &name);
1506 return reader_add_attr(reader, &name, &pub);
1509 else
1511 strval sys;
1513 /* system id */
1514 reader_skipn(reader, 6);
1515 cnt = reader_skipspaces(reader);
1516 if (!cnt) return WC_E_WHITESPACE;
1518 hr = reader_parse_sys_literal(reader, &sys);
1519 if (FAILED(hr)) return hr;
1521 reader_init_strvalue(systemW, strlenW(systemW), &name);
1522 return reader_add_attr(reader, &name, &sys);
1525 return hr;
1528 /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */
1529 static HRESULT reader_parse_dtd(xmlreader *reader)
1531 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',0};
1532 strval name;
1533 WCHAR *cur;
1534 HRESULT hr;
1536 /* check if we have "<!DOCTYPE" */
1537 if (reader_cmp(reader, doctypeW)) return S_FALSE;
1538 reader_shrink(reader);
1540 /* DTD processing is not allowed by default */
1541 if (reader->dtdmode == DtdProcessing_Prohibit) return WC_E_DTDPROHIBITED;
1543 reader_skipn(reader, 9);
1544 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
1546 /* name */
1547 hr = reader_parse_name(reader, &name);
1548 if (FAILED(hr)) return WC_E_DECLDOCTYPE;
1550 reader_skipspaces(reader);
1552 hr = reader_parse_externalid(reader);
1553 if (FAILED(hr)) return hr;
1555 reader_skipspaces(reader);
1557 cur = reader_get_cur(reader);
1558 if (*cur != '>')
1560 FIXME("internal subset parsing not implemented\n");
1561 return E_NOTIMPL;
1564 /* skip '>' */
1565 reader_skipn(reader, 1);
1567 reader->nodetype = XmlNodeType_DocumentType;
1568 reader_set_strvalue(reader, StringValue_LocalName, &name);
1569 reader_set_strvalue(reader, StringValue_QualifiedName, &name);
1571 return S_OK;
1574 /* [11 NS] LocalPart ::= NCName */
1575 static HRESULT reader_parse_local(xmlreader *reader, strval *local)
1577 WCHAR *ptr, *start;
1579 if (reader->resume[XmlReadResume_Local])
1581 start = reader->resume[XmlReadResume_Local];
1582 ptr = reader_get_cur(reader);
1584 else
1586 ptr = start = reader_get_cur(reader);
1589 while (is_ncnamechar(*ptr))
1591 reader_skipn(reader, 1);
1592 ptr = reader_get_cur(reader);
1595 if (is_reader_pending(reader))
1597 reader->resume[XmlReadResume_Local] = start;
1598 return E_PENDING;
1600 else
1601 reader->resume[XmlReadResume_Local] = NULL;
1603 reader_init_strvalue(start, ptr-start, local);
1605 return S_OK;
1608 /* [7 NS] QName ::= PrefixedName | UnprefixedName
1609 [8 NS] PrefixedName ::= Prefix ':' LocalPart
1610 [9 NS] UnprefixedName ::= LocalPart
1611 [10 NS] Prefix ::= NCName */
1612 static HRESULT reader_parse_qname(xmlreader *reader, strval *prefix, strval *local, strval *qname)
1614 WCHAR *ptr, *start;
1615 HRESULT hr;
1617 if (reader->resume[XmlReadResume_Name])
1619 start = reader->resume[XmlReadResume_Name];
1620 ptr = reader_get_cur(reader);
1622 else
1624 ptr = start = reader_get_cur(reader);
1625 reader->resume[XmlReadResume_Name] = start;
1626 if (!is_ncnamechar(*ptr)) return NC_E_QNAMECHARACTER;
1629 if (reader->resume[XmlReadResume_Local])
1631 hr = reader_parse_local(reader, local);
1632 if (FAILED(hr)) return hr;
1634 reader_init_strvalue(reader->resume[XmlReadResume_Name],
1635 local->start - reader->resume[XmlReadResume_Name] - 1,
1636 prefix);
1638 else
1640 /* skip prefix part */
1641 while (is_ncnamechar(*ptr))
1643 reader_skipn(reader, 1);
1644 ptr = reader_get_cur(reader);
1647 if (is_reader_pending(reader)) return E_PENDING;
1649 /* got a qualified name */
1650 if (*ptr == ':')
1652 reader_init_strvalue(start, ptr-start, prefix);
1654 /* skip ':' */
1655 reader_skipn(reader, 1);
1656 hr = reader_parse_local(reader, local);
1657 if (FAILED(hr)) return hr;
1659 else
1661 reader_init_strvalue(reader->resume[XmlReadResume_Name], ptr-reader->resume[XmlReadResume_Name], local);
1662 reader_init_strvalue(NULL, 0, prefix);
1666 reader_init_strvalue(start, ptr-start, local);
1668 if (prefix->len)
1669 TRACE("qname %s:%s\n", debugstr_wn(prefix->start, prefix->len), debugstr_wn(local->start, local->len));
1670 else
1671 TRACE("ncname %s\n", debugstr_wn(local->start, local->len));
1673 reader_init_strvalue(prefix->start ? prefix->start : local->start,
1674 /* count ':' too */
1675 (prefix->len ? prefix->len + 1 : 0) + local->len,
1676 qname);
1678 reader->resume[XmlReadResume_Name] = NULL;
1679 reader->resume[XmlReadResume_Local] = NULL;
1681 return S_OK;
1684 /* [12 NS] STag ::= '<' QName (S Attribute)* S? '>'
1685 [14 NS] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>' */
1686 static HRESULT reader_parse_stag(xmlreader *reader, strval *prefix, strval *local, strval *qname, int *empty)
1688 static const WCHAR endW[] = {'/','>',0};
1689 HRESULT hr;
1691 hr = reader_parse_qname(reader, prefix, local, qname);
1692 if (FAILED(hr)) return hr;
1694 reader_skipspaces(reader);
1696 /* empty element */
1697 if ((*empty = !reader_cmp(reader, endW)))
1699 /* skip '/>' */
1700 reader_skipn(reader, 2);
1701 return S_OK;
1704 /* got a start tag */
1705 if (!reader_cmp(reader, gtW))
1707 /* skip '>' */
1708 reader_skipn(reader, 1);
1709 return reader_push_element(reader, qname);
1712 FIXME("only empty elements/start tags without attribute list supported\n");
1713 return E_NOTIMPL;
1716 /* [39] element ::= EmptyElemTag | STag content ETag */
1717 static HRESULT reader_parse_element(xmlreader *reader)
1719 HRESULT hr;
1721 switch (reader->resumestate)
1723 case XmlReadResumeState_Initial:
1724 /* check if we are really on element */
1725 if (reader_cmp(reader, ltW)) return S_FALSE;
1727 /* skip '<' */
1728 reader_skipn(reader, 1);
1730 reader_shrink(reader);
1731 reader->resumestate = XmlReadResumeState_STag;
1732 case XmlReadResumeState_STag:
1734 strval qname, prefix, local;
1735 int empty = 0;
1737 /* this handles empty elements too */
1738 hr = reader_parse_stag(reader, &prefix, &local, &qname, &empty);
1739 if (FAILED(hr)) return hr;
1741 /* FIXME: need to check for defined namespace to reject invalid prefix,
1742 currently reject all prefixes */
1743 if (prefix.len) return NC_E_UNDECLAREDPREFIX;
1745 /* if we got empty element and stack is empty go straight to Misc */
1746 if (empty && list_empty(&reader->elements))
1747 reader->instate = XmlReadInState_MiscEnd;
1748 else
1749 reader->instate = XmlReadInState_Content;
1751 reader->nodetype = XmlNodeType_Element;
1752 reader->resumestate = XmlReadResumeState_Initial;
1753 reader_set_strvalue(reader, StringValue_LocalName, &local);
1754 reader_set_strvalue(reader, StringValue_QualifiedName, &qname);
1755 break;
1757 default:
1758 hr = E_FAIL;
1761 return hr;
1764 /* [13 NS] ETag ::= '</' QName S? '>' */
1765 static HRESULT reader_parse_endtag(xmlreader *reader)
1767 strval prefix, local, qname;
1768 struct element *elem;
1769 HRESULT hr;
1771 /* skip '</' */
1772 reader_skipn(reader, 2);
1774 hr = reader_parse_qname(reader, &prefix, &local, &qname);
1775 if (FAILED(hr)) return hr;
1777 reader_skipspaces(reader);
1779 if (reader_cmp(reader, gtW)) return WC_E_GREATERTHAN;
1781 /* skip '>' */
1782 reader_skipn(reader, 1);
1784 /* Element stack should never be empty at this point, cause we shouldn't get to
1785 content parsing if it's empty. */
1786 elem = LIST_ENTRY(list_head(&reader->elements), struct element, entry);
1787 if (!strval_eq(&elem->qname, &qname)) return WC_E_ELEMENTMATCH;
1789 reader_pop_element(reader);
1791 reader->nodetype = XmlNodeType_EndElement;
1792 reader_set_strvalue(reader, StringValue_LocalName, &local);
1793 reader_set_strvalue(reader, StringValue_QualifiedName, &qname);
1795 return S_OK;
1798 /* [18] CDSect ::= CDStart CData CDEnd
1799 [19] CDStart ::= '<![CDATA['
1800 [20] CData ::= (Char* - (Char* ']]>' Char*))
1801 [21] CDEnd ::= ']]>' */
1802 static HRESULT reader_parse_cdata(xmlreader *reader)
1804 WCHAR *start, *ptr;
1806 if (reader->resume[XmlReadResume_Body])
1808 start = reader->resume[XmlReadResume_Body];
1809 ptr = reader_get_cur(reader);
1811 else
1813 /* skip markup '<![CDATA[' */
1814 reader_skipn(reader, 9);
1815 reader_shrink(reader);
1816 ptr = start = reader_get_cur(reader);
1817 reader->nodetype = XmlNodeType_CDATA;
1818 reader->resume[XmlReadResume_Body] = start;
1819 reader->resumestate = XmlReadResumeState_CDATA;
1820 reader_set_strvalue(reader, StringValue_LocalName, NULL);
1821 reader_set_strvalue(reader, StringValue_QualifiedName, NULL);
1822 reader_set_strvalue(reader, StringValue_Value, NULL);
1825 while (*ptr)
1827 if (*ptr == ']' && *(ptr+1) == ']' && *(ptr+2) == '>')
1829 strval value;
1831 TRACE("%s\n", debugstr_wn(start, ptr-start));
1832 /* skip ']]>' */
1833 reader_skipn(reader, 3);
1834 reader_init_strvalue(start, ptr-start, &value);
1835 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1836 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1837 reader_set_strvalue(reader, StringValue_Value, &value);
1838 reader->resume[XmlReadResume_Body] = NULL;
1839 reader->resumestate = XmlReadResumeState_Initial;
1840 return S_OK;
1842 else
1844 /* Value normalization is not fully implemented, rules are:
1846 - single '\r' -> '\n';
1847 - sequence '\r\n' -> '\n', in this case value length changes;
1849 if (*ptr == '\r') *ptr = '\n';
1850 reader_skipn(reader, 1);
1851 ptr++;
1855 return S_OK;
1858 /* [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1859 [67] Reference ::= EntityRef | CharRef
1860 [68] EntityRef ::= '&' Name ';' */
1861 static HRESULT reader_parse_reference(xmlreader *reader)
1863 FIXME("References not supported\n");
1864 return E_NOTIMPL;
1867 /* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) */
1868 static HRESULT reader_parse_chardata(xmlreader *reader)
1870 FIXME("CharData not supported\n");
1871 return E_NOTIMPL;
1874 /* [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* */
1875 static HRESULT reader_parse_content(xmlreader *reader)
1877 static const WCHAR cdstartW[] = {'<','!','[','C','D','A','T','A','[',0};
1878 static const WCHAR etagW[] = {'<','/',0};
1879 static const WCHAR ampW[] = {'&',0};
1881 if (reader->resumestate != XmlReadResumeState_Initial)
1883 switch (reader->resumestate)
1885 case XmlReadResumeState_CDATA:
1886 return reader_parse_cdata(reader);
1887 case XmlReadResumeState_Comment:
1888 return reader_parse_comment(reader);
1889 case XmlReadResumeState_PIBody:
1890 case XmlReadResumeState_PITarget:
1891 return reader_parse_pi(reader);
1892 default:
1893 ERR("unknown resume state %d\n", reader->resumestate);
1897 reader_shrink(reader);
1899 /* handle end tag here, it indicates end of content as well */
1900 if (!reader_cmp(reader, etagW))
1901 return reader_parse_endtag(reader);
1903 if (!reader_cmp(reader, commentW))
1904 return reader_parse_comment(reader);
1906 if (!reader_cmp(reader, piW))
1907 return reader_parse_pi(reader);
1909 if (!reader_cmp(reader, cdstartW))
1910 return reader_parse_cdata(reader);
1912 if (!reader_cmp(reader, ampW))
1913 return reader_parse_reference(reader);
1915 if (!reader_cmp(reader, ltW))
1916 return reader_parse_element(reader);
1918 /* what's left must be CharData */
1919 return reader_parse_chardata(reader);
1922 static HRESULT reader_parse_nextnode(xmlreader *reader)
1924 HRESULT hr;
1926 while (1)
1928 switch (reader->instate)
1930 /* if it's a first call for a new input we need to detect stream encoding */
1931 case XmlReadInState_Initial:
1933 xml_encoding enc;
1935 hr = readerinput_growraw(reader->input);
1936 if (FAILED(hr)) return hr;
1938 /* try to detect encoding by BOM or data and set input code page */
1939 hr = readerinput_detectencoding(reader->input, &enc);
1940 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
1941 if (FAILED(hr)) return hr;
1943 /* always switch first time cause we have to put something in */
1944 readerinput_switchencoding(reader->input, enc);
1946 /* parse xml declaration */
1947 hr = reader_parse_xmldecl(reader);
1948 if (FAILED(hr)) return hr;
1950 readerinput_shrinkraw(reader->input, -1);
1951 reader->instate = XmlReadInState_Misc_DTD;
1952 if (hr == S_OK) return hr;
1954 break;
1955 case XmlReadInState_Misc_DTD:
1956 hr = reader_parse_misc(reader);
1957 if (FAILED(hr)) return hr;
1959 if (hr == S_FALSE)
1960 reader->instate = XmlReadInState_DTD;
1961 else
1962 return hr;
1963 break;
1964 case XmlReadInState_DTD:
1965 hr = reader_parse_dtd(reader);
1966 if (FAILED(hr)) return hr;
1968 if (hr == S_OK)
1970 reader->instate = XmlReadInState_DTD_Misc;
1971 return hr;
1973 else
1974 reader->instate = XmlReadInState_Element;
1975 break;
1976 case XmlReadInState_DTD_Misc:
1977 hr = reader_parse_misc(reader);
1978 if (FAILED(hr)) return hr;
1980 if (hr == S_FALSE)
1981 reader->instate = XmlReadInState_Element;
1982 else
1983 return hr;
1984 break;
1985 case XmlReadInState_Element:
1986 return reader_parse_element(reader);
1987 case XmlReadInState_Content:
1988 return reader_parse_content(reader);
1989 default:
1990 FIXME("internal state %d not handled\n", reader->instate);
1991 return E_NOTIMPL;
1995 return E_NOTIMPL;
1998 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
2000 xmlreader *This = impl_from_IXmlReader(iface);
2002 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
2004 if (IsEqualGUID(riid, &IID_IUnknown) ||
2005 IsEqualGUID(riid, &IID_IXmlReader))
2007 *ppvObject = iface;
2009 else
2011 FIXME("interface %s not implemented\n", debugstr_guid(riid));
2012 *ppvObject = NULL;
2013 return E_NOINTERFACE;
2016 IXmlReader_AddRef(iface);
2018 return S_OK;
2021 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
2023 xmlreader *This = impl_from_IXmlReader(iface);
2024 ULONG ref = InterlockedIncrement(&This->ref);
2025 TRACE("(%p)->(%d)\n", This, ref);
2026 return ref;
2029 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
2031 xmlreader *This = impl_from_IXmlReader(iface);
2032 LONG ref = InterlockedDecrement(&This->ref);
2034 TRACE("(%p)->(%d)\n", This, ref);
2036 if (ref == 0)
2038 IMalloc *imalloc = This->imalloc;
2039 if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
2040 reader_clear_attrs(This);
2041 reader_clear_elements(This);
2042 reader_free_strvalues(This);
2043 reader_free(This, This);
2044 if (imalloc) IMalloc_Release(imalloc);
2047 return ref;
2050 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
2052 xmlreader *This = impl_from_IXmlReader(iface);
2053 IXmlReaderInput *readerinput;
2054 HRESULT hr;
2056 TRACE("(%p)->(%p)\n", This, input);
2058 if (This->input)
2060 readerinput_release_stream(This->input);
2061 IUnknown_Release(&This->input->IXmlReaderInput_iface);
2062 This->input = NULL;
2065 This->line = This->pos = 0;
2066 reader_clear_elements(This);
2067 This->depth = 0;
2068 This->resumestate = XmlReadResumeState_Initial;
2069 memset(This->resume, 0, sizeof(This->resume));
2071 /* just reset current input */
2072 if (!input)
2074 This->state = XmlReadState_Initial;
2075 return S_OK;
2078 /* now try IXmlReaderInput, ISequentialStream, IStream */
2079 hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&readerinput);
2080 if (hr == S_OK)
2082 if (readerinput->lpVtbl == &xmlreaderinputvtbl)
2083 This->input = impl_from_IXmlReaderInput(readerinput);
2084 else
2086 ERR("got external IXmlReaderInput implementation: %p, vtbl=%p\n",
2087 readerinput, readerinput->lpVtbl);
2088 IUnknown_Release(readerinput);
2089 return E_FAIL;
2094 if (hr != S_OK || !readerinput)
2096 /* create IXmlReaderInput basing on supplied interface */
2097 hr = CreateXmlReaderInputWithEncodingName(input,
2098 NULL, NULL, FALSE, NULL, &readerinput);
2099 if (hr != S_OK) return hr;
2100 This->input = impl_from_IXmlReaderInput(readerinput);
2103 /* set stream for supplied IXmlReaderInput */
2104 hr = readerinput_query_for_stream(This->input);
2105 if (hr == S_OK)
2107 This->state = XmlReadState_Initial;
2108 This->instate = XmlReadInState_Initial;
2111 return hr;
2114 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
2116 xmlreader *This = impl_from_IXmlReader(iface);
2118 TRACE("(%p %u %p)\n", This, property, value);
2120 if (!value) return E_INVALIDARG;
2122 switch (property)
2124 case XmlReaderProperty_DtdProcessing:
2125 *value = This->dtdmode;
2126 break;
2127 case XmlReaderProperty_ReadState:
2128 *value = This->state;
2129 break;
2130 default:
2131 FIXME("Unimplemented property (%u)\n", property);
2132 return E_NOTIMPL;
2135 return S_OK;
2138 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
2140 xmlreader *This = impl_from_IXmlReader(iface);
2142 TRACE("(%p %u %lu)\n", iface, property, value);
2144 switch (property)
2146 case XmlReaderProperty_DtdProcessing:
2147 if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
2148 This->dtdmode = value;
2149 break;
2150 default:
2151 FIXME("Unimplemented property (%u)\n", property);
2152 return E_NOTIMPL;
2155 return S_OK;
2158 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
2160 xmlreader *This = impl_from_IXmlReader(iface);
2161 XmlNodeType oldtype = This->nodetype;
2162 HRESULT hr;
2164 TRACE("(%p)->(%p)\n", This, nodetype);
2166 if (This->state == XmlReadState_Closed) return S_FALSE;
2168 hr = reader_parse_nextnode(This);
2169 if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
2170 This->state = XmlReadState_Interactive;
2171 if (hr == S_OK) *nodetype = This->nodetype;
2173 return hr;
2176 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
2178 xmlreader *This = impl_from_IXmlReader(iface);
2179 TRACE("(%p)->(%p)\n", This, node_type);
2181 /* When we're on attribute always return attribute type, container node type is kept.
2182 Note that container is not necessarily an element, and attribute doesn't mean it's
2183 an attribute in XML spec terms. */
2184 *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
2185 return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
2188 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
2190 xmlreader *This = impl_from_IXmlReader(iface);
2192 TRACE("(%p)\n", This);
2194 if (!This->attr_count) return S_FALSE;
2195 This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
2196 return S_OK;
2199 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
2201 xmlreader *This = impl_from_IXmlReader(iface);
2202 const struct list *next;
2204 TRACE("(%p)\n", This);
2206 if (!This->attr_count) return S_FALSE;
2208 if (!This->attr)
2209 return IXmlReader_MoveToFirstAttribute(iface);
2211 next = list_next(&This->attrs, &This->attr->entry);
2212 if (next)
2213 This->attr = LIST_ENTRY(next, struct attribute, entry);
2215 return next ? S_OK : S_FALSE;
2218 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
2219 LPCWSTR local_name,
2220 LPCWSTR namespaceUri)
2222 FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
2223 return E_NOTIMPL;
2226 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
2228 xmlreader *This = impl_from_IXmlReader(iface);
2230 TRACE("(%p)\n", This);
2232 if (!This->attr_count) return S_FALSE;
2233 This->attr = NULL;
2234 return S_OK;
2237 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *name, UINT *len)
2239 xmlreader *This = impl_from_IXmlReader(iface);
2241 TRACE("(%p)->(%p %p)\n", This, name, len);
2242 *name = This->strvalues[StringValue_QualifiedName].str;
2243 *len = This->strvalues[StringValue_QualifiedName].len;
2244 return S_OK;
2247 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
2248 LPCWSTR *namespaceUri,
2249 UINT *namespaceUri_length)
2251 FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
2252 return E_NOTIMPL;
2255 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface, LPCWSTR *name, UINT *len)
2257 xmlreader *This = impl_from_IXmlReader(iface);
2259 TRACE("(%p)->(%p %p)\n", This, name, len);
2260 *name = This->strvalues[StringValue_LocalName].str;
2261 *len = This->strvalues[StringValue_LocalName].len;
2262 return S_OK;
2265 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
2266 LPCWSTR *prefix,
2267 UINT *prefix_length)
2269 FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
2270 return E_NOTIMPL;
2273 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, const WCHAR **value, UINT *len)
2275 xmlreader *reader = impl_from_IXmlReader(iface);
2276 strval *val = &reader->strvalues[StringValue_Value];
2278 TRACE("(%p)->(%p %p)\n", reader, value, len);
2280 *value = NULL;
2282 if ((reader->nodetype == XmlNodeType_Comment && !val->str) || is_reader_pending(reader))
2284 XmlNodeType type;
2285 HRESULT hr;
2287 hr = IXmlReader_Read(iface, &type);
2288 if (FAILED(hr)) return hr;
2290 /* return if still pending, partially read values are not reported */
2291 if (is_reader_pending(reader)) return E_PENDING;
2294 if (!val->str)
2296 val->str = reader_alloc(reader, (val->len+1)*sizeof(WCHAR));
2297 if (!val->str) return E_OUTOFMEMORY;
2298 memcpy(val->str, val->start, val->len*sizeof(WCHAR));
2299 val->str[val->len] = 0;
2302 *value = val->str;
2303 if (len) *len = val->len;
2304 return S_OK;
2307 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface, WCHAR *buffer, UINT chunk_size, UINT *read)
2309 xmlreader *reader = impl_from_IXmlReader(iface);
2310 strval *val = &reader->strvalues[StringValue_Value];
2311 UINT len;
2313 TRACE("(%p)->(%p %u %p)\n", reader, buffer, chunk_size, read);
2315 /* Value is already allocated, chunked reads are not possible. */
2316 if (val->str) return S_FALSE;
2318 if (val->len)
2320 len = min(chunk_size, val->len);
2321 memcpy(buffer, val->start, len);
2322 val->start += len;
2323 val->len -= len;
2324 if (read) *read = len;
2327 return S_OK;
2330 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
2331 LPCWSTR *baseUri,
2332 UINT *baseUri_length)
2334 FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
2335 return E_NOTIMPL;
2338 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
2340 FIXME("(%p): stub\n", iface);
2341 return E_NOTIMPL;
2344 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
2346 FIXME("(%p): stub\n", iface);
2347 return E_NOTIMPL;
2350 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
2352 xmlreader *This = impl_from_IXmlReader(iface);
2354 TRACE("(%p %p)\n", This, lineNumber);
2356 if (!lineNumber) return E_INVALIDARG;
2358 *lineNumber = This->line;
2360 return S_OK;
2363 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
2365 xmlreader *This = impl_from_IXmlReader(iface);
2367 TRACE("(%p %p)\n", This, linePosition);
2369 if (!linePosition) return E_INVALIDARG;
2371 *linePosition = This->pos;
2373 return S_OK;
2376 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
2378 xmlreader *This = impl_from_IXmlReader(iface);
2380 TRACE("(%p)->(%p)\n", This, count);
2382 if (!count) return E_INVALIDARG;
2384 *count = This->attr_count;
2385 return S_OK;
2388 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
2390 xmlreader *This = impl_from_IXmlReader(iface);
2391 TRACE("(%p)->(%p)\n", This, depth);
2392 *depth = This->depth;
2393 return S_OK;
2396 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
2398 FIXME("(%p): stub\n", iface);
2399 return E_NOTIMPL;
2402 static const struct IXmlReaderVtbl xmlreader_vtbl =
2404 xmlreader_QueryInterface,
2405 xmlreader_AddRef,
2406 xmlreader_Release,
2407 xmlreader_SetInput,
2408 xmlreader_GetProperty,
2409 xmlreader_SetProperty,
2410 xmlreader_Read,
2411 xmlreader_GetNodeType,
2412 xmlreader_MoveToFirstAttribute,
2413 xmlreader_MoveToNextAttribute,
2414 xmlreader_MoveToAttributeByName,
2415 xmlreader_MoveToElement,
2416 xmlreader_GetQualifiedName,
2417 xmlreader_GetNamespaceUri,
2418 xmlreader_GetLocalName,
2419 xmlreader_GetPrefix,
2420 xmlreader_GetValue,
2421 xmlreader_ReadValueChunk,
2422 xmlreader_GetBaseUri,
2423 xmlreader_IsDefault,
2424 xmlreader_IsEmptyElement,
2425 xmlreader_GetLineNumber,
2426 xmlreader_GetLinePosition,
2427 xmlreader_GetAttributeCount,
2428 xmlreader_GetDepth,
2429 xmlreader_IsEOF
2432 /** IXmlReaderInput **/
2433 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
2435 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2437 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
2439 if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
2440 IsEqualGUID(riid, &IID_IUnknown))
2442 *ppvObject = iface;
2444 else
2446 WARN("interface %s not implemented\n", debugstr_guid(riid));
2447 *ppvObject = NULL;
2448 return E_NOINTERFACE;
2451 IUnknown_AddRef(iface);
2453 return S_OK;
2456 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
2458 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2459 ULONG ref = InterlockedIncrement(&This->ref);
2460 TRACE("(%p)->(%d)\n", This, ref);
2461 return ref;
2464 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
2466 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
2467 LONG ref = InterlockedDecrement(&This->ref);
2469 TRACE("(%p)->(%d)\n", This, ref);
2471 if (ref == 0)
2473 IMalloc *imalloc = This->imalloc;
2474 if (This->input) IUnknown_Release(This->input);
2475 if (This->stream) ISequentialStream_Release(This->stream);
2476 if (This->buffer) free_input_buffer(This->buffer);
2477 readerinput_free(This, This->baseuri);
2478 readerinput_free(This, This);
2479 if (imalloc) IMalloc_Release(imalloc);
2482 return ref;
2485 static const struct IUnknownVtbl xmlreaderinputvtbl =
2487 xmlreaderinput_QueryInterface,
2488 xmlreaderinput_AddRef,
2489 xmlreaderinput_Release
2492 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
2494 xmlreader *reader;
2495 int i;
2497 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
2499 if (!IsEqualGUID(riid, &IID_IXmlReader))
2501 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
2502 return E_FAIL;
2505 if (imalloc)
2506 reader = IMalloc_Alloc(imalloc, sizeof(*reader));
2507 else
2508 reader = heap_alloc(sizeof(*reader));
2509 if(!reader) return E_OUTOFMEMORY;
2511 reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
2512 reader->ref = 1;
2513 reader->input = NULL;
2514 reader->state = XmlReadState_Closed;
2515 reader->instate = XmlReadInState_Initial;
2516 reader->resumestate = XmlReadResumeState_Initial;
2517 reader->dtdmode = DtdProcessing_Prohibit;
2518 reader->line = reader->pos = 0;
2519 reader->imalloc = imalloc;
2520 if (imalloc) IMalloc_AddRef(imalloc);
2521 reader->nodetype = XmlNodeType_None;
2522 list_init(&reader->attrs);
2523 reader->attr_count = 0;
2524 reader->attr = NULL;
2525 list_init(&reader->elements);
2526 reader->depth = 0;
2527 memset(reader->resume, 0, sizeof(reader->resume));
2529 for (i = 0; i < StringValue_Last; i++)
2530 reader->strvalues[i] = strval_empty;
2532 *obj = &reader->IXmlReader_iface;
2534 TRACE("returning iface %p\n", *obj);
2536 return S_OK;
2539 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
2540 IMalloc *imalloc,
2541 LPCWSTR encoding,
2542 BOOL hint,
2543 LPCWSTR base_uri,
2544 IXmlReaderInput **ppInput)
2546 xmlreaderinput *readerinput;
2547 HRESULT hr;
2549 TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
2550 hint, wine_dbgstr_w(base_uri), ppInput);
2552 if (!stream || !ppInput) return E_INVALIDARG;
2554 if (imalloc)
2555 readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
2556 else
2557 readerinput = heap_alloc(sizeof(*readerinput));
2558 if(!readerinput) return E_OUTOFMEMORY;
2560 readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinputvtbl;
2561 readerinput->ref = 1;
2562 readerinput->imalloc = imalloc;
2563 readerinput->stream = NULL;
2564 if (imalloc) IMalloc_AddRef(imalloc);
2565 readerinput->encoding = parse_encoding_name(encoding, -1);
2566 readerinput->hint = hint;
2567 readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
2568 readerinput->pending = 0;
2570 hr = alloc_input_buffer(readerinput);
2571 if (hr != S_OK)
2573 readerinput_free(readerinput, readerinput->baseuri);
2574 readerinput_free(readerinput, readerinput);
2575 if (imalloc) IMalloc_Release(imalloc);
2576 return hr;
2578 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
2580 *ppInput = &readerinput->IXmlReaderInput_iface;
2582 TRACE("returning iface %p\n", *ppInput);
2584 return S_OK;