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
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);
49 XmlReadInState_Initial,
50 XmlReadInState_XmlDecl,
51 XmlReadInState_Misc_DTD,
53 XmlReadInState_DTD_Misc,
54 XmlReadInState_Element
55 } XmlReaderInternalState;
59 StringValue_LocalName,
60 StringValue_QualifiedName,
63 } XmlReaderStringValue;
65 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
66 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
68 static const WCHAR dblquoteW[] = {'\"',0};
69 static const WCHAR quoteW[] = {'\'',0};
71 struct xml_encoding_data
78 static const struct xml_encoding_data xml_encoding_map[] = {
79 { utf16W, XmlEncoding_UTF16, ~0 },
80 { utf8W, XmlEncoding_UTF8, CP_UTF8 }
87 unsigned int allocated;
91 typedef struct input_buffer input_buffer;
95 IXmlReaderInput IXmlReaderInput_iface;
97 /* reference passed on IXmlReaderInput creation, is kept when input is created */
100 xml_encoding encoding;
103 /* stream reference set after SetInput() call from reader,
104 stored as sequential stream, cause currently
105 optimizations possible with IStream aren't implemented */
106 ISequentialStream *stream;
107 input_buffer *buffer;
116 static WCHAR emptyW[] = {0};
117 static const strval strval_empty = {emptyW, 0};
128 IXmlReader IXmlReader_iface;
130 xmlreaderinput *input;
133 XmlReaderInternalState instate;
134 XmlNodeType nodetype;
135 DtdProcessing dtdmode;
136 UINT line, pos; /* reader position in XML stream */
137 struct list attrs; /* attributes list for current node */
138 struct attribute *attr; /* current attribute */
140 strval strvalues[StringValue_Last];
145 encoded_buffer utf16;
146 encoded_buffer encoded;
148 xmlreaderinput *input;
151 static inline xmlreader *impl_from_IXmlReader(IXmlReader *iface)
153 return CONTAINING_RECORD(iface, xmlreader, IXmlReader_iface);
156 static inline xmlreaderinput *impl_from_IXmlReaderInput(IXmlReaderInput *iface)
158 return CONTAINING_RECORD(iface, xmlreaderinput, IXmlReaderInput_iface);
161 static inline void *m_alloc(IMalloc *imalloc, size_t len)
164 return IMalloc_Alloc(imalloc, len);
166 return heap_alloc(len);
169 static inline void *m_realloc(IMalloc *imalloc, void *mem, size_t len)
172 return IMalloc_Realloc(imalloc, mem, len);
174 return heap_realloc(mem, len);
177 static inline void m_free(IMalloc *imalloc, void *mem)
180 IMalloc_Free(imalloc, mem);
185 /* reader memory allocation functions */
186 static inline void *reader_alloc(xmlreader *reader, size_t len)
188 return m_alloc(reader->imalloc, len);
191 static inline void reader_free(xmlreader *reader, void *mem)
193 m_free(reader->imalloc, mem);
196 /* reader input memory allocation functions */
197 static inline void *readerinput_alloc(xmlreaderinput *input, size_t len)
199 return m_alloc(input->imalloc, len);
202 static inline void *readerinput_realloc(xmlreaderinput *input, void *mem, size_t len)
204 return m_realloc(input->imalloc, mem, len);
207 static inline void readerinput_free(xmlreaderinput *input, void *mem)
209 m_free(input->imalloc, mem);
212 static inline WCHAR *readerinput_strdupW(xmlreaderinput *input, const WCHAR *str)
219 size = (strlenW(str)+1)*sizeof(WCHAR);
220 ret = readerinput_alloc(input, size);
221 if (ret) memcpy(ret, str, size);
227 static void reader_clear_attrs(xmlreader *reader)
229 struct attribute *attr, *attr2;
230 LIST_FOR_EACH_ENTRY_SAFE(attr, attr2, &reader->attrs, struct attribute, entry)
232 reader_free(reader, attr);
234 list_init(&reader->attrs);
235 reader->attr_count = 0;
238 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
239 while we are on a node with attributes */
240 static HRESULT reader_add_attr(xmlreader *reader, strval *localname, strval *value)
242 struct attribute *attr;
244 attr = reader_alloc(reader, sizeof(*attr));
245 if (!attr) return E_OUTOFMEMORY;
247 attr->localname = *localname;
248 attr->value = *value;
249 list_add_tail(&reader->attrs, &attr->entry);
250 reader->attr_count++;
255 static void reader_free_strvalue(xmlreader *reader, XmlReaderStringValue type)
257 strval *v = &reader->strvalues[type];
259 if (v->str != strval_empty.str)
261 reader_free(reader, v->str);
266 static void reader_free_strvalues(xmlreader *reader)
269 for (type = 0; type < StringValue_Last; type++)
270 reader_free_strvalue(reader, type);
273 /* always make a copy, cause strings are supposed to be null terminated */
274 static void reader_set_strvalue(xmlreader *reader, XmlReaderStringValue type, const strval *value)
276 strval *v = &reader->strvalues[type];
278 reader_free_strvalue(reader, type);
279 if (value->str == strval_empty.str)
283 v->str = reader_alloc(reader, (value->len + 1)*sizeof(WCHAR));
284 memcpy(v->str, value->str, value->len*sizeof(WCHAR));
285 v->str[value->len] = 0;
290 static HRESULT init_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
292 const int initial_len = 0x2000;
293 buffer->data = readerinput_alloc(input, initial_len);
294 if (!buffer->data) return E_OUTOFMEMORY;
296 memset(buffer->data, 0, 4);
297 buffer->cur = buffer->data;
298 buffer->allocated = initial_len;
304 static void free_encoded_buffer(xmlreaderinput *input, encoded_buffer *buffer)
306 readerinput_free(input, buffer->data);
309 static HRESULT get_code_page(xml_encoding encoding, UINT *cp)
311 if (encoding == XmlEncoding_Unknown)
313 FIXME("unsupported encoding %d\n", encoding);
317 *cp = xml_encoding_map[encoding].cp;
322 static xml_encoding parse_encoding_name(const WCHAR *name, int len)
326 if (!name) return XmlEncoding_Unknown;
329 max = sizeof(xml_encoding_map)/sizeof(struct xml_encoding_data) - 1;
336 c = strncmpiW(xml_encoding_map[n].name, name, len);
338 c = strcmpiW(xml_encoding_map[n].name, name);
340 return xml_encoding_map[n].enc;
348 return XmlEncoding_Unknown;
351 static HRESULT alloc_input_buffer(xmlreaderinput *input)
353 input_buffer *buffer;
356 input->buffer = NULL;
358 buffer = readerinput_alloc(input, sizeof(*buffer));
359 if (!buffer) return E_OUTOFMEMORY;
361 buffer->input = input;
362 buffer->code_page = ~0; /* code page is unknown at this point */
363 hr = init_encoded_buffer(input, &buffer->utf16);
365 readerinput_free(input, buffer);
369 hr = init_encoded_buffer(input, &buffer->encoded);
371 free_encoded_buffer(input, &buffer->utf16);
372 readerinput_free(input, buffer);
376 input->buffer = buffer;
380 static void free_input_buffer(input_buffer *buffer)
382 free_encoded_buffer(buffer->input, &buffer->encoded);
383 free_encoded_buffer(buffer->input, &buffer->utf16);
384 readerinput_free(buffer->input, buffer);
387 static void readerinput_release_stream(xmlreaderinput *readerinput)
389 if (readerinput->stream) {
390 ISequentialStream_Release(readerinput->stream);
391 readerinput->stream = NULL;
395 /* Queries already stored interface for IStream/ISequentialStream.
396 Interface supplied on creation will be overwritten */
397 static HRESULT readerinput_query_for_stream(xmlreaderinput *readerinput)
401 readerinput_release_stream(readerinput);
402 hr = IUnknown_QueryInterface(readerinput->input, &IID_IStream, (void**)&readerinput->stream);
404 hr = IUnknown_QueryInterface(readerinput->input, &IID_ISequentialStream, (void**)&readerinput->stream);
409 /* reads a chunk to raw buffer */
410 static HRESULT readerinput_growraw(xmlreaderinput *readerinput)
412 encoded_buffer *buffer = &readerinput->buffer->encoded;
413 /* to make sure aligned length won't exceed allocated length */
414 ULONG len = buffer->allocated - buffer->written - 4;
418 /* always try to get aligned to 4 bytes, so the only case we can get partially read characters is
419 variable width encodings like UTF-8 */
420 len = (len + 3) & ~3;
421 /* try to use allocated space or grow */
422 if (buffer->allocated - buffer->written < len)
424 buffer->allocated *= 2;
425 buffer->data = readerinput_realloc(readerinput, buffer->data, buffer->allocated);
426 len = buffer->allocated - buffer->written;
429 hr = ISequentialStream_Read(readerinput->stream, buffer->data + buffer->written, len, &read);
430 if (FAILED(hr)) return hr;
431 TRACE("requested %d, read %d, ret 0x%08x\n", len, read, hr);
432 buffer->written += read;
437 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
438 static void readerinput_grow(xmlreaderinput *readerinput, int length)
440 encoded_buffer *buffer = &readerinput->buffer->utf16;
442 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
443 if (buffer->allocated < buffer->written + length + 4)
445 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
446 buffer->data = readerinput_realloc(readerinput, buffer->data, grown_size);
447 buffer->allocated = grown_size;
451 static HRESULT readerinput_detectencoding(xmlreaderinput *readerinput, xml_encoding *enc)
453 encoded_buffer *buffer = &readerinput->buffer->encoded;
454 static char startA[] = {'<','?'};
455 static char commentA[] = {'<','!'};
456 static WCHAR startW[] = {'<','?'};
457 static WCHAR commentW[] = {'<','!'};
458 static char utf8bom[] = {0xef,0xbb,0xbf};
459 static char utf16lebom[] = {0xff,0xfe};
461 *enc = XmlEncoding_Unknown;
463 if (buffer->written <= 3) return MX_E_INPUTEND;
465 /* try start symbols if we have enough data to do that, input buffer should contain
466 first chunk already */
467 if (!memcmp(buffer->data, startA, sizeof(startA)) ||
468 !memcmp(buffer->data, commentA, sizeof(commentA)))
469 *enc = XmlEncoding_UTF8;
470 else if (!memcmp(buffer->data, startW, sizeof(startW)) ||
471 !memcmp(buffer->data, commentW, sizeof(commentW)))
472 *enc = XmlEncoding_UTF16;
473 /* try with BOM now */
474 else if (!memcmp(buffer->data, utf8bom, sizeof(utf8bom)))
476 buffer->cur += sizeof(utf8bom);
477 *enc = XmlEncoding_UTF8;
479 else if (!memcmp(buffer->data, utf16lebom, sizeof(utf16lebom)))
481 buffer->cur += sizeof(utf16lebom);
482 *enc = XmlEncoding_UTF16;
488 static int readerinput_get_utf8_convlen(xmlreaderinput *readerinput)
490 encoded_buffer *buffer = &readerinput->buffer->encoded;
491 int len = buffer->written;
493 /* complete single byte char */
494 if (!(buffer->data[len-1] & 0x80)) return len;
496 /* find start byte of multibyte char */
497 while (--len && !(buffer->data[len] & 0xc0))
503 /* Returns byte length of complete char sequence for buffer code page,
504 it's relative to current buffer position which is currently used for BOM handling
506 static int readerinput_get_convlen(xmlreaderinput *readerinput)
508 encoded_buffer *buffer = &readerinput->buffer->encoded;
511 if (readerinput->buffer->code_page == CP_UTF8)
512 len = readerinput_get_utf8_convlen(readerinput);
514 len = buffer->written;
516 TRACE("%d\n", len - (int)(buffer->cur - buffer->data));
517 return len - (buffer->cur - buffer->data);
520 /* It's possible that raw buffer has some leftovers from last conversion - some char
521 sequence that doesn't represent a full code point. Length argument should be calculated with
522 readerinput_get_convlen(), if it's -1 it will be calculated here. */
523 static void readerinput_shrinkraw(xmlreaderinput *readerinput, int len)
525 encoded_buffer *buffer = &readerinput->buffer->encoded;
528 len = readerinput_get_convlen(readerinput);
530 memmove(buffer->data, buffer->cur + (buffer->written - len), len);
531 /* everything below cur is lost too */
532 buffer->written -= len + (buffer->cur - buffer->data);
533 /* after this point we don't need cur pointer really,
534 it's used only to mark where actual data begins when first chunk is read */
535 buffer->cur = buffer->data;
538 /* note that raw buffer content is kept */
539 static void readerinput_switchencoding(xmlreaderinput *readerinput, xml_encoding enc)
541 encoded_buffer *src = &readerinput->buffer->encoded;
542 encoded_buffer *dest = &readerinput->buffer->utf16;
548 hr = get_code_page(enc, &cp);
549 if (FAILED(hr)) return;
551 readerinput->buffer->code_page = cp;
552 len = readerinput_get_convlen(readerinput);
554 TRACE("switching to cp %d\n", cp);
556 /* just copy in this case */
557 if (enc == XmlEncoding_UTF16)
559 readerinput_grow(readerinput, len);
560 memcpy(dest->data, src->cur, len);
561 dest->written += len*sizeof(WCHAR);
565 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
566 readerinput_grow(readerinput, dest_len);
567 ptr = (WCHAR*)dest->data;
568 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
570 dest->written += dest_len*sizeof(WCHAR);
573 /* shrinks parsed data a buffer begins with */
574 static void reader_shrink(xmlreader *reader)
576 encoded_buffer *buffer = &reader->input->buffer->utf16;
578 /* avoid to move too often using threshold shrink length */
579 if (buffer->cur - buffer->data > buffer->written / 2)
581 buffer->written -= buffer->cur - buffer->data;
582 memmove(buffer->data, buffer->cur, buffer->written);
583 buffer->cur = buffer->data;
584 *(WCHAR*)&buffer->cur[buffer->written] = 0;
588 /* This is a normal way for reader to get new data converted from raw buffer to utf16 buffer.
589 It won't attempt to shrink but will grow destination buffer if needed */
590 static void reader_more(xmlreader *reader)
592 xmlreaderinput *readerinput = reader->input;
593 encoded_buffer *src = &readerinput->buffer->encoded;
594 encoded_buffer *dest = &readerinput->buffer->utf16;
595 UINT cp = readerinput->buffer->code_page;
599 /* get some raw data from stream first */
600 readerinput_growraw(readerinput);
601 len = readerinput_get_convlen(readerinput);
603 /* just copy for UTF-16 case */
606 readerinput_grow(readerinput, len);
607 memcpy(dest->data, src->cur, len);
608 dest->written += len*sizeof(WCHAR);
612 dest_len = MultiByteToWideChar(cp, 0, src->cur, len, NULL, 0);
613 readerinput_grow(readerinput, dest_len);
614 ptr = (WCHAR*)dest->data;
615 MultiByteToWideChar(cp, 0, src->cur, len, ptr, dest_len);
617 dest->written += dest_len*sizeof(WCHAR);
618 /* get rid of processed data */
619 readerinput_shrinkraw(readerinput, len);
622 static inline WCHAR *reader_get_cur(xmlreader *reader)
624 WCHAR *ptr = (WCHAR*)reader->input->buffer->utf16.cur;
625 if (!*ptr) reader_more(reader);
629 static int reader_cmp(xmlreader *reader, const WCHAR *str)
631 const WCHAR *ptr = reader_get_cur(reader);
632 return strncmpW(str, ptr, strlenW(str));
635 /* moves cursor n WCHARs forward */
636 static void reader_skipn(xmlreader *reader, int n)
638 encoded_buffer *buffer = &reader->input->buffer->utf16;
639 const WCHAR *ptr = reader_get_cur(reader);
641 while (*ptr++ && n--)
643 buffer->cur += sizeof(WCHAR);
648 static inline int is_wchar_space(WCHAR ch)
650 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
653 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
654 static int reader_skipspaces(xmlreader *reader)
656 encoded_buffer *buffer = &reader->input->buffer->utf16;
657 const WCHAR *ptr = reader_get_cur(reader), *start = ptr;
659 while (is_wchar_space(*ptr))
661 buffer->cur += sizeof(WCHAR);
664 else if (*ptr == '\n')
677 /* [26] VersionNum ::= '1.' [0-9]+ */
678 static HRESULT reader_parse_versionnum(xmlreader *reader, strval *val)
680 WCHAR *ptr, *ptr2, *start = reader_get_cur(reader);
681 static const WCHAR onedotW[] = {'1','.',0};
683 if (reader_cmp(reader, onedotW)) return WC_E_XMLDECL;
685 reader_skipn(reader, 2);
687 ptr2 = ptr = reader_get_cur(reader);
688 while (*ptr >= '0' && *ptr <= '9')
691 if (ptr2 == ptr) return WC_E_DIGIT;
692 TRACE("version=%s\n", debugstr_wn(start, ptr-start));
694 val->len = ptr-start;
695 reader_skipn(reader, ptr-ptr2);
699 /* [25] Eq ::= S? '=' S? */
700 static HRESULT reader_parse_eq(xmlreader *reader)
702 static const WCHAR eqW[] = {'=',0};
703 reader_skipspaces(reader);
704 if (reader_cmp(reader, eqW)) return WC_E_EQUAL;
706 reader_skipn(reader, 1);
707 reader_skipspaces(reader);
711 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
712 static HRESULT reader_parse_versioninfo(xmlreader *reader)
714 static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
718 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
720 if (reader_cmp(reader, versionW)) return WC_E_XMLDECL;
721 name.str = reader_get_cur(reader);
724 reader_skipn(reader, 7);
726 hr = reader_parse_eq(reader);
727 if (FAILED(hr)) return hr;
729 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
732 reader_skipn(reader, 1);
734 hr = reader_parse_versionnum(reader, &val);
735 if (FAILED(hr)) return hr;
737 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
741 reader_skipn(reader, 1);
743 return reader_add_attr(reader, &name, &val);
746 /* ([A-Za-z0-9._] | '-') */
747 static inline int is_wchar_encname(WCHAR ch)
749 return ((ch >= 'A' && ch <= 'Z') ||
750 (ch >= 'a' && ch <= 'z') ||
751 (ch >= '0' && ch <= '9') ||
752 (ch == '.') || (ch == '_') ||
756 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
757 static HRESULT reader_parse_encname(xmlreader *reader, strval *val)
759 WCHAR *start = reader_get_cur(reader), *ptr;
763 if ((*start < 'A' || *start > 'Z') && (*start < 'a' || *start > 'z'))
767 while (is_wchar_encname(*++ptr))
771 enc = parse_encoding_name(start, len);
772 TRACE("encoding name %s\n", debugstr_wn(start, len));
776 if (enc == XmlEncoding_Unknown)
779 /* skip encoding name */
780 reader_skipn(reader, len);
784 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
785 static HRESULT reader_parse_encdecl(xmlreader *reader)
787 static const WCHAR encodingW[] = {'e','n','c','o','d','i','n','g',0};
791 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
793 if (reader_cmp(reader, encodingW)) return S_FALSE;
794 name.str = reader_get_cur(reader);
796 /* skip 'encoding' */
797 reader_skipn(reader, 8);
799 hr = reader_parse_eq(reader);
800 if (FAILED(hr)) return hr;
802 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
805 reader_skipn(reader, 1);
807 hr = reader_parse_encname(reader, &val);
808 if (FAILED(hr)) return hr;
810 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
814 reader_skipn(reader, 1);
816 return reader_add_attr(reader, &name, &val);
819 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
820 static HRESULT reader_parse_sddecl(xmlreader *reader)
822 static const WCHAR standaloneW[] = {'s','t','a','n','d','a','l','o','n','e',0};
823 static const WCHAR yesW[] = {'y','e','s',0};
824 static const WCHAR noW[] = {'n','o',0};
829 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
831 if (reader_cmp(reader, standaloneW)) return S_FALSE;
832 name.str = reader_get_cur(reader);
834 /* skip 'standalone' */
835 reader_skipn(reader, 10);
837 hr = reader_parse_eq(reader);
838 if (FAILED(hr)) return hr;
840 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
843 reader_skipn(reader, 1);
845 if (reader_cmp(reader, yesW) && reader_cmp(reader, noW))
848 start = reader_get_cur(reader);
849 /* skip 'yes'|'no' */
850 reader_skipn(reader, reader_cmp(reader, yesW) ? 2 : 3);
851 ptr = reader_get_cur(reader);
852 TRACE("standalone=%s\n", debugstr_wn(start, ptr-start));
856 if (reader_cmp(reader, quoteW) && reader_cmp(reader, dblquoteW))
859 reader_skipn(reader, 1);
861 return reader_add_attr(reader, &name, &val);
864 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
865 static HRESULT reader_parse_xmldecl(xmlreader *reader)
867 static const WCHAR xmldeclW[] = {'<','?','x','m','l',' ',0};
868 static const WCHAR declcloseW[] = {'?','>',0};
871 /* check if we have "<?xml " */
872 if (reader_cmp(reader, xmldeclW)) return S_FALSE;
874 reader_skipn(reader, 5);
875 hr = reader_parse_versioninfo(reader);
879 hr = reader_parse_encdecl(reader);
883 hr = reader_parse_sddecl(reader);
887 reader_skipspaces(reader);
888 if (reader_cmp(reader, declcloseW)) return WC_E_XMLDECL;
889 reader_skipn(reader, 2);
891 reader->nodetype = XmlNodeType_XmlDeclaration;
892 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
893 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
894 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
899 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
900 static HRESULT reader_parse_comment(xmlreader *reader)
905 reader_skipn(reader, 4);
906 reader_shrink(reader);
907 ptr = start = reader_get_cur(reader);
917 strval value = { start, ptr-start };
919 TRACE("%s\n", debugstr_wn(start, ptr-start));
921 reader_skipn(reader, 3);
922 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
923 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
924 reader_set_strvalue(reader, StringValue_Value, &value);
925 reader->nodetype = XmlNodeType_Comment;
939 reader_skipn(reader, 1);
940 ptr = reader_get_cur(reader);
944 return MX_E_INPUTEND;
947 /* [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] */
948 static inline int is_char(WCHAR ch)
950 return (ch == '\t') || (ch == '\r') || (ch == '\n') ||
951 (ch >= 0x20 && ch <= 0xd7ff) ||
952 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
953 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
954 (ch >= 0xe000 && ch <= 0xfffd);
957 /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
958 static inline int is_pubchar(WCHAR ch)
960 return (ch == ' ') ||
961 (ch >= 'a' && ch <= 'z') ||
962 (ch >= 'A' && ch <= 'Z') ||
963 (ch >= '0' && ch <= '9') ||
964 (ch >= '-' && ch <= ';') || /* '()*+,-./:; */
965 (ch == '=') || (ch == '?') ||
966 (ch == '@') || (ch == '!') ||
967 (ch >= '#' && ch <= '%') || /* #$% */
968 (ch == '_') || (ch == '\r') || (ch == '\n');
971 static inline int is_namestartchar(WCHAR ch)
973 return (ch == ':') || (ch >= 'A' && ch <= 'Z') ||
974 (ch == '_') || (ch >= 'a' && ch <= 'z') ||
975 (ch >= 0xc0 && ch <= 0xd6) ||
976 (ch >= 0xd8 && ch <= 0xf6) ||
977 (ch >= 0xf8 && ch <= 0x2ff) ||
978 (ch >= 0x370 && ch <= 0x37d) ||
979 (ch >= 0x37f && ch <= 0x1fff) ||
980 (ch >= 0x200c && ch <= 0x200d) ||
981 (ch >= 0x2070 && ch <= 0x218f) ||
982 (ch >= 0x2c00 && ch <= 0x2fef) ||
983 (ch >= 0x3001 && ch <= 0xd7ff) ||
984 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
985 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
986 (ch >= 0xf900 && ch <= 0xfdcf) ||
987 (ch >= 0xfdf0 && ch <= 0xfffd);
990 static inline int is_namechar(WCHAR ch)
992 return (ch == ':') || (ch >= 'A' && ch <= 'Z') ||
993 (ch == '_') || (ch >= 'a' && ch <= 'z') ||
994 (ch == '-') || (ch == '.') ||
995 (ch >= '0' && ch <= '9') ||
997 (ch >= 0xc0 && ch <= 0xd6) ||
998 (ch >= 0xd8 && ch <= 0xf6) ||
999 (ch >= 0xf8 && ch <= 0x2ff) ||
1000 (ch >= 0x300 && ch <= 0x36f) ||
1001 (ch >= 0x370 && ch <= 0x37d) ||
1002 (ch >= 0x37f && ch <= 0x1fff) ||
1003 (ch >= 0x200c && ch <= 0x200d) ||
1004 (ch >= 0x203f && ch <= 0x2040) ||
1005 (ch >= 0x2070 && ch <= 0x218f) ||
1006 (ch >= 0x2c00 && ch <= 0x2fef) ||
1007 (ch >= 0x3001 && ch <= 0xd7ff) ||
1008 (ch >= 0xd800 && ch <= 0xdbff) || /* high surrogate */
1009 (ch >= 0xdc00 && ch <= 0xdfff) || /* low surrogate */
1010 (ch >= 0xf900 && ch <= 0xfdcf) ||
1011 (ch >= 0xfdf0 && ch <= 0xfffd);
1014 /* [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
1015 [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
1016 [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
1017 [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
1018 [5] Name ::= NameStartChar (NameChar)* */
1019 static HRESULT reader_parse_name(xmlreader *reader, strval *name)
1021 WCHAR *ptr, *start = reader_get_cur(reader);
1024 if (!is_namestartchar(*ptr)) return WC_E_NAMECHARACTER;
1026 while (is_namechar(*ptr))
1028 reader_skipn(reader, 1);
1029 ptr = reader_get_cur(reader);
1032 TRACE("name %s:%d\n", debugstr_wn(start, ptr-start), (int)(ptr-start));
1034 name->len = ptr-start;
1039 /* [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) */
1040 static HRESULT reader_parse_pitarget(xmlreader *reader, strval *target)
1042 static const WCHAR xmlW[] = {'x','m','l'};
1047 hr = reader_parse_name(reader, &name);
1048 if (FAILED(hr)) return WC_E_PI;
1050 /* now that we got name check for illegal content */
1051 if (name.len == 3 && !strncmpiW(name.str, xmlW, 3))
1052 return WC_E_LEADINGXML;
1054 /* PITarget can't be a qualified name */
1055 for (i = 0; i < name.len; i++)
1056 if (name.str[i] == ':')
1057 return i ? NC_E_NAMECOLON : WC_E_PI;
1059 TRACE("pitarget %s:%d\n", debugstr_wn(name.str, name.len), name.len);
1064 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
1065 static HRESULT reader_parse_pi(xmlreader *reader)
1072 reader_skipn(reader, 2);
1073 reader_shrink(reader);
1075 hr = reader_parse_pitarget(reader, &target);
1076 if (FAILED(hr)) return hr;
1078 ptr = reader_get_cur(reader);
1079 /* exit earlier if there's no content */
1080 if (ptr[0] == '?' && ptr[1] == '>')
1083 reader_skipn(reader, 2);
1084 reader->nodetype = XmlNodeType_ProcessingInstruction;
1085 reader_set_strvalue(reader, StringValue_LocalName, &target);
1086 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1087 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1091 /* now at least a single space char should be there */
1092 if (!is_wchar_space(*ptr)) return WC_E_WHITESPACE;
1093 reader_skipspaces(reader);
1095 ptr = start = reader_get_cur(reader);
1103 strval value = { start, ptr-start };
1105 TRACE("%s\n", debugstr_wn(start, ptr-start));
1107 reader_skipn(reader, 2);
1108 reader->nodetype = XmlNodeType_ProcessingInstruction;
1109 reader_set_strvalue(reader, StringValue_LocalName, &target);
1110 reader_set_strvalue(reader, StringValue_QualifiedName, &target);
1111 reader_set_strvalue(reader, StringValue_Value, &value);
1117 reader_more(reader);
1122 reader_skipn(reader, 1);
1123 ptr = reader_get_cur(reader);
1130 /* This one is used to parse significant whitespace nodes, like in Misc production */
1131 static HRESULT reader_parse_whitespace(xmlreader *reader)
1135 reader_shrink(reader);
1136 start = reader_get_cur(reader);
1138 reader_skipspaces(reader);
1139 ptr = reader_get_cur(reader);
1140 TRACE("%s\n", debugstr_wn(start, ptr-start));
1142 reader->nodetype = XmlNodeType_Whitespace;
1143 reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
1144 reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
1145 reader_set_strvalue(reader, StringValue_Value, &strval_empty);
1149 /* [27] Misc ::= Comment | PI | S */
1150 static HRESULT reader_parse_misc(xmlreader *reader)
1152 HRESULT hr = S_FALSE;
1156 static const WCHAR commentW[] = {'<','!','-','-',0};
1157 static const WCHAR piW[] = {'<','?',0};
1158 const WCHAR *cur = reader_get_cur(reader);
1160 if (is_wchar_space(*cur))
1161 hr = reader_parse_whitespace(reader);
1162 else if (!reader_cmp(reader, commentW))
1163 hr = reader_parse_comment(reader);
1164 else if (!reader_cmp(reader, piW))
1165 hr = reader_parse_pi(reader);
1169 if (hr != S_FALSE) return hr;
1175 /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
1176 static HRESULT reader_parse_sys_literal(xmlreader *reader, strval *literal)
1178 WCHAR *start = reader_get_cur(reader), *cur, quote;
1180 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1183 reader_skipn(reader, 1);
1185 cur = start = reader_get_cur(reader);
1186 while (is_char(*cur) && *cur != quote)
1188 reader_skipn(reader, 1);
1189 cur = reader_get_cur(reader);
1191 if (*cur == quote) reader_skipn(reader, 1);
1193 literal->str = start;
1194 literal->len = cur-start;
1195 TRACE("%s\n", debugstr_wn(start, cur-start));
1199 /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1200 [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1201 static HRESULT reader_parse_pub_literal(xmlreader *reader, strval *literal)
1203 WCHAR *start = reader_get_cur(reader), *cur, quote;
1205 if (*start != '"' && *start != '\'') return WC_E_QUOTE;
1208 reader_skipn(reader, 1);
1211 while (is_pubchar(*cur) && *cur != quote)
1213 reader_skipn(reader, 1);
1214 cur = reader_get_cur(reader);
1217 literal->str = start;
1218 literal->len = cur-start;
1219 TRACE("%s\n", debugstr_wn(start, cur-start));
1223 /* [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral */
1224 static HRESULT reader_parse_externalid(xmlreader *reader)
1226 static WCHAR systemW[] = {'S','Y','S','T','E','M',0};
1227 static WCHAR publicW[] = {'P','U','B','L','I','C',0};
1232 if (reader_cmp(reader, systemW))
1234 if (reader_cmp(reader, publicW))
1241 reader_skipn(reader, 6);
1242 cnt = reader_skipspaces(reader);
1243 if (!cnt) return WC_E_WHITESPACE;
1245 hr = reader_parse_pub_literal(reader, &pub);
1246 if (FAILED(hr)) return hr;
1249 name.len = strlenW(publicW);
1250 return reader_add_attr(reader, &name, &pub);
1258 reader_skipn(reader, 6);
1259 cnt = reader_skipspaces(reader);
1260 if (!cnt) return WC_E_WHITESPACE;
1262 hr = reader_parse_sys_literal(reader, &sys);
1263 if (FAILED(hr)) return hr;
1266 name.len = strlenW(systemW);
1267 return reader_add_attr(reader, &name, &sys);
1273 /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */
1274 static HRESULT reader_parse_dtd(xmlreader *reader)
1276 static const WCHAR doctypeW[] = {'<','!','D','O','C','T','Y','P','E',0};
1281 /* check if we have "<!DOCTYPE" */
1282 if (reader_cmp(reader, doctypeW)) return S_FALSE;
1283 reader_shrink(reader);
1285 /* DTD processing is not allowed by default */
1286 if (reader->dtdmode == DtdProcessing_Prohibit) return WC_E_DTDPROHIBITED;
1288 reader_skipn(reader, 9);
1289 if (!reader_skipspaces(reader)) return WC_E_WHITESPACE;
1292 hr = reader_parse_name(reader, &name);
1293 if (FAILED(hr)) return WC_E_DECLDOCTYPE;
1295 reader_skipspaces(reader);
1297 hr = reader_parse_externalid(reader);
1298 if (FAILED(hr)) return hr;
1300 reader_skipspaces(reader);
1302 cur = reader_get_cur(reader);
1305 FIXME("internal subset parsing not implemented\n");
1310 reader_skipn(reader, 1);
1312 reader->nodetype = XmlNodeType_DocumentType;
1313 reader_set_strvalue(reader, StringValue_LocalName, &name);
1314 reader_set_strvalue(reader, StringValue_QualifiedName, &name);
1319 /* [39] element ::= EmptyElemTag | STag content ETag */
1320 static HRESULT reader_parse_element(xmlreader *reader)
1322 FIXME("element parsing not implemented\n");
1326 static HRESULT reader_parse_nextnode(xmlreader *reader)
1332 switch (reader->instate)
1334 /* if it's a first call for a new input we need to detect stream encoding */
1335 case XmlReadInState_Initial:
1339 hr = readerinput_growraw(reader->input);
1340 if (FAILED(hr)) return hr;
1342 /* try to detect encoding by BOM or data and set input code page */
1343 hr = readerinput_detectencoding(reader->input, &enc);
1344 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map[enc].name), hr);
1345 if (FAILED(hr)) return hr;
1347 /* always switch first time cause we have to put something in */
1348 readerinput_switchencoding(reader->input, enc);
1350 /* parse xml declaration */
1351 hr = reader_parse_xmldecl(reader);
1352 if (FAILED(hr)) return hr;
1354 readerinput_shrinkraw(reader->input, -1);
1355 reader->instate = XmlReadInState_Misc_DTD;
1356 if (hr == S_OK) return hr;
1359 case XmlReadInState_Misc_DTD:
1360 hr = reader_parse_misc(reader);
1361 if (FAILED(hr)) return hr;
1364 reader->instate = XmlReadInState_DTD;
1368 case XmlReadInState_DTD:
1369 hr = reader_parse_dtd(reader);
1370 if (FAILED(hr)) return hr;
1374 reader->instate = XmlReadInState_DTD_Misc;
1378 reader->instate = XmlReadInState_Element;
1380 case XmlReadInState_DTD_Misc:
1381 hr = reader_parse_misc(reader);
1382 if (FAILED(hr)) return hr;
1385 reader->instate = XmlReadInState_Element;
1389 case XmlReadInState_Element:
1390 hr = reader_parse_element(reader);
1391 if (FAILED(hr)) return hr;
1394 FIXME("internal state %d not handled\n", reader->instate);
1402 static HRESULT WINAPI xmlreader_QueryInterface(IXmlReader *iface, REFIID riid, void** ppvObject)
1404 xmlreader *This = impl_from_IXmlReader(iface);
1406 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1408 if (IsEqualGUID(riid, &IID_IUnknown) ||
1409 IsEqualGUID(riid, &IID_IXmlReader))
1415 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1416 return E_NOINTERFACE;
1419 IXmlReader_AddRef(iface);
1424 static ULONG WINAPI xmlreader_AddRef(IXmlReader *iface)
1426 xmlreader *This = impl_from_IXmlReader(iface);
1427 ULONG ref = InterlockedIncrement(&This->ref);
1428 TRACE("(%p)->(%d)\n", This, ref);
1432 static ULONG WINAPI xmlreader_Release(IXmlReader *iface)
1434 xmlreader *This = impl_from_IXmlReader(iface);
1435 LONG ref = InterlockedDecrement(&This->ref);
1437 TRACE("(%p)->(%d)\n", This, ref);
1441 IMalloc *imalloc = This->imalloc;
1442 if (This->input) IUnknown_Release(&This->input->IXmlReaderInput_iface);
1443 reader_clear_attrs(This);
1444 reader_free_strvalues(This);
1445 reader_free(This, This);
1446 if (imalloc) IMalloc_Release(imalloc);
1452 static HRESULT WINAPI xmlreader_SetInput(IXmlReader* iface, IUnknown *input)
1454 xmlreader *This = impl_from_IXmlReader(iface);
1457 TRACE("(%p)->(%p)\n", This, input);
1461 readerinput_release_stream(This->input);
1462 IUnknown_Release(&This->input->IXmlReaderInput_iface);
1466 This->line = This->pos = 0;
1468 /* just reset current input */
1471 This->state = XmlReadState_Initial;
1475 /* now try IXmlReaderInput, ISequentialStream, IStream */
1476 hr = IUnknown_QueryInterface(input, &IID_IXmlReaderInput, (void**)&This->input);
1479 IXmlReaderInput *readerinput;
1481 /* create IXmlReaderInput basing on supplied interface */
1482 hr = CreateXmlReaderInputWithEncodingName(input,
1483 NULL, NULL, FALSE, NULL, &readerinput);
1484 if (hr != S_OK) return hr;
1485 This->input = impl_from_IXmlReaderInput(readerinput);
1488 /* set stream for supplied IXmlReaderInput */
1489 hr = readerinput_query_for_stream(This->input);
1492 This->state = XmlReadState_Initial;
1493 This->instate = XmlReadInState_Initial;
1499 static HRESULT WINAPI xmlreader_GetProperty(IXmlReader* iface, UINT property, LONG_PTR *value)
1501 xmlreader *This = impl_from_IXmlReader(iface);
1503 TRACE("(%p %u %p)\n", This, property, value);
1505 if (!value) return E_INVALIDARG;
1509 case XmlReaderProperty_DtdProcessing:
1510 *value = This->dtdmode;
1512 case XmlReaderProperty_ReadState:
1513 *value = This->state;
1516 FIXME("Unimplemented property (%u)\n", property);
1523 static HRESULT WINAPI xmlreader_SetProperty(IXmlReader* iface, UINT property, LONG_PTR value)
1525 xmlreader *This = impl_from_IXmlReader(iface);
1527 TRACE("(%p %u %lu)\n", iface, property, value);
1531 case XmlReaderProperty_DtdProcessing:
1532 if (value < 0 || value > _DtdProcessing_Last) return E_INVALIDARG;
1533 This->dtdmode = value;
1536 FIXME("Unimplemented property (%u)\n", property);
1543 static HRESULT WINAPI xmlreader_Read(IXmlReader* iface, XmlNodeType *nodetype)
1545 xmlreader *This = impl_from_IXmlReader(iface);
1546 XmlNodeType oldtype = This->nodetype;
1549 TRACE("(%p)->(%p)\n", This, nodetype);
1551 if (This->state == XmlReadState_Closed) return S_FALSE;
1553 hr = reader_parse_nextnode(This);
1554 if (oldtype == XmlNodeType_None && This->nodetype != oldtype)
1555 This->state = XmlReadState_Interactive;
1556 if (hr == S_OK) *nodetype = This->nodetype;
1561 static HRESULT WINAPI xmlreader_GetNodeType(IXmlReader* iface, XmlNodeType *node_type)
1563 xmlreader *This = impl_from_IXmlReader(iface);
1564 TRACE("(%p)->(%p)\n", This, node_type);
1566 /* When we're on attribute always return attribute type, container node type is kept.
1567 Note that container is not necessarily an element, and attribute doesn't mean it's
1568 an attribute in XML spec terms. */
1569 *node_type = This->attr ? XmlNodeType_Attribute : This->nodetype;
1570 return This->state == XmlReadState_Closed ? S_FALSE : S_OK;
1573 static HRESULT WINAPI xmlreader_MoveToFirstAttribute(IXmlReader* iface)
1575 xmlreader *This = impl_from_IXmlReader(iface);
1577 TRACE("(%p)\n", This);
1579 if (!This->attr_count) return S_FALSE;
1580 This->attr = LIST_ENTRY(list_head(&This->attrs), struct attribute, entry);
1584 static HRESULT WINAPI xmlreader_MoveToNextAttribute(IXmlReader* iface)
1586 xmlreader *This = impl_from_IXmlReader(iface);
1587 const struct list *next;
1589 TRACE("(%p)\n", This);
1591 if (!This->attr_count) return S_FALSE;
1594 return IXmlReader_MoveToFirstAttribute(iface);
1596 next = list_next(&This->attrs, &This->attr->entry);
1598 This->attr = LIST_ENTRY(next, struct attribute, entry);
1600 return next ? S_OK : S_FALSE;
1603 static HRESULT WINAPI xmlreader_MoveToAttributeByName(IXmlReader* iface,
1605 LPCWSTR namespaceUri)
1607 FIXME("(%p %p %p): stub\n", iface, local_name, namespaceUri);
1611 static HRESULT WINAPI xmlreader_MoveToElement(IXmlReader* iface)
1613 xmlreader *This = impl_from_IXmlReader(iface);
1615 TRACE("(%p)\n", This);
1617 if (!This->attr_count) return S_FALSE;
1622 static HRESULT WINAPI xmlreader_GetQualifiedName(IXmlReader* iface, LPCWSTR *name, UINT *len)
1624 xmlreader *This = impl_from_IXmlReader(iface);
1626 TRACE("(%p)->(%p %p)\n", This, name, len);
1627 *name = This->strvalues[StringValue_QualifiedName].str;
1628 *len = This->strvalues[StringValue_QualifiedName].len;
1632 static HRESULT WINAPI xmlreader_GetNamespaceUri(IXmlReader* iface,
1633 LPCWSTR *namespaceUri,
1634 UINT *namespaceUri_length)
1636 FIXME("(%p %p %p): stub\n", iface, namespaceUri, namespaceUri_length);
1640 static HRESULT WINAPI xmlreader_GetLocalName(IXmlReader* iface, LPCWSTR *name, UINT *len)
1642 xmlreader *This = impl_from_IXmlReader(iface);
1644 TRACE("(%p)->(%p %p)\n", This, name, len);
1645 *name = This->strvalues[StringValue_LocalName].str;
1646 *len = This->strvalues[StringValue_LocalName].len;
1650 static HRESULT WINAPI xmlreader_GetPrefix(IXmlReader* iface,
1652 UINT *prefix_length)
1654 FIXME("(%p %p %p): stub\n", iface, prefix, prefix_length);
1658 static HRESULT WINAPI xmlreader_GetValue(IXmlReader* iface, LPCWSTR *value, UINT *len)
1660 xmlreader *This = impl_from_IXmlReader(iface);
1662 TRACE("(%p)->(%p %p)\n", This, value, len);
1663 *value = This->strvalues[StringValue_Value].str;
1664 if (len) *len = This->strvalues[StringValue_Value].len;
1668 static HRESULT WINAPI xmlreader_ReadValueChunk(IXmlReader* iface,
1673 FIXME("(%p %p %u %p): stub\n", iface, buffer, chunk_size, read);
1677 static HRESULT WINAPI xmlreader_GetBaseUri(IXmlReader* iface,
1679 UINT *baseUri_length)
1681 FIXME("(%p %p %p): stub\n", iface, baseUri, baseUri_length);
1685 static BOOL WINAPI xmlreader_IsDefault(IXmlReader* iface)
1687 FIXME("(%p): stub\n", iface);
1691 static BOOL WINAPI xmlreader_IsEmptyElement(IXmlReader* iface)
1693 FIXME("(%p): stub\n", iface);
1697 static HRESULT WINAPI xmlreader_GetLineNumber(IXmlReader* iface, UINT *lineNumber)
1699 xmlreader *This = impl_from_IXmlReader(iface);
1701 TRACE("(%p %p)\n", This, lineNumber);
1703 if (!lineNumber) return E_INVALIDARG;
1705 *lineNumber = This->line;
1710 static HRESULT WINAPI xmlreader_GetLinePosition(IXmlReader* iface, UINT *linePosition)
1712 xmlreader *This = impl_from_IXmlReader(iface);
1714 TRACE("(%p %p)\n", This, linePosition);
1716 if (!linePosition) return E_INVALIDARG;
1718 *linePosition = This->pos;
1723 static HRESULT WINAPI xmlreader_GetAttributeCount(IXmlReader* iface, UINT *count)
1725 xmlreader *This = impl_from_IXmlReader(iface);
1727 TRACE("(%p)->(%p)\n", This, count);
1729 if (!count) return E_INVALIDARG;
1731 *count = This->attr_count;
1735 static HRESULT WINAPI xmlreader_GetDepth(IXmlReader* iface, UINT *depth)
1737 FIXME("(%p %p): stub\n", iface, depth);
1741 static BOOL WINAPI xmlreader_IsEOF(IXmlReader* iface)
1743 FIXME("(%p): stub\n", iface);
1747 static const struct IXmlReaderVtbl xmlreader_vtbl =
1749 xmlreader_QueryInterface,
1753 xmlreader_GetProperty,
1754 xmlreader_SetProperty,
1756 xmlreader_GetNodeType,
1757 xmlreader_MoveToFirstAttribute,
1758 xmlreader_MoveToNextAttribute,
1759 xmlreader_MoveToAttributeByName,
1760 xmlreader_MoveToElement,
1761 xmlreader_GetQualifiedName,
1762 xmlreader_GetNamespaceUri,
1763 xmlreader_GetLocalName,
1764 xmlreader_GetPrefix,
1766 xmlreader_ReadValueChunk,
1767 xmlreader_GetBaseUri,
1768 xmlreader_IsDefault,
1769 xmlreader_IsEmptyElement,
1770 xmlreader_GetLineNumber,
1771 xmlreader_GetLinePosition,
1772 xmlreader_GetAttributeCount,
1777 /** IXmlReaderInput **/
1778 static HRESULT WINAPI xmlreaderinput_QueryInterface(IXmlReaderInput *iface, REFIID riid, void** ppvObject)
1780 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1782 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
1784 if (IsEqualGUID(riid, &IID_IXmlReaderInput) ||
1785 IsEqualGUID(riid, &IID_IUnknown))
1791 WARN("interface %s not implemented\n", debugstr_guid(riid));
1792 return E_NOINTERFACE;
1795 IUnknown_AddRef(iface);
1800 static ULONG WINAPI xmlreaderinput_AddRef(IXmlReaderInput *iface)
1802 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1803 ULONG ref = InterlockedIncrement(&This->ref);
1804 TRACE("(%p)->(%d)\n", This, ref);
1808 static ULONG WINAPI xmlreaderinput_Release(IXmlReaderInput *iface)
1810 xmlreaderinput *This = impl_from_IXmlReaderInput(iface);
1811 LONG ref = InterlockedDecrement(&This->ref);
1813 TRACE("(%p)->(%d)\n", This, ref);
1817 IMalloc *imalloc = This->imalloc;
1818 if (This->input) IUnknown_Release(This->input);
1819 if (This->stream) ISequentialStream_Release(This->stream);
1820 if (This->buffer) free_input_buffer(This->buffer);
1821 readerinput_free(This, This->baseuri);
1822 readerinput_free(This, This);
1823 if (imalloc) IMalloc_Release(imalloc);
1829 static const struct IUnknownVtbl xmlreaderinput_vtbl =
1831 xmlreaderinput_QueryInterface,
1832 xmlreaderinput_AddRef,
1833 xmlreaderinput_Release
1836 HRESULT WINAPI CreateXmlReader(REFIID riid, void **obj, IMalloc *imalloc)
1841 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1843 if (!IsEqualGUID(riid, &IID_IXmlReader))
1845 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1850 reader = IMalloc_Alloc(imalloc, sizeof(*reader));
1852 reader = heap_alloc(sizeof(*reader));
1853 if(!reader) return E_OUTOFMEMORY;
1855 reader->IXmlReader_iface.lpVtbl = &xmlreader_vtbl;
1857 reader->input = NULL;
1858 reader->state = XmlReadState_Closed;
1859 reader->instate = XmlReadInState_Initial;
1860 reader->dtdmode = DtdProcessing_Prohibit;
1861 reader->line = reader->pos = 0;
1862 reader->imalloc = imalloc;
1863 if (imalloc) IMalloc_AddRef(imalloc);
1864 reader->nodetype = XmlNodeType_None;
1865 list_init(&reader->attrs);
1866 reader->attr_count = 0;
1867 reader->attr = NULL;
1869 for (i = 0; i < StringValue_Last; i++)
1870 reader->strvalues[i] = strval_empty;
1872 *obj = &reader->IXmlReader_iface;
1874 TRACE("returning iface %p\n", *obj);
1879 HRESULT WINAPI CreateXmlReaderInputWithEncodingName(IUnknown *stream,
1884 IXmlReaderInput **ppInput)
1886 xmlreaderinput *readerinput;
1889 TRACE("%p %p %s %d %s %p\n", stream, imalloc, wine_dbgstr_w(encoding),
1890 hint, wine_dbgstr_w(base_uri), ppInput);
1892 if (!stream || !ppInput) return E_INVALIDARG;
1895 readerinput = IMalloc_Alloc(imalloc, sizeof(*readerinput));
1897 readerinput = heap_alloc(sizeof(*readerinput));
1898 if(!readerinput) return E_OUTOFMEMORY;
1900 readerinput->IXmlReaderInput_iface.lpVtbl = &xmlreaderinput_vtbl;
1901 readerinput->ref = 1;
1902 readerinput->imalloc = imalloc;
1903 readerinput->stream = NULL;
1904 if (imalloc) IMalloc_AddRef(imalloc);
1905 readerinput->encoding = parse_encoding_name(encoding, -1);
1906 readerinput->hint = hint;
1907 readerinput->baseuri = readerinput_strdupW(readerinput, base_uri);
1909 hr = alloc_input_buffer(readerinput);
1912 readerinput_free(readerinput, readerinput);
1915 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&readerinput->input);
1917 *ppInput = &readerinput->IXmlReaderInput_iface;
1919 TRACE("returning iface %p\n", *ppInput);