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 XmlReadInState_Content
,
56 XmlReadInState_MiscEnd
57 } XmlReaderInternalState
;
61 StringValue_LocalName
,
62 StringValue_QualifiedName
,
65 } XmlReaderStringValue
;
67 static const WCHAR utf16W
[] = {'U','T','F','-','1','6',0};
68 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
70 static const WCHAR dblquoteW
[] = {'\"',0};
71 static const WCHAR quoteW
[] = {'\'',0};
72 static const WCHAR ltW
[] = {'<',0};
73 static const WCHAR gtW
[] = {'>',0};
74 static const WCHAR commentW
[] = {'<','!','-','-',0};
75 static const WCHAR piW
[] = {'<','?',0};
77 struct xml_encoding_data
84 static const struct xml_encoding_data xml_encoding_map
[] = {
85 { utf16W
, XmlEncoding_UTF16
, ~0 },
86 { utf8W
, XmlEncoding_UTF8
, CP_UTF8
}
93 unsigned int allocated
;
97 typedef struct input_buffer input_buffer
;
101 IXmlReaderInput IXmlReaderInput_iface
;
103 /* reference passed on IXmlReaderInput creation, is kept when input is created */
106 xml_encoding encoding
;
109 /* stream reference set after SetInput() call from reader,
110 stored as sequential stream, cause currently
111 optimizations possible with IStream aren't implemented */
112 ISequentialStream
*stream
;
113 input_buffer
*buffer
;
114 unsigned int pending
: 1;
117 static const struct IUnknownVtbl xmlreaderinputvtbl
;
125 static WCHAR emptyW
[] = {0};
126 static const strval strval_empty
= {emptyW
, 0};
143 IXmlReader IXmlReader_iface
;
145 xmlreaderinput
*input
;
148 XmlReaderInternalState instate
;
149 XmlNodeType nodetype
;
150 DtdProcessing dtdmode
;
151 UINT line
, pos
; /* reader position in XML stream */
152 struct list attrs
; /* attributes list for current node */
153 struct attribute
*attr
; /* current attribute */
155 struct list elements
;
156 strval strvalues
[StringValue_Last
];
163 encoded_buffer utf16
;
164 encoded_buffer encoded
;
166 xmlreaderinput
*input
;
169 static inline xmlreader
*impl_from_IXmlReader(IXmlReader
*iface
)
171 return CONTAINING_RECORD(iface
, xmlreader
, IXmlReader_iface
);
174 static inline xmlreaderinput
*impl_from_IXmlReaderInput(IXmlReaderInput
*iface
)
176 return CONTAINING_RECORD(iface
, xmlreaderinput
, IXmlReaderInput_iface
);
179 static inline void *m_alloc(IMalloc
*imalloc
, size_t len
)
182 return IMalloc_Alloc(imalloc
, len
);
184 return heap_alloc(len
);
187 static inline void *m_realloc(IMalloc
*imalloc
, void *mem
, size_t len
)
190 return IMalloc_Realloc(imalloc
, mem
, len
);
192 return heap_realloc(mem
, len
);
195 static inline void m_free(IMalloc
*imalloc
, void *mem
)
198 IMalloc_Free(imalloc
, mem
);
203 /* reader memory allocation functions */
204 static inline void *reader_alloc(xmlreader
*reader
, size_t len
)
206 return m_alloc(reader
->imalloc
, len
);
209 static inline void reader_free(xmlreader
*reader
, void *mem
)
211 m_free(reader
->imalloc
, mem
);
214 static HRESULT
reader_strvaldup(xmlreader
*reader
, const strval
*src
, strval
*dest
)
218 if (src
->str
!= strval_empty
.str
)
220 dest
->str
= reader_alloc(reader
, (dest
->len
+1)*sizeof(WCHAR
));
221 if (!dest
->str
) return E_OUTOFMEMORY
;
222 memcpy(dest
->str
, src
->str
, dest
->len
*sizeof(WCHAR
));
223 dest
->str
[dest
->len
] = 0;
229 /* reader input memory allocation functions */
230 static inline void *readerinput_alloc(xmlreaderinput
*input
, size_t len
)
232 return m_alloc(input
->imalloc
, len
);
235 static inline void *readerinput_realloc(xmlreaderinput
*input
, void *mem
, size_t len
)
237 return m_realloc(input
->imalloc
, mem
, len
);
240 static inline void readerinput_free(xmlreaderinput
*input
, void *mem
)
242 m_free(input
->imalloc
, mem
);
245 static inline WCHAR
*readerinput_strdupW(xmlreaderinput
*input
, const WCHAR
*str
)
252 size
= (strlenW(str
)+1)*sizeof(WCHAR
);
253 ret
= readerinput_alloc(input
, size
);
254 if (ret
) memcpy(ret
, str
, size
);
260 static void reader_clear_attrs(xmlreader
*reader
)
262 struct attribute
*attr
, *attr2
;
263 LIST_FOR_EACH_ENTRY_SAFE(attr
, attr2
, &reader
->attrs
, struct attribute
, entry
)
265 reader_free(reader
, attr
);
267 list_init(&reader
->attrs
);
268 reader
->attr_count
= 0;
271 /* attribute data holds pointers to buffer data, so buffer shrink is not possible
272 while we are on a node with attributes */
273 static HRESULT
reader_add_attr(xmlreader
*reader
, strval
*localname
, strval
*value
)
275 struct attribute
*attr
;
277 attr
= reader_alloc(reader
, sizeof(*attr
));
278 if (!attr
) return E_OUTOFMEMORY
;
280 attr
->localname
= *localname
;
281 attr
->value
= *value
;
282 list_add_tail(&reader
->attrs
, &attr
->entry
);
283 reader
->attr_count
++;
288 /* This one frees stored string value if needed */
289 static void reader_free_strvalued(xmlreader
*reader
, strval
*v
)
291 if (v
->str
!= strval_empty
.str
)
293 reader_free(reader
, v
->str
);
298 static void reader_free_strvalue(xmlreader
*reader
, XmlReaderStringValue type
)
300 reader_free_strvalued(reader
, &reader
->strvalues
[type
]);
303 static void reader_free_strvalues(xmlreader
*reader
)
306 for (type
= 0; type
< StringValue_Last
; type
++)
307 reader_free_strvalue(reader
, type
);
310 /* This helper should only be used to test if strings are the same,
311 it doesn't try to sort. */
312 static inline int strval_eq(const strval
*str1
, const strval
*str2
)
314 if (str1
->len
!= str2
->len
) return 0;
315 return !memcmp(str1
->str
, str2
->str
, str1
->len
*sizeof(WCHAR
));
318 static void reader_clear_elements(xmlreader
*reader
)
320 struct element
*elem
, *elem2
;
321 LIST_FOR_EACH_ENTRY_SAFE(elem
, elem2
, &reader
->elements
, struct element
, entry
)
323 reader_free_strvalued(reader
, &elem
->qname
);
324 reader_free(reader
, elem
);
326 list_init(&reader
->elements
);
329 static HRESULT
reader_inc_depth(xmlreader
*reader
)
331 /* FIXME: handle XmlReaderProperty_MaxElementDepth property */
336 static HRESULT
reader_push_element(xmlreader
*reader
, strval
*qname
)
338 struct element
*elem
;
341 elem
= reader_alloc(reader
, sizeof(*elem
));
342 if (!elem
) return E_OUTOFMEMORY
;
344 hr
= reader_strvaldup(reader
, qname
, &elem
->qname
);
345 if (FAILED(hr
)) return hr
;
347 if (!list_empty(&reader
->elements
))
349 hr
= reader_inc_depth(reader
);
350 if (FAILED(hr
)) return hr
;
353 list_add_head(&reader
->elements
, &elem
->entry
);
357 static void reader_pop_element(xmlreader
*reader
)
359 struct element
*elem
= LIST_ENTRY(list_head(&reader
->elements
), struct element
, entry
);
363 list_remove(&elem
->entry
);
364 reader_free_strvalued(reader
, &elem
->qname
);
365 reader_free(reader
, elem
);
369 /* Always make a copy, cause strings are supposed to be null terminated. Null pointer for 'value'
370 means node value is to be determined. */
371 static void reader_set_strvalue(xmlreader
*reader
, XmlReaderStringValue type
, const strval
*value
)
373 strval
*v
= &reader
->strvalues
[type
];
375 reader_free_strvalue(reader
, type
);
383 if (value
->str
== strval_empty
.str
)
387 v
->str
= reader_alloc(reader
, (value
->len
+ 1)*sizeof(WCHAR
));
388 memcpy(v
->str
, value
->str
, value
->len
*sizeof(WCHAR
));
389 v
->str
[value
->len
] = 0;
394 static inline int is_reader_pending(xmlreader
*reader
)
396 return reader
->input
->pending
;
399 static HRESULT
init_encoded_buffer(xmlreaderinput
*input
, encoded_buffer
*buffer
)
401 const int initial_len
= 0x2000;
402 buffer
->data
= readerinput_alloc(input
, initial_len
);
403 if (!buffer
->data
) return E_OUTOFMEMORY
;
405 memset(buffer
->data
, 0, 4);
406 buffer
->cur
= buffer
->data
;
407 buffer
->allocated
= initial_len
;
413 static void free_encoded_buffer(xmlreaderinput
*input
, encoded_buffer
*buffer
)
415 readerinput_free(input
, buffer
->data
);
418 static HRESULT
get_code_page(xml_encoding encoding
, UINT
*cp
)
420 if (encoding
== XmlEncoding_Unknown
)
422 FIXME("unsupported encoding %d\n", encoding
);
426 *cp
= xml_encoding_map
[encoding
].cp
;
431 static xml_encoding
parse_encoding_name(const WCHAR
*name
, int len
)
435 if (!name
) return XmlEncoding_Unknown
;
438 max
= sizeof(xml_encoding_map
)/sizeof(struct xml_encoding_data
) - 1;
445 c
= strncmpiW(xml_encoding_map
[n
].name
, name
, len
);
447 c
= strcmpiW(xml_encoding_map
[n
].name
, name
);
449 return xml_encoding_map
[n
].enc
;
457 return XmlEncoding_Unknown
;
460 static HRESULT
alloc_input_buffer(xmlreaderinput
*input
)
462 input_buffer
*buffer
;
465 input
->buffer
= NULL
;
467 buffer
= readerinput_alloc(input
, sizeof(*buffer
));
468 if (!buffer
) return E_OUTOFMEMORY
;
470 buffer
->input
= input
;
471 buffer
->code_page
= ~0; /* code page is unknown at this point */
472 hr
= init_encoded_buffer(input
, &buffer
->utf16
);
474 readerinput_free(input
, buffer
);
478 hr
= init_encoded_buffer(input
, &buffer
->encoded
);
480 free_encoded_buffer(input
, &buffer
->utf16
);
481 readerinput_free(input
, buffer
);
485 input
->buffer
= buffer
;
489 static void free_input_buffer(input_buffer
*buffer
)
491 free_encoded_buffer(buffer
->input
, &buffer
->encoded
);
492 free_encoded_buffer(buffer
->input
, &buffer
->utf16
);
493 readerinput_free(buffer
->input
, buffer
);
496 static void readerinput_release_stream(xmlreaderinput
*readerinput
)
498 if (readerinput
->stream
) {
499 ISequentialStream_Release(readerinput
->stream
);
500 readerinput
->stream
= NULL
;
504 /* Queries already stored interface for IStream/ISequentialStream.
505 Interface supplied on creation will be overwritten */
506 static HRESULT
readerinput_query_for_stream(xmlreaderinput
*readerinput
)
510 readerinput_release_stream(readerinput
);
511 hr
= IUnknown_QueryInterface(readerinput
->input
, &IID_IStream
, (void**)&readerinput
->stream
);
513 hr
= IUnknown_QueryInterface(readerinput
->input
, &IID_ISequentialStream
, (void**)&readerinput
->stream
);
518 /* reads a chunk to raw buffer */
519 static HRESULT
readerinput_growraw(xmlreaderinput
*readerinput
)
521 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
522 /* to make sure aligned length won't exceed allocated length */
523 ULONG len
= buffer
->allocated
- buffer
->written
- 4;
527 /* always try to get aligned to 4 bytes, so the only case we can get partially read characters is
528 variable width encodings like UTF-8 */
529 len
= (len
+ 3) & ~3;
530 /* try to use allocated space or grow */
531 if (buffer
->allocated
- buffer
->written
< len
)
533 buffer
->allocated
*= 2;
534 buffer
->data
= readerinput_realloc(readerinput
, buffer
->data
, buffer
->allocated
);
535 len
= buffer
->allocated
- buffer
->written
;
539 hr
= ISequentialStream_Read(readerinput
->stream
, buffer
->data
+ buffer
->written
, len
, &read
);
540 TRACE("requested %d, read %d, ret 0x%08x\n", len
, read
, hr
);
541 readerinput
->pending
= hr
== E_PENDING
;
542 if (FAILED(hr
)) return hr
;
543 buffer
->written
+= read
;
548 /* grows UTF-16 buffer so it has at least 'length' bytes free on return */
549 static void readerinput_grow(xmlreaderinput
*readerinput
, int length
)
551 encoded_buffer
*buffer
= &readerinput
->buffer
->utf16
;
553 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
554 if (buffer
->allocated
< buffer
->written
+ length
+ 4)
556 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
557 buffer
->data
= readerinput_realloc(readerinput
, buffer
->data
, grown_size
);
558 buffer
->allocated
= grown_size
;
562 static inline int readerinput_is_utf8(xmlreaderinput
*readerinput
)
564 static char startA
[] = {'<','?'};
565 static char commentA
[] = {'<','!'};
566 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
567 unsigned char *ptr
= (unsigned char*)buffer
->data
;
569 return !memcmp(buffer
->data
, startA
, sizeof(startA
)) ||
570 !memcmp(buffer
->data
, commentA
, sizeof(commentA
)) ||
571 /* test start byte */
574 (ptr
[1] && (ptr
[1] <= 0x7f)) ||
575 (buffer
->data
[1] >> 5) == 0x6 || /* 2 bytes */
576 (buffer
->data
[1] >> 4) == 0xe || /* 3 bytes */
577 (buffer
->data
[1] >> 3) == 0x1e) /* 4 bytes */
581 static HRESULT
readerinput_detectencoding(xmlreaderinput
*readerinput
, xml_encoding
*enc
)
583 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
584 static WCHAR startW
[] = {'<','?'};
585 static WCHAR commentW
[] = {'<','!'};
586 static char utf8bom
[] = {0xef,0xbb,0xbf};
587 static char utf16lebom
[] = {0xff,0xfe};
589 *enc
= XmlEncoding_Unknown
;
591 if (buffer
->written
<= 3) return MX_E_INPUTEND
;
593 /* try start symbols if we have enough data to do that, input buffer should contain
594 first chunk already */
595 if (readerinput_is_utf8(readerinput
))
596 *enc
= XmlEncoding_UTF8
;
597 else if (!memcmp(buffer
->data
, startW
, sizeof(startW
)) ||
598 !memcmp(buffer
->data
, commentW
, sizeof(commentW
)))
599 *enc
= XmlEncoding_UTF16
;
600 /* try with BOM now */
601 else if (!memcmp(buffer
->data
, utf8bom
, sizeof(utf8bom
)))
603 buffer
->cur
+= sizeof(utf8bom
);
604 *enc
= XmlEncoding_UTF8
;
606 else if (!memcmp(buffer
->data
, utf16lebom
, sizeof(utf16lebom
)))
608 buffer
->cur
+= sizeof(utf16lebom
);
609 *enc
= XmlEncoding_UTF16
;
615 static int readerinput_get_utf8_convlen(xmlreaderinput
*readerinput
)
617 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
618 int len
= buffer
->written
;
620 /* complete single byte char */
621 if (!(buffer
->data
[len
-1] & 0x80)) return len
;
623 /* find start byte of multibyte char */
624 while (--len
&& !(buffer
->data
[len
] & 0xc0))
630 /* Returns byte length of complete char sequence for buffer code page,
631 it's relative to current buffer position which is currently used for BOM handling
633 static int readerinput_get_convlen(xmlreaderinput
*readerinput
)
635 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
638 if (readerinput
->buffer
->code_page
== CP_UTF8
)
639 len
= readerinput_get_utf8_convlen(readerinput
);
641 len
= buffer
->written
;
643 TRACE("%d\n", len
- (int)(buffer
->cur
- buffer
->data
));
644 return len
- (buffer
->cur
- buffer
->data
);
647 /* It's possible that raw buffer has some leftovers from last conversion - some char
648 sequence that doesn't represent a full code point. Length argument should be calculated with
649 readerinput_get_convlen(), if it's -1 it will be calculated here. */
650 static void readerinput_shrinkraw(xmlreaderinput
*readerinput
, int len
)
652 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
655 len
= readerinput_get_convlen(readerinput
);
657 memmove(buffer
->data
, buffer
->cur
+ (buffer
->written
- len
), len
);
658 /* everything below cur is lost too */
659 buffer
->written
-= len
+ (buffer
->cur
- buffer
->data
);
660 /* after this point we don't need cur pointer really,
661 it's used only to mark where actual data begins when first chunk is read */
662 buffer
->cur
= buffer
->data
;
665 /* note that raw buffer content is kept */
666 static void readerinput_switchencoding(xmlreaderinput
*readerinput
, xml_encoding enc
)
668 encoded_buffer
*src
= &readerinput
->buffer
->encoded
;
669 encoded_buffer
*dest
= &readerinput
->buffer
->utf16
;
675 hr
= get_code_page(enc
, &cp
);
676 if (FAILED(hr
)) return;
678 readerinput
->buffer
->code_page
= cp
;
679 len
= readerinput_get_convlen(readerinput
);
681 TRACE("switching to cp %d\n", cp
);
683 /* just copy in this case */
684 if (enc
== XmlEncoding_UTF16
)
686 readerinput_grow(readerinput
, len
);
687 memcpy(dest
->data
, src
->cur
, len
);
688 dest
->written
+= len
*sizeof(WCHAR
);
692 dest_len
= MultiByteToWideChar(cp
, 0, src
->cur
, len
, NULL
, 0);
693 readerinput_grow(readerinput
, dest_len
);
694 ptr
= (WCHAR
*)dest
->data
;
695 MultiByteToWideChar(cp
, 0, src
->cur
, len
, ptr
, dest_len
);
697 dest
->written
+= dest_len
*sizeof(WCHAR
);
700 /* shrinks parsed data a buffer begins with */
701 static void reader_shrink(xmlreader
*reader
)
703 encoded_buffer
*buffer
= &reader
->input
->buffer
->utf16
;
705 /* avoid to move too often using threshold shrink length */
706 if (buffer
->cur
- buffer
->data
> buffer
->written
/ 2)
708 buffer
->written
-= buffer
->cur
- buffer
->data
;
709 memmove(buffer
->data
, buffer
->cur
, buffer
->written
);
710 buffer
->cur
= buffer
->data
;
711 *(WCHAR
*)&buffer
->cur
[buffer
->written
] = 0;
715 /* This is a normal way for reader to get new data converted from raw buffer to utf16 buffer.
716 It won't attempt to shrink but will grow destination buffer if needed */
717 static HRESULT
reader_more(xmlreader
*reader
)
719 xmlreaderinput
*readerinput
= reader
->input
;
720 encoded_buffer
*src
= &readerinput
->buffer
->encoded
;
721 encoded_buffer
*dest
= &readerinput
->buffer
->utf16
;
722 UINT cp
= readerinput
->buffer
->code_page
;
727 /* get some raw data from stream first */
728 hr
= readerinput_growraw(readerinput
);
729 len
= readerinput_get_convlen(readerinput
);
731 /* just copy for UTF-16 case */
734 readerinput_grow(readerinput
, len
);
735 memcpy(dest
->data
, src
->cur
, len
);
736 dest
->written
+= len
*sizeof(WCHAR
);
740 dest_len
= MultiByteToWideChar(cp
, 0, src
->cur
, len
, NULL
, 0);
741 readerinput_grow(readerinput
, dest_len
);
742 ptr
= (WCHAR
*)dest
->data
;
743 MultiByteToWideChar(cp
, 0, src
->cur
, len
, ptr
, dest_len
);
745 dest
->written
+= dest_len
*sizeof(WCHAR
);
746 /* get rid of processed data */
747 readerinput_shrinkraw(readerinput
, len
);
752 static inline WCHAR
*reader_get_cur(xmlreader
*reader
)
754 WCHAR
*ptr
= (WCHAR
*)reader
->input
->buffer
->utf16
.cur
;
755 if (!*ptr
) reader_more(reader
);
759 static int reader_cmp(xmlreader
*reader
, const WCHAR
*str
)
761 const WCHAR
*ptr
= reader_get_cur(reader
);
762 return strncmpW(str
, ptr
, strlenW(str
));
765 /* moves cursor n WCHARs forward */
766 static void reader_skipn(xmlreader
*reader
, int n
)
768 encoded_buffer
*buffer
= &reader
->input
->buffer
->utf16
;
769 const WCHAR
*ptr
= reader_get_cur(reader
);
771 while (*ptr
++ && n
--)
773 buffer
->cur
+= sizeof(WCHAR
);
778 static inline int is_wchar_space(WCHAR ch
)
780 return ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n';
783 /* [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
784 static int reader_skipspaces(xmlreader
*reader
)
786 encoded_buffer
*buffer
= &reader
->input
->buffer
->utf16
;
787 const WCHAR
*ptr
= reader_get_cur(reader
), *start
= ptr
;
789 while (is_wchar_space(*ptr
))
791 buffer
->cur
+= sizeof(WCHAR
);
794 else if (*ptr
== '\n')
807 /* [26] VersionNum ::= '1.' [0-9]+ */
808 static HRESULT
reader_parse_versionnum(xmlreader
*reader
, strval
*val
)
810 WCHAR
*ptr
, *ptr2
, *start
= reader_get_cur(reader
);
811 static const WCHAR onedotW
[] = {'1','.',0};
813 if (reader_cmp(reader
, onedotW
)) return WC_E_XMLDECL
;
815 reader_skipn(reader
, 2);
817 ptr2
= ptr
= reader_get_cur(reader
);
818 while (*ptr
>= '0' && *ptr
<= '9')
821 if (ptr2
== ptr
) return WC_E_DIGIT
;
822 TRACE("version=%s\n", debugstr_wn(start
, ptr
-start
));
824 val
->len
= ptr
-start
;
825 reader_skipn(reader
, ptr
-ptr2
);
829 /* [25] Eq ::= S? '=' S? */
830 static HRESULT
reader_parse_eq(xmlreader
*reader
)
832 static const WCHAR eqW
[] = {'=',0};
833 reader_skipspaces(reader
);
834 if (reader_cmp(reader
, eqW
)) return WC_E_EQUAL
;
836 reader_skipn(reader
, 1);
837 reader_skipspaces(reader
);
841 /* [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') */
842 static HRESULT
reader_parse_versioninfo(xmlreader
*reader
)
844 static const WCHAR versionW
[] = {'v','e','r','s','i','o','n',0};
848 if (!reader_skipspaces(reader
)) return WC_E_WHITESPACE
;
850 if (reader_cmp(reader
, versionW
)) return WC_E_XMLDECL
;
851 name
.str
= reader_get_cur(reader
);
854 reader_skipn(reader
, 7);
856 hr
= reader_parse_eq(reader
);
857 if (FAILED(hr
)) return hr
;
859 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
862 reader_skipn(reader
, 1);
864 hr
= reader_parse_versionnum(reader
, &val
);
865 if (FAILED(hr
)) return hr
;
867 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
871 reader_skipn(reader
, 1);
873 return reader_add_attr(reader
, &name
, &val
);
876 /* ([A-Za-z0-9._] | '-') */
877 static inline int is_wchar_encname(WCHAR ch
)
879 return ((ch
>= 'A' && ch
<= 'Z') ||
880 (ch
>= 'a' && ch
<= 'z') ||
881 (ch
>= '0' && ch
<= '9') ||
882 (ch
== '.') || (ch
== '_') ||
886 /* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* */
887 static HRESULT
reader_parse_encname(xmlreader
*reader
, strval
*val
)
889 WCHAR
*start
= reader_get_cur(reader
), *ptr
;
893 if ((*start
< 'A' || *start
> 'Z') && (*start
< 'a' || *start
> 'z'))
897 while (is_wchar_encname(*++ptr
))
901 enc
= parse_encoding_name(start
, len
);
902 TRACE("encoding name %s\n", debugstr_wn(start
, len
));
906 if (enc
== XmlEncoding_Unknown
)
909 /* skip encoding name */
910 reader_skipn(reader
, len
);
914 /* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) */
915 static HRESULT
reader_parse_encdecl(xmlreader
*reader
)
917 static const WCHAR encodingW
[] = {'e','n','c','o','d','i','n','g',0};
921 if (!reader_skipspaces(reader
)) return WC_E_WHITESPACE
;
923 if (reader_cmp(reader
, encodingW
)) return S_FALSE
;
924 name
.str
= reader_get_cur(reader
);
926 /* skip 'encoding' */
927 reader_skipn(reader
, 8);
929 hr
= reader_parse_eq(reader
);
930 if (FAILED(hr
)) return hr
;
932 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
935 reader_skipn(reader
, 1);
937 hr
= reader_parse_encname(reader
, &val
);
938 if (FAILED(hr
)) return hr
;
940 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
944 reader_skipn(reader
, 1);
946 return reader_add_attr(reader
, &name
, &val
);
949 /* [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) */
950 static HRESULT
reader_parse_sddecl(xmlreader
*reader
)
952 static const WCHAR standaloneW
[] = {'s','t','a','n','d','a','l','o','n','e',0};
953 static const WCHAR yesW
[] = {'y','e','s',0};
954 static const WCHAR noW
[] = {'n','o',0};
959 if (!reader_skipspaces(reader
)) return WC_E_WHITESPACE
;
961 if (reader_cmp(reader
, standaloneW
)) return S_FALSE
;
962 name
.str
= reader_get_cur(reader
);
964 /* skip 'standalone' */
965 reader_skipn(reader
, 10);
967 hr
= reader_parse_eq(reader
);
968 if (FAILED(hr
)) return hr
;
970 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
973 reader_skipn(reader
, 1);
975 if (reader_cmp(reader
, yesW
) && reader_cmp(reader
, noW
))
978 start
= reader_get_cur(reader
);
979 /* skip 'yes'|'no' */
980 reader_skipn(reader
, reader_cmp(reader
, yesW
) ? 2 : 3);
981 ptr
= reader_get_cur(reader
);
982 TRACE("standalone=%s\n", debugstr_wn(start
, ptr
-start
));
986 if (reader_cmp(reader
, quoteW
) && reader_cmp(reader
, dblquoteW
))
989 reader_skipn(reader
, 1);
991 return reader_add_attr(reader
, &name
, &val
);
994 /* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
995 static HRESULT
reader_parse_xmldecl(xmlreader
*reader
)
997 static const WCHAR xmldeclW
[] = {'<','?','x','m','l',' ',0};
998 static const WCHAR declcloseW
[] = {'?','>',0};
1001 /* check if we have "<?xml " */
1002 if (reader_cmp(reader
, xmldeclW
)) return S_FALSE
;
1004 reader_skipn(reader
, 5);
1005 hr
= reader_parse_versioninfo(reader
);
1009 hr
= reader_parse_encdecl(reader
);
1013 hr
= reader_parse_sddecl(reader
);
1017 reader_skipspaces(reader
);
1018 if (reader_cmp(reader
, declcloseW
)) return WC_E_XMLDECL
;
1019 reader_skipn(reader
, 2);
1021 reader
->nodetype
= XmlNodeType_XmlDeclaration
;
1022 reader_set_strvalue(reader
, StringValue_LocalName
, &strval_empty
);
1023 reader_set_strvalue(reader
, StringValue_QualifiedName
, &strval_empty
);
1024 reader_set_strvalue(reader
, StringValue_Value
, &strval_empty
);
1029 /* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' */
1030 static HRESULT
reader_parse_comment(xmlreader
*reader
)
1036 start
= reader
->save
;
1037 ptr
= reader_get_cur(reader
);
1038 reader
->save
= NULL
;
1043 reader_skipn(reader
, 4);
1044 reader_shrink(reader
);
1045 ptr
= start
= reader_get_cur(reader
);
1046 reader
->nodetype
= XmlNodeType_Comment
;
1047 reader_set_strvalue(reader
, StringValue_LocalName
, NULL
);
1048 reader_set_strvalue(reader
, StringValue_QualifiedName
, NULL
);
1049 reader_set_strvalue(reader
, StringValue_Value
, NULL
);
1052 /* will exit when there's no more data, it won't attempt to
1053 read more from stream */
1062 strval value
= { start
, ptr
-start
};
1064 TRACE("%s\n", debugstr_wn(start
, ptr
-start
));
1066 reader_skipn(reader
, 3);
1067 reader_set_strvalue(reader
, StringValue_LocalName
, &strval_empty
);
1068 reader_set_strvalue(reader
, StringValue_QualifiedName
, &strval_empty
);
1069 reader_set_strvalue(reader
, StringValue_Value
, &value
);
1070 reader
->nodetype
= XmlNodeType_Comment
;
1074 return WC_E_COMMENT
;
1081 reader_skipn(reader
, 1);
1089 /* [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] */
1090 static inline int is_char(WCHAR ch
)
1092 return (ch
== '\t') || (ch
== '\r') || (ch
== '\n') ||
1093 (ch
>= 0x20 && ch
<= 0xd7ff) ||
1094 (ch
>= 0xd800 && ch
<= 0xdbff) || /* high surrogate */
1095 (ch
>= 0xdc00 && ch
<= 0xdfff) || /* low surrogate */
1096 (ch
>= 0xe000 && ch
<= 0xfffd);
1099 /* [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1100 static inline int is_pubchar(WCHAR ch
)
1102 return (ch
== ' ') ||
1103 (ch
>= 'a' && ch
<= 'z') ||
1104 (ch
>= 'A' && ch
<= 'Z') ||
1105 (ch
>= '0' && ch
<= '9') ||
1106 (ch
>= '-' && ch
<= ';') || /* '()*+,-./:; */
1107 (ch
== '=') || (ch
== '?') ||
1108 (ch
== '@') || (ch
== '!') ||
1109 (ch
>= '#' && ch
<= '%') || /* #$% */
1110 (ch
== '_') || (ch
== '\r') || (ch
== '\n');
1113 static inline int is_namestartchar(WCHAR ch
)
1115 return (ch
== ':') || (ch
>= 'A' && ch
<= 'Z') ||
1116 (ch
== '_') || (ch
>= 'a' && ch
<= 'z') ||
1117 (ch
>= 0xc0 && ch
<= 0xd6) ||
1118 (ch
>= 0xd8 && ch
<= 0xf6) ||
1119 (ch
>= 0xf8 && ch
<= 0x2ff) ||
1120 (ch
>= 0x370 && ch
<= 0x37d) ||
1121 (ch
>= 0x37f && ch
<= 0x1fff) ||
1122 (ch
>= 0x200c && ch
<= 0x200d) ||
1123 (ch
>= 0x2070 && ch
<= 0x218f) ||
1124 (ch
>= 0x2c00 && ch
<= 0x2fef) ||
1125 (ch
>= 0x3001 && ch
<= 0xd7ff) ||
1126 (ch
>= 0xd800 && ch
<= 0xdbff) || /* high surrogate */
1127 (ch
>= 0xdc00 && ch
<= 0xdfff) || /* low surrogate */
1128 (ch
>= 0xf900 && ch
<= 0xfdcf) ||
1129 (ch
>= 0xfdf0 && ch
<= 0xfffd);
1132 /* [4 NS] NCName ::= Name - (Char* ':' Char*) */
1133 static inline int is_ncnamechar(WCHAR ch
)
1135 return (ch
>= 'A' && ch
<= 'Z') ||
1136 (ch
== '_') || (ch
>= 'a' && ch
<= 'z') ||
1137 (ch
== '-') || (ch
== '.') ||
1138 (ch
>= '0' && ch
<= '9') ||
1140 (ch
>= 0xc0 && ch
<= 0xd6) ||
1141 (ch
>= 0xd8 && ch
<= 0xf6) ||
1142 (ch
>= 0xf8 && ch
<= 0x2ff) ||
1143 (ch
>= 0x300 && ch
<= 0x36f) ||
1144 (ch
>= 0x370 && ch
<= 0x37d) ||
1145 (ch
>= 0x37f && ch
<= 0x1fff) ||
1146 (ch
>= 0x200c && ch
<= 0x200d) ||
1147 (ch
>= 0x203f && ch
<= 0x2040) ||
1148 (ch
>= 0x2070 && ch
<= 0x218f) ||
1149 (ch
>= 0x2c00 && ch
<= 0x2fef) ||
1150 (ch
>= 0x3001 && ch
<= 0xd7ff) ||
1151 (ch
>= 0xd800 && ch
<= 0xdbff) || /* high surrogate */
1152 (ch
>= 0xdc00 && ch
<= 0xdfff) || /* low surrogate */
1153 (ch
>= 0xf900 && ch
<= 0xfdcf) ||
1154 (ch
>= 0xfdf0 && ch
<= 0xfffd);
1157 static inline int is_namechar(WCHAR ch
)
1159 return (ch
== ':') || is_ncnamechar(ch
);
1162 /* [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
1163 [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
1164 [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
1165 [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
1166 [5] Name ::= NameStartChar (NameChar)* */
1167 static HRESULT
reader_parse_name(xmlreader
*reader
, strval
*name
)
1169 WCHAR
*ptr
, *start
= reader_get_cur(reader
);
1172 if (!is_namestartchar(*ptr
)) return WC_E_NAMECHARACTER
;
1174 while (is_namechar(*ptr
))
1176 reader_skipn(reader
, 1);
1177 ptr
= reader_get_cur(reader
);
1180 TRACE("name %s:%d\n", debugstr_wn(start
, ptr
-start
), (int)(ptr
-start
));
1182 name
->len
= ptr
-start
;
1187 /* [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) */
1188 static HRESULT
reader_parse_pitarget(xmlreader
*reader
, strval
*target
)
1190 static const WCHAR xmlW
[] = {'x','m','l'};
1195 hr
= reader_parse_name(reader
, &name
);
1196 if (FAILED(hr
)) return WC_E_PI
;
1198 /* now that we got name check for illegal content */
1199 if (name
.len
== 3 && !strncmpiW(name
.str
, xmlW
, 3))
1200 return WC_E_LEADINGXML
;
1202 /* PITarget can't be a qualified name */
1203 for (i
= 0; i
< name
.len
; i
++)
1204 if (name
.str
[i
] == ':')
1205 return i
? NC_E_NAMECOLON
: WC_E_PI
;
1207 TRACE("pitarget %s:%d\n", debugstr_wn(name
.str
, name
.len
), name
.len
);
1212 /* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' */
1213 static HRESULT
reader_parse_pi(xmlreader
*reader
)
1220 reader_skipn(reader
, 2);
1221 reader_shrink(reader
);
1223 hr
= reader_parse_pitarget(reader
, &target
);
1224 if (FAILED(hr
)) return hr
;
1226 ptr
= reader_get_cur(reader
);
1227 /* exit earlier if there's no content */
1228 if (ptr
[0] == '?' && ptr
[1] == '>')
1231 reader_skipn(reader
, 2);
1232 reader
->nodetype
= XmlNodeType_ProcessingInstruction
;
1233 reader_set_strvalue(reader
, StringValue_LocalName
, &target
);
1234 reader_set_strvalue(reader
, StringValue_QualifiedName
, &target
);
1235 reader_set_strvalue(reader
, StringValue_Value
, &strval_empty
);
1239 /* now at least a single space char should be there */
1240 if (!is_wchar_space(*ptr
)) return WC_E_WHITESPACE
;
1241 reader_skipspaces(reader
);
1243 ptr
= start
= reader_get_cur(reader
);
1251 strval value
= { start
, ptr
-start
};
1253 TRACE("%s\n", debugstr_wn(start
, ptr
-start
));
1255 reader_skipn(reader
, 2);
1256 reader
->nodetype
= XmlNodeType_ProcessingInstruction
;
1257 reader_set_strvalue(reader
, StringValue_LocalName
, &target
);
1258 reader_set_strvalue(reader
, StringValue_QualifiedName
, &target
);
1259 reader_set_strvalue(reader
, StringValue_Value
, &value
);
1265 reader_more(reader
);
1270 reader_skipn(reader
, 1);
1271 ptr
= reader_get_cur(reader
);
1278 /* This one is used to parse significant whitespace nodes, like in Misc production */
1279 static HRESULT
reader_parse_whitespace(xmlreader
*reader
)
1283 reader_shrink(reader
);
1284 start
= reader_get_cur(reader
);
1286 reader_skipspaces(reader
);
1287 ptr
= reader_get_cur(reader
);
1288 TRACE("%s\n", debugstr_wn(start
, ptr
-start
));
1290 reader
->nodetype
= XmlNodeType_Whitespace
;
1291 reader_set_strvalue(reader
, StringValue_LocalName
, &strval_empty
);
1292 reader_set_strvalue(reader
, StringValue_QualifiedName
, &strval_empty
);
1293 reader_set_strvalue(reader
, StringValue_Value
, &strval_empty
);
1297 /* [27] Misc ::= Comment | PI | S */
1298 static HRESULT
reader_parse_misc(xmlreader
*reader
)
1300 HRESULT hr
= S_FALSE
;
1302 if (is_reader_pending(reader
))
1304 hr
= reader_more(reader
);
1305 if (FAILED(hr
)) return hr
;
1307 /* finish current node */
1308 if (reader
->nodetype
== XmlNodeType_Comment
)
1309 return reader_parse_comment(reader
);
1314 const WCHAR
*cur
= reader_get_cur(reader
);
1316 if (is_wchar_space(*cur
))
1317 hr
= reader_parse_whitespace(reader
);
1318 else if (!reader_cmp(reader
, commentW
))
1319 hr
= reader_parse_comment(reader
);
1320 else if (!reader_cmp(reader
, piW
))
1321 hr
= reader_parse_pi(reader
);
1325 if (hr
!= S_FALSE
) return hr
;
1331 /* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") */
1332 static HRESULT
reader_parse_sys_literal(xmlreader
*reader
, strval
*literal
)
1334 WCHAR
*start
= reader_get_cur(reader
), *cur
, quote
;
1336 if (*start
!= '"' && *start
!= '\'') return WC_E_QUOTE
;
1339 reader_skipn(reader
, 1);
1341 cur
= start
= reader_get_cur(reader
);
1342 while (is_char(*cur
) && *cur
!= quote
)
1344 reader_skipn(reader
, 1);
1345 cur
= reader_get_cur(reader
);
1347 if (*cur
== quote
) reader_skipn(reader
, 1);
1349 literal
->str
= start
;
1350 literal
->len
= cur
-start
;
1351 TRACE("%s\n", debugstr_wn(start
, cur
-start
));
1355 /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1356 [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] */
1357 static HRESULT
reader_parse_pub_literal(xmlreader
*reader
, strval
*literal
)
1359 WCHAR
*start
= reader_get_cur(reader
), *cur
, quote
;
1361 if (*start
!= '"' && *start
!= '\'') return WC_E_QUOTE
;
1364 reader_skipn(reader
, 1);
1367 while (is_pubchar(*cur
) && *cur
!= quote
)
1369 reader_skipn(reader
, 1);
1370 cur
= reader_get_cur(reader
);
1373 literal
->str
= start
;
1374 literal
->len
= cur
-start
;
1375 TRACE("%s\n", debugstr_wn(start
, cur
-start
));
1379 /* [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral */
1380 static HRESULT
reader_parse_externalid(xmlreader
*reader
)
1382 static WCHAR systemW
[] = {'S','Y','S','T','E','M',0};
1383 static WCHAR publicW
[] = {'P','U','B','L','I','C',0};
1388 if (reader_cmp(reader
, systemW
))
1390 if (reader_cmp(reader
, publicW
))
1397 reader_skipn(reader
, 6);
1398 cnt
= reader_skipspaces(reader
);
1399 if (!cnt
) return WC_E_WHITESPACE
;
1401 hr
= reader_parse_pub_literal(reader
, &pub
);
1402 if (FAILED(hr
)) return hr
;
1405 name
.len
= strlenW(publicW
);
1406 return reader_add_attr(reader
, &name
, &pub
);
1414 reader_skipn(reader
, 6);
1415 cnt
= reader_skipspaces(reader
);
1416 if (!cnt
) return WC_E_WHITESPACE
;
1418 hr
= reader_parse_sys_literal(reader
, &sys
);
1419 if (FAILED(hr
)) return hr
;
1422 name
.len
= strlenW(systemW
);
1423 return reader_add_attr(reader
, &name
, &sys
);
1429 /* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' */
1430 static HRESULT
reader_parse_dtd(xmlreader
*reader
)
1432 static const WCHAR doctypeW
[] = {'<','!','D','O','C','T','Y','P','E',0};
1437 /* check if we have "<!DOCTYPE" */
1438 if (reader_cmp(reader
, doctypeW
)) return S_FALSE
;
1439 reader_shrink(reader
);
1441 /* DTD processing is not allowed by default */
1442 if (reader
->dtdmode
== DtdProcessing_Prohibit
) return WC_E_DTDPROHIBITED
;
1444 reader_skipn(reader
, 9);
1445 if (!reader_skipspaces(reader
)) return WC_E_WHITESPACE
;
1448 hr
= reader_parse_name(reader
, &name
);
1449 if (FAILED(hr
)) return WC_E_DECLDOCTYPE
;
1451 reader_skipspaces(reader
);
1453 hr
= reader_parse_externalid(reader
);
1454 if (FAILED(hr
)) return hr
;
1456 reader_skipspaces(reader
);
1458 cur
= reader_get_cur(reader
);
1461 FIXME("internal subset parsing not implemented\n");
1466 reader_skipn(reader
, 1);
1468 reader
->nodetype
= XmlNodeType_DocumentType
;
1469 reader_set_strvalue(reader
, StringValue_LocalName
, &name
);
1470 reader_set_strvalue(reader
, StringValue_QualifiedName
, &name
);
1475 /* [7 NS] QName ::= PrefixedName | UnprefixedName
1476 [8 NS] PrefixedName ::= Prefix ':' LocalPart
1477 [9 NS] UnprefixedName ::= LocalPart
1478 [10 NS] Prefix ::= NCName
1479 [11 NS] LocalPart ::= NCName */
1480 static HRESULT
reader_parse_qname(xmlreader
*reader
, strval
*prefix
, strval
*local
, strval
*qname
)
1482 WCHAR
*ptr
, *start
= reader_get_cur(reader
);
1485 if (!is_ncnamechar(*ptr
)) return NC_E_QNAMECHARACTER
;
1487 while (is_ncnamechar(*ptr
))
1489 reader_skipn(reader
, 1);
1490 ptr
= reader_get_cur(reader
);
1493 /* got a qualified name */
1496 prefix
->str
= start
;
1497 prefix
->len
= ptr
-start
;
1499 reader_skipn(reader
, 1);
1500 start
= ptr
= reader_get_cur(reader
);
1502 while (is_ncnamechar(*ptr
))
1504 reader_skipn(reader
, 1);
1505 ptr
= reader_get_cur(reader
);
1515 local
->len
= ptr
-start
;
1518 TRACE("qname %s:%s\n", debugstr_wn(prefix
->str
, prefix
->len
), debugstr_wn(local
->str
, local
->len
));
1520 TRACE("ncname %s\n", debugstr_wn(local
->str
, local
->len
));
1522 qname
->str
= prefix
->str
? prefix
->str
: local
->str
;
1524 qname
->len
= (prefix
->len
? prefix
->len
+ 1 : 0) + local
->len
;
1529 /* [12 NS] STag ::= '<' QName (S Attribute)* S? '>'
1530 [14 NS] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>' */
1531 static HRESULT
reader_parse_stag(xmlreader
*reader
, strval
*prefix
, strval
*local
, strval
*qname
, int *empty
)
1533 static const WCHAR endW
[] = {'/','>',0};
1537 reader_skipn(reader
, 1);
1539 hr
= reader_parse_qname(reader
, prefix
, local
, qname
);
1540 if (FAILED(hr
)) return hr
;
1542 reader_skipspaces(reader
);
1545 if ((*empty
= !reader_cmp(reader
, endW
)))
1548 reader_skipn(reader
, 2);
1552 /* got a start tag */
1553 if (!reader_cmp(reader
, gtW
))
1556 reader_skipn(reader
, 1);
1557 return reader_push_element(reader
, qname
);
1560 FIXME("only empty elements/start tags without attribute list supported\n");
1564 /* [39] element ::= EmptyElemTag | STag content ETag */
1565 static HRESULT
reader_parse_element(xmlreader
*reader
)
1567 strval qname
, prefix
, local
;
1571 /* check if we are really on element */
1572 if (reader_cmp(reader
, ltW
)) return S_FALSE
;
1573 reader_shrink(reader
);
1575 /* this handles empty elements too */
1577 hr
= reader_parse_stag(reader
, &prefix
, &local
, &qname
, &empty
);
1578 if (FAILED(hr
)) return hr
;
1580 /* FIXME: need to check for defined namespace to reject invalid prefix,
1581 currently reject all prefixes */
1582 if (prefix
.len
) return NC_E_UNDECLAREDPREFIX
;
1584 /* if we got empty element and stack is empty go straight to Misc */
1585 if (empty
&& list_empty(&reader
->elements
))
1586 reader
->instate
= XmlReadInState_MiscEnd
;
1588 reader
->instate
= XmlReadInState_Content
;
1590 reader
->nodetype
= XmlNodeType_Element
;
1591 reader_set_strvalue(reader
, StringValue_LocalName
, &local
);
1592 reader_set_strvalue(reader
, StringValue_QualifiedName
, &qname
);
1597 /* [13 NS] ETag ::= '</' QName S? '>' */
1598 static HRESULT
reader_parse_endtag(xmlreader
*reader
)
1600 strval prefix
, local
, qname
;
1601 struct element
*elem
;
1605 reader_skipn(reader
, 2);
1607 hr
= reader_parse_qname(reader
, &prefix
, &local
, &qname
);
1608 if (FAILED(hr
)) return hr
;
1610 reader_skipspaces(reader
);
1612 if (reader_cmp(reader
, gtW
)) return WC_E_GREATERTHAN
;
1615 reader_skipn(reader
, 1);
1617 /* Element stack should never be empty at this point, cause we shouldn't get to
1618 content parsing if it's empty. */
1619 elem
= LIST_ENTRY(list_head(&reader
->elements
), struct element
, entry
);
1620 if (!strval_eq(&elem
->qname
, &qname
)) return WC_E_ELEMENTMATCH
;
1622 reader_pop_element(reader
);
1624 reader
->nodetype
= XmlNodeType_EndElement
;
1625 reader_set_strvalue(reader
, StringValue_LocalName
, &local
);
1626 reader_set_strvalue(reader
, StringValue_QualifiedName
, &qname
);
1631 /* [18] CDSect ::= CDStart CData CDEnd
1632 [19] CDStart ::= '<![CDATA['
1633 [20] CData ::= (Char* - (Char* ']]>' Char*))
1634 [21] CDEnd ::= ']]>' */
1635 static HRESULT
reader_parse_cdata(xmlreader
*reader
)
1637 FIXME("CDATA sections are not supported\n");
1641 /* [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1642 [67] Reference ::= EntityRef | CharRef
1643 [68] EntityRef ::= '&' Name ';' */
1644 static HRESULT
reader_parse_reference(xmlreader
*reader
)
1646 FIXME("References not supported\n");
1650 /* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) */
1651 static HRESULT
reader_parse_chardata(xmlreader
*reader
)
1653 FIXME("CharData not supported\n");
1657 /* [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* */
1658 static HRESULT
reader_parse_content(xmlreader
*reader
)
1660 static const WCHAR cdstartW
[] = {'<','!','[','C','D','A','T','A','[',0};
1661 static const WCHAR etagW
[] = {'<','/',0};
1662 static const WCHAR ampW
[] = {'&',0};
1664 reader_shrink(reader
);
1666 /* handle end tag here, it indicates end of content as well */
1667 if (!reader_cmp(reader
, etagW
))
1668 return reader_parse_endtag(reader
);
1670 if (!reader_cmp(reader
, commentW
))
1671 return reader_parse_comment(reader
);
1673 if (!reader_cmp(reader
, piW
))
1674 return reader_parse_pi(reader
);
1676 if (!reader_cmp(reader
, cdstartW
))
1677 return reader_parse_cdata(reader
);
1679 if (!reader_cmp(reader
, ampW
))
1680 return reader_parse_reference(reader
);
1682 if (!reader_cmp(reader
, ltW
))
1683 return reader_parse_element(reader
);
1685 /* what's left must be CharData */
1686 return reader_parse_chardata(reader
);
1689 static HRESULT
reader_parse_nextnode(xmlreader
*reader
)
1695 switch (reader
->instate
)
1697 /* if it's a first call for a new input we need to detect stream encoding */
1698 case XmlReadInState_Initial
:
1702 hr
= readerinput_growraw(reader
->input
);
1703 if (FAILED(hr
)) return hr
;
1705 /* try to detect encoding by BOM or data and set input code page */
1706 hr
= readerinput_detectencoding(reader
->input
, &enc
);
1707 TRACE("detected encoding %s, 0x%08x\n", debugstr_w(xml_encoding_map
[enc
].name
), hr
);
1708 if (FAILED(hr
)) return hr
;
1710 /* always switch first time cause we have to put something in */
1711 readerinput_switchencoding(reader
->input
, enc
);
1713 /* parse xml declaration */
1714 hr
= reader_parse_xmldecl(reader
);
1715 if (FAILED(hr
)) return hr
;
1717 readerinput_shrinkraw(reader
->input
, -1);
1718 reader
->instate
= XmlReadInState_Misc_DTD
;
1719 if (hr
== S_OK
) return hr
;
1722 case XmlReadInState_Misc_DTD
:
1723 hr
= reader_parse_misc(reader
);
1724 if (FAILED(hr
)) return hr
;
1727 reader
->instate
= XmlReadInState_DTD
;
1731 case XmlReadInState_DTD
:
1732 hr
= reader_parse_dtd(reader
);
1733 if (FAILED(hr
)) return hr
;
1737 reader
->instate
= XmlReadInState_DTD_Misc
;
1741 reader
->instate
= XmlReadInState_Element
;
1743 case XmlReadInState_DTD_Misc
:
1744 hr
= reader_parse_misc(reader
);
1745 if (FAILED(hr
)) return hr
;
1748 reader
->instate
= XmlReadInState_Element
;
1752 case XmlReadInState_Element
:
1753 return reader_parse_element(reader
);
1754 case XmlReadInState_Content
:
1755 return reader_parse_content(reader
);
1757 FIXME("internal state %d not handled\n", reader
->instate
);
1765 static HRESULT WINAPI
xmlreader_QueryInterface(IXmlReader
*iface
, REFIID riid
, void** ppvObject
)
1767 xmlreader
*This
= impl_from_IXmlReader(iface
);
1769 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
1771 if (IsEqualGUID(riid
, &IID_IUnknown
) ||
1772 IsEqualGUID(riid
, &IID_IXmlReader
))
1778 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1780 return E_NOINTERFACE
;
1783 IXmlReader_AddRef(iface
);
1788 static ULONG WINAPI
xmlreader_AddRef(IXmlReader
*iface
)
1790 xmlreader
*This
= impl_from_IXmlReader(iface
);
1791 ULONG ref
= InterlockedIncrement(&This
->ref
);
1792 TRACE("(%p)->(%d)\n", This
, ref
);
1796 static ULONG WINAPI
xmlreader_Release(IXmlReader
*iface
)
1798 xmlreader
*This
= impl_from_IXmlReader(iface
);
1799 LONG ref
= InterlockedDecrement(&This
->ref
);
1801 TRACE("(%p)->(%d)\n", This
, ref
);
1805 IMalloc
*imalloc
= This
->imalloc
;
1806 if (This
->input
) IUnknown_Release(&This
->input
->IXmlReaderInput_iface
);
1807 reader_clear_attrs(This
);
1808 reader_clear_elements(This
);
1809 reader_free_strvalues(This
);
1810 reader_free(This
, This
);
1811 if (imalloc
) IMalloc_Release(imalloc
);
1817 static HRESULT WINAPI
xmlreader_SetInput(IXmlReader
* iface
, IUnknown
*input
)
1819 xmlreader
*This
= impl_from_IXmlReader(iface
);
1820 IXmlReaderInput
*readerinput
;
1823 TRACE("(%p)->(%p)\n", This
, input
);
1827 readerinput_release_stream(This
->input
);
1828 IUnknown_Release(&This
->input
->IXmlReaderInput_iface
);
1832 This
->line
= This
->pos
= 0;
1833 reader_clear_elements(This
);
1837 /* just reset current input */
1840 This
->state
= XmlReadState_Initial
;
1844 /* now try IXmlReaderInput, ISequentialStream, IStream */
1845 hr
= IUnknown_QueryInterface(input
, &IID_IXmlReaderInput
, (void**)&readerinput
);
1848 if (readerinput
->lpVtbl
== &xmlreaderinputvtbl
)
1849 This
->input
= impl_from_IXmlReaderInput(readerinput
);
1852 ERR("got external IXmlReaderInput implementation: %p, vtbl=%p\n",
1853 readerinput
, readerinput
->lpVtbl
);
1854 IUnknown_Release(readerinput
);
1860 if (hr
!= S_OK
|| !readerinput
)
1862 /* create IXmlReaderInput basing on supplied interface */
1863 hr
= CreateXmlReaderInputWithEncodingName(input
,
1864 NULL
, NULL
, FALSE
, NULL
, &readerinput
);
1865 if (hr
!= S_OK
) return hr
;
1866 This
->input
= impl_from_IXmlReaderInput(readerinput
);
1869 /* set stream for supplied IXmlReaderInput */
1870 hr
= readerinput_query_for_stream(This
->input
);
1873 This
->state
= XmlReadState_Initial
;
1874 This
->instate
= XmlReadInState_Initial
;
1880 static HRESULT WINAPI
xmlreader_GetProperty(IXmlReader
* iface
, UINT property
, LONG_PTR
*value
)
1882 xmlreader
*This
= impl_from_IXmlReader(iface
);
1884 TRACE("(%p %u %p)\n", This
, property
, value
);
1886 if (!value
) return E_INVALIDARG
;
1890 case XmlReaderProperty_DtdProcessing
:
1891 *value
= This
->dtdmode
;
1893 case XmlReaderProperty_ReadState
:
1894 *value
= This
->state
;
1897 FIXME("Unimplemented property (%u)\n", property
);
1904 static HRESULT WINAPI
xmlreader_SetProperty(IXmlReader
* iface
, UINT property
, LONG_PTR value
)
1906 xmlreader
*This
= impl_from_IXmlReader(iface
);
1908 TRACE("(%p %u %lu)\n", iface
, property
, value
);
1912 case XmlReaderProperty_DtdProcessing
:
1913 if (value
< 0 || value
> _DtdProcessing_Last
) return E_INVALIDARG
;
1914 This
->dtdmode
= value
;
1917 FIXME("Unimplemented property (%u)\n", property
);
1924 static HRESULT WINAPI
xmlreader_Read(IXmlReader
* iface
, XmlNodeType
*nodetype
)
1926 xmlreader
*This
= impl_from_IXmlReader(iface
);
1927 XmlNodeType oldtype
= This
->nodetype
;
1930 TRACE("(%p)->(%p)\n", This
, nodetype
);
1932 if (This
->state
== XmlReadState_Closed
) return S_FALSE
;
1934 hr
= reader_parse_nextnode(This
);
1935 if (oldtype
== XmlNodeType_None
&& This
->nodetype
!= oldtype
)
1936 This
->state
= XmlReadState_Interactive
;
1937 if (hr
== S_OK
) *nodetype
= This
->nodetype
;
1942 static HRESULT WINAPI
xmlreader_GetNodeType(IXmlReader
* iface
, XmlNodeType
*node_type
)
1944 xmlreader
*This
= impl_from_IXmlReader(iface
);
1945 TRACE("(%p)->(%p)\n", This
, node_type
);
1947 /* When we're on attribute always return attribute type, container node type is kept.
1948 Note that container is not necessarily an element, and attribute doesn't mean it's
1949 an attribute in XML spec terms. */
1950 *node_type
= This
->attr
? XmlNodeType_Attribute
: This
->nodetype
;
1951 return This
->state
== XmlReadState_Closed
? S_FALSE
: S_OK
;
1954 static HRESULT WINAPI
xmlreader_MoveToFirstAttribute(IXmlReader
* iface
)
1956 xmlreader
*This
= impl_from_IXmlReader(iface
);
1958 TRACE("(%p)\n", This
);
1960 if (!This
->attr_count
) return S_FALSE
;
1961 This
->attr
= LIST_ENTRY(list_head(&This
->attrs
), struct attribute
, entry
);
1965 static HRESULT WINAPI
xmlreader_MoveToNextAttribute(IXmlReader
* iface
)
1967 xmlreader
*This
= impl_from_IXmlReader(iface
);
1968 const struct list
*next
;
1970 TRACE("(%p)\n", This
);
1972 if (!This
->attr_count
) return S_FALSE
;
1975 return IXmlReader_MoveToFirstAttribute(iface
);
1977 next
= list_next(&This
->attrs
, &This
->attr
->entry
);
1979 This
->attr
= LIST_ENTRY(next
, struct attribute
, entry
);
1981 return next
? S_OK
: S_FALSE
;
1984 static HRESULT WINAPI
xmlreader_MoveToAttributeByName(IXmlReader
* iface
,
1986 LPCWSTR namespaceUri
)
1988 FIXME("(%p %p %p): stub\n", iface
, local_name
, namespaceUri
);
1992 static HRESULT WINAPI
xmlreader_MoveToElement(IXmlReader
* iface
)
1994 xmlreader
*This
= impl_from_IXmlReader(iface
);
1996 TRACE("(%p)\n", This
);
1998 if (!This
->attr_count
) return S_FALSE
;
2003 static HRESULT WINAPI
xmlreader_GetQualifiedName(IXmlReader
* iface
, LPCWSTR
*name
, UINT
*len
)
2005 xmlreader
*This
= impl_from_IXmlReader(iface
);
2007 TRACE("(%p)->(%p %p)\n", This
, name
, len
);
2008 *name
= This
->strvalues
[StringValue_QualifiedName
].str
;
2009 *len
= This
->strvalues
[StringValue_QualifiedName
].len
;
2013 static HRESULT WINAPI
xmlreader_GetNamespaceUri(IXmlReader
* iface
,
2014 LPCWSTR
*namespaceUri
,
2015 UINT
*namespaceUri_length
)
2017 FIXME("(%p %p %p): stub\n", iface
, namespaceUri
, namespaceUri_length
);
2021 static HRESULT WINAPI
xmlreader_GetLocalName(IXmlReader
* iface
, LPCWSTR
*name
, UINT
*len
)
2023 xmlreader
*This
= impl_from_IXmlReader(iface
);
2025 TRACE("(%p)->(%p %p)\n", This
, name
, len
);
2026 *name
= This
->strvalues
[StringValue_LocalName
].str
;
2027 *len
= This
->strvalues
[StringValue_LocalName
].len
;
2031 static HRESULT WINAPI
xmlreader_GetPrefix(IXmlReader
* iface
,
2033 UINT
*prefix_length
)
2035 FIXME("(%p %p %p): stub\n", iface
, prefix
, prefix_length
);
2039 static HRESULT WINAPI
xmlreader_GetValue(IXmlReader
* iface
, LPCWSTR
*value
, UINT
*len
)
2041 xmlreader
*This
= impl_from_IXmlReader(iface
);
2043 TRACE("(%p)->(%p %p)\n", This
, value
, len
);
2045 if ((This
->nodetype
== XmlNodeType_Comment
&& !This
->strvalues
[StringValue_Value
].str
) ||
2046 is_reader_pending(This
))
2051 hr
= IXmlReader_Read(iface
, &type
);
2052 if (FAILED(hr
)) return hr
;
2054 /* return if still pending, partially read values are not reported */
2055 if (is_reader_pending(This
)) return E_PENDING
;
2058 *value
= This
->strvalues
[StringValue_Value
].str
;
2059 if (len
) *len
= This
->strvalues
[StringValue_Value
].len
;
2063 static HRESULT WINAPI
xmlreader_ReadValueChunk(IXmlReader
* iface
,
2068 FIXME("(%p %p %u %p): stub\n", iface
, buffer
, chunk_size
, read
);
2072 static HRESULT WINAPI
xmlreader_GetBaseUri(IXmlReader
* iface
,
2074 UINT
*baseUri_length
)
2076 FIXME("(%p %p %p): stub\n", iface
, baseUri
, baseUri_length
);
2080 static BOOL WINAPI
xmlreader_IsDefault(IXmlReader
* iface
)
2082 FIXME("(%p): stub\n", iface
);
2086 static BOOL WINAPI
xmlreader_IsEmptyElement(IXmlReader
* iface
)
2088 FIXME("(%p): stub\n", iface
);
2092 static HRESULT WINAPI
xmlreader_GetLineNumber(IXmlReader
* iface
, UINT
*lineNumber
)
2094 xmlreader
*This
= impl_from_IXmlReader(iface
);
2096 TRACE("(%p %p)\n", This
, lineNumber
);
2098 if (!lineNumber
) return E_INVALIDARG
;
2100 *lineNumber
= This
->line
;
2105 static HRESULT WINAPI
xmlreader_GetLinePosition(IXmlReader
* iface
, UINT
*linePosition
)
2107 xmlreader
*This
= impl_from_IXmlReader(iface
);
2109 TRACE("(%p %p)\n", This
, linePosition
);
2111 if (!linePosition
) return E_INVALIDARG
;
2113 *linePosition
= This
->pos
;
2118 static HRESULT WINAPI
xmlreader_GetAttributeCount(IXmlReader
* iface
, UINT
*count
)
2120 xmlreader
*This
= impl_from_IXmlReader(iface
);
2122 TRACE("(%p)->(%p)\n", This
, count
);
2124 if (!count
) return E_INVALIDARG
;
2126 *count
= This
->attr_count
;
2130 static HRESULT WINAPI
xmlreader_GetDepth(IXmlReader
* iface
, UINT
*depth
)
2132 xmlreader
*This
= impl_from_IXmlReader(iface
);
2133 TRACE("(%p)->(%p)\n", This
, depth
);
2134 *depth
= This
->depth
;
2138 static BOOL WINAPI
xmlreader_IsEOF(IXmlReader
* iface
)
2140 FIXME("(%p): stub\n", iface
);
2144 static const struct IXmlReaderVtbl xmlreader_vtbl
=
2146 xmlreader_QueryInterface
,
2150 xmlreader_GetProperty
,
2151 xmlreader_SetProperty
,
2153 xmlreader_GetNodeType
,
2154 xmlreader_MoveToFirstAttribute
,
2155 xmlreader_MoveToNextAttribute
,
2156 xmlreader_MoveToAttributeByName
,
2157 xmlreader_MoveToElement
,
2158 xmlreader_GetQualifiedName
,
2159 xmlreader_GetNamespaceUri
,
2160 xmlreader_GetLocalName
,
2161 xmlreader_GetPrefix
,
2163 xmlreader_ReadValueChunk
,
2164 xmlreader_GetBaseUri
,
2165 xmlreader_IsDefault
,
2166 xmlreader_IsEmptyElement
,
2167 xmlreader_GetLineNumber
,
2168 xmlreader_GetLinePosition
,
2169 xmlreader_GetAttributeCount
,
2174 /** IXmlReaderInput **/
2175 static HRESULT WINAPI
xmlreaderinput_QueryInterface(IXmlReaderInput
*iface
, REFIID riid
, void** ppvObject
)
2177 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
2179 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
2181 if (IsEqualGUID(riid
, &IID_IXmlReaderInput
) ||
2182 IsEqualGUID(riid
, &IID_IUnknown
))
2188 WARN("interface %s not implemented\n", debugstr_guid(riid
));
2190 return E_NOINTERFACE
;
2193 IUnknown_AddRef(iface
);
2198 static ULONG WINAPI
xmlreaderinput_AddRef(IXmlReaderInput
*iface
)
2200 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
2201 ULONG ref
= InterlockedIncrement(&This
->ref
);
2202 TRACE("(%p)->(%d)\n", This
, ref
);
2206 static ULONG WINAPI
xmlreaderinput_Release(IXmlReaderInput
*iface
)
2208 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
2209 LONG ref
= InterlockedDecrement(&This
->ref
);
2211 TRACE("(%p)->(%d)\n", This
, ref
);
2215 IMalloc
*imalloc
= This
->imalloc
;
2216 if (This
->input
) IUnknown_Release(This
->input
);
2217 if (This
->stream
) ISequentialStream_Release(This
->stream
);
2218 if (This
->buffer
) free_input_buffer(This
->buffer
);
2219 readerinput_free(This
, This
->baseuri
);
2220 readerinput_free(This
, This
);
2221 if (imalloc
) IMalloc_Release(imalloc
);
2227 static const struct IUnknownVtbl xmlreaderinputvtbl
=
2229 xmlreaderinput_QueryInterface
,
2230 xmlreaderinput_AddRef
,
2231 xmlreaderinput_Release
2234 HRESULT WINAPI
CreateXmlReader(REFIID riid
, void **obj
, IMalloc
*imalloc
)
2239 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
2241 if (!IsEqualGUID(riid
, &IID_IXmlReader
))
2243 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid
));
2248 reader
= IMalloc_Alloc(imalloc
, sizeof(*reader
));
2250 reader
= heap_alloc(sizeof(*reader
));
2251 if(!reader
) return E_OUTOFMEMORY
;
2253 reader
->IXmlReader_iface
.lpVtbl
= &xmlreader_vtbl
;
2255 reader
->input
= NULL
;
2256 reader
->state
= XmlReadState_Closed
;
2257 reader
->instate
= XmlReadInState_Initial
;
2258 reader
->dtdmode
= DtdProcessing_Prohibit
;
2259 reader
->line
= reader
->pos
= 0;
2260 reader
->imalloc
= imalloc
;
2261 if (imalloc
) IMalloc_AddRef(imalloc
);
2262 reader
->nodetype
= XmlNodeType_None
;
2263 list_init(&reader
->attrs
);
2264 reader
->attr_count
= 0;
2265 reader
->attr
= NULL
;
2266 list_init(&reader
->elements
);
2268 reader
->save
= NULL
;
2270 for (i
= 0; i
< StringValue_Last
; i
++)
2271 reader
->strvalues
[i
] = strval_empty
;
2273 *obj
= &reader
->IXmlReader_iface
;
2275 TRACE("returning iface %p\n", *obj
);
2280 HRESULT WINAPI
CreateXmlReaderInputWithEncodingName(IUnknown
*stream
,
2285 IXmlReaderInput
**ppInput
)
2287 xmlreaderinput
*readerinput
;
2290 TRACE("%p %p %s %d %s %p\n", stream
, imalloc
, wine_dbgstr_w(encoding
),
2291 hint
, wine_dbgstr_w(base_uri
), ppInput
);
2293 if (!stream
|| !ppInput
) return E_INVALIDARG
;
2296 readerinput
= IMalloc_Alloc(imalloc
, sizeof(*readerinput
));
2298 readerinput
= heap_alloc(sizeof(*readerinput
));
2299 if(!readerinput
) return E_OUTOFMEMORY
;
2301 readerinput
->IXmlReaderInput_iface
.lpVtbl
= &xmlreaderinputvtbl
;
2302 readerinput
->ref
= 1;
2303 readerinput
->imalloc
= imalloc
;
2304 readerinput
->stream
= NULL
;
2305 if (imalloc
) IMalloc_AddRef(imalloc
);
2306 readerinput
->encoding
= parse_encoding_name(encoding
, -1);
2307 readerinput
->hint
= hint
;
2308 readerinput
->baseuri
= readerinput_strdupW(readerinput
, base_uri
);
2309 readerinput
->pending
= 0;
2311 hr
= alloc_input_buffer(readerinput
);
2314 readerinput_free(readerinput
, readerinput
->baseuri
);
2315 readerinput_free(readerinput
, readerinput
);
2316 if (imalloc
) IMalloc_Release(imalloc
);
2319 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&readerinput
->input
);
2321 *ppInput
= &readerinput
->IXmlReaderInput_iface
;
2323 TRACE("returning iface %p\n", *ppInput
);