2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014-2018 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "xmllite_private.h"
33 #include "wine/debug.h"
34 #include "wine/list.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(xmllite
);
38 /* not defined in public headers */
39 DEFINE_GUID(IID_IXmlWriterOutput
, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
41 static const WCHAR xmlnsuriW
[] = L
"http://www.w3.org/2000/xmlns/";
46 unsigned int allocated
;
53 XmlWriterState_Initial
, /* output is not set yet */
54 XmlWriterState_Ready
, /* SetOutput() was called, ready to start */
55 XmlWriterState_InvalidEncoding
, /* SetOutput() was called, but output had invalid encoding */
56 XmlWriterState_PIDocStarted
, /* document was started with manually added 'xml' PI */
57 XmlWriterState_DocStarted
, /* document was started with WriteStartDocument() */
58 XmlWriterState_ElemStarted
, /* writing element */
59 XmlWriterState_Content
, /* content is accepted at this point */
60 XmlWriterState_DocClosed
/* WriteEndDocument was called */
65 IXmlWriterOutput IXmlWriterOutput_iface
;
68 ISequentialStream
*stream
;
70 xml_encoding encoding
;
71 WCHAR
*encoding_name
; /* exactly as specified on output creation */
72 struct output_buffer buffer
;
76 static const struct IUnknownVtbl xmlwriteroutputvtbl
;
82 unsigned int len
; /* qname length in chars */
93 struct element
*element
;
96 typedef struct _xmlwriter
98 IXmlWriter IXmlWriter_iface
;
101 xmlwriteroutput
*output
;
102 unsigned int indent_level
;
106 XmlConformanceLevel conformance
;
107 XmlWriterState state
;
108 struct list elements
;
109 DWORD bomwritten
: 1;
110 DWORD starttagopen
: 1;
114 static inline xmlwriter
*impl_from_IXmlWriter(IXmlWriter
*iface
)
116 return CONTAINING_RECORD(iface
, xmlwriter
, IXmlWriter_iface
);
119 static inline xmlwriteroutput
*impl_from_IXmlWriterOutput(IXmlWriterOutput
*iface
)
121 return CONTAINING_RECORD(iface
, xmlwriteroutput
, IXmlWriterOutput_iface
);
124 static const char *debugstr_writer_prop(XmlWriterProperty prop
)
126 static const char * const prop_names
[] =
131 "OmitXmlDeclaration",
135 if (prop
> _XmlWriterProperty_Last
)
136 return wine_dbg_sprintf("unknown property=%d", prop
);
138 return prop_names
[prop
];
141 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
142 const WCHAR
*encoding_name
, xmlwriteroutput
**out
);
144 /* writer output memory allocation functions */
145 static inline void *writeroutput_alloc(xmlwriteroutput
*output
, size_t len
)
147 return m_alloc(output
->imalloc
, len
);
150 static inline void writeroutput_free(xmlwriteroutput
*output
, void *mem
)
152 m_free(output
->imalloc
, mem
);
155 static inline void *writeroutput_realloc(xmlwriteroutput
*output
, void *mem
, size_t len
)
157 return m_realloc(output
->imalloc
, mem
, len
);
160 /* writer memory allocation functions */
161 static inline void *writer_alloc(const xmlwriter
*writer
, size_t len
)
163 return m_alloc(writer
->imalloc
, len
);
166 static inline void writer_free(const xmlwriter
*writer
, void *mem
)
168 m_free(writer
->imalloc
, mem
);
171 static BOOL
is_empty_string(const WCHAR
*str
)
173 return !str
|| !*str
;
176 static struct element
*alloc_element(xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*local
)
181 ret
= writer_alloc(writer
, sizeof(*ret
));
182 if (!ret
) return ret
;
184 len
= is_empty_string(prefix
) ? 0 : lstrlenW(prefix
) + 1 /* ':' */;
185 len
+= lstrlenW(local
);
187 ret
->qname
= writer_alloc(writer
, (len
+ 1)*sizeof(WCHAR
));
191 writer_free(writer
, ret
);
196 if (is_empty_string(prefix
))
200 lstrcpyW(ret
->qname
, prefix
);
201 lstrcatW(ret
->qname
, L
":");
203 lstrcatW(ret
->qname
, local
);
209 static void writer_free_element(xmlwriter
*writer
, struct element
*element
)
213 LIST_FOR_EACH_ENTRY_SAFE(ns
, ns2
, &element
->ns
, struct ns
, entry
)
215 list_remove(&ns
->entry
);
216 writer_free(writer
, ns
->prefix
);
217 writer_free(writer
, ns
->uri
);
218 writer_free(writer
, ns
);
221 writer_free(writer
, element
->qname
);
222 writer_free(writer
, element
);
225 static void writer_free_element_stack(xmlwriter
*writer
)
227 struct element
*element
, *element2
;
229 LIST_FOR_EACH_ENTRY_SAFE(element
, element2
, &writer
->elements
, struct element
, entry
)
231 list_remove(&element
->entry
);
232 writer_free_element(writer
, element
);
236 static void writer_push_element(xmlwriter
*writer
, struct element
*element
)
238 list_add_head(&writer
->elements
, &element
->entry
);
241 static struct element
*pop_element(xmlwriter
*writer
)
243 struct element
*element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
246 list_remove(&element
->entry
);
251 static WCHAR
*writer_strndupW(const xmlwriter
*writer
, const WCHAR
*str
, int len
)
261 ret
= writer_alloc(writer
, (len
+ 1) * sizeof(WCHAR
));
264 memcpy(ret
, str
, len
* sizeof(WCHAR
));
271 static WCHAR
*writer_strdupW(const xmlwriter
*writer
, const WCHAR
*str
)
273 return writer_strndupW(writer
, str
, -1);
276 static struct ns
*writer_push_ns(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
, const WCHAR
*uri
)
278 struct element
*element
;
281 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
285 if ((ns
= writer_alloc(writer
, sizeof(*ns
))))
287 ns
->prefix
= writer_strndupW(writer
, prefix
, prefix_len
);
288 ns
->prefix_len
= prefix_len
;
289 ns
->uri
= writer_strdupW(writer
, uri
);
291 ns
->element
= element
;
292 list_add_tail(&element
->ns
, &ns
->entry
);
298 static struct ns
*writer_find_ns_current(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
300 struct element
*element
;
303 if (is_empty_string(prefix
) || is_empty_string(uri
))
306 element
= LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
);
308 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
310 if (!wcscmp(uri
, ns
->uri
) && !wcscmp(prefix
, ns
->prefix
))
317 static struct ns
*writer_find_ns(const xmlwriter
*writer
, const WCHAR
*prefix
, const WCHAR
*uri
)
319 struct element
*element
;
322 if (is_empty_string(prefix
) && is_empty_string(uri
))
325 LIST_FOR_EACH_ENTRY(element
, &writer
->elements
, struct element
, entry
)
327 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
331 if (!ns
->prefix
) continue;
332 if (!wcscmp(ns
->prefix
, prefix
))
335 else if (!wcscmp(uri
, ns
->uri
))
337 if (prefix
&& !*prefix
)
339 if (!prefix
|| !wcscmp(prefix
, ns
->prefix
))
348 static HRESULT
is_valid_ncname(const WCHAR
*str
, int *out
)
359 if (!is_ncnamechar(*str
))
360 return WC_E_NAMECHARACTER
;
369 static HRESULT
is_valid_name(const WCHAR
*str
, unsigned int *out
)
371 unsigned int len
= 1;
378 if (!is_namestartchar(*str
++))
379 return WC_E_NAMECHARACTER
;
383 if (!is_namechar(*str
))
384 return WC_E_NAMECHARACTER
;
393 static HRESULT
is_valid_pubid(const WCHAR
*str
, unsigned int *out
)
395 unsigned int len
= 0;
404 if (!is_pubchar(*str
++))
405 return WC_E_PUBLICID
;
414 static HRESULT
init_output_buffer(xmlwriteroutput
*output
)
416 struct output_buffer
*buffer
= &output
->buffer
;
417 const int initial_len
= 0x2000;
421 if (FAILED(hr
= get_code_page(output
->encoding
, &cp
)))
422 WARN("Failed to get code page for specified encoding.\n");
424 buffer
->data
= writeroutput_alloc(output
, initial_len
);
425 if (!buffer
->data
) return E_OUTOFMEMORY
;
427 memset(buffer
->data
, 0, 4);
428 buffer
->allocated
= initial_len
;
430 buffer
->codepage
= cp
;
435 static void free_output_buffer(xmlwriteroutput
*output
)
437 struct output_buffer
*buffer
= &output
->buffer
;
438 writeroutput_free(output
, buffer
->data
);
440 buffer
->allocated
= 0;
444 static HRESULT
grow_output_buffer(xmlwriteroutput
*output
, int length
)
446 struct output_buffer
*buffer
= &output
->buffer
;
447 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
448 if (buffer
->allocated
< buffer
->written
+ length
+ 4) {
449 int grown_size
= max(2*buffer
->allocated
, buffer
->allocated
+ length
);
450 char *ptr
= writeroutput_realloc(output
, buffer
->data
, grown_size
);
451 if (!ptr
) return E_OUTOFMEMORY
;
453 buffer
->allocated
= grown_size
;
459 static HRESULT
write_output_buffer(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
461 struct output_buffer
*buffer
= &output
->buffer
;
466 if (buffer
->codepage
== 1200) {
467 /* For UTF-16 encoding just copy. */
468 length
= len
== -1 ? lstrlenW(data
) : len
;
470 length
*= sizeof(WCHAR
);
472 hr
= grow_output_buffer(output
, length
);
473 if (FAILED(hr
)) return hr
;
474 ptr
= buffer
->data
+ buffer
->written
;
476 memcpy(ptr
, data
, length
);
477 buffer
->written
+= length
;
479 /* null termination */
484 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, NULL
, 0, NULL
, NULL
);
485 hr
= grow_output_buffer(output
, length
);
486 if (FAILED(hr
)) return hr
;
487 ptr
= buffer
->data
+ buffer
->written
;
488 length
= WideCharToMultiByte(buffer
->codepage
, 0, data
, len
, ptr
, length
, NULL
, NULL
);
489 buffer
->written
+= len
== -1 ? length
-1 : length
;
491 output
->written
= length
!= 0;
496 static HRESULT
write_output_buffer_char(xmlwriteroutput
*output
, WCHAR ch
)
498 return write_output_buffer(output
, &ch
, 1);
501 static HRESULT
write_output_buffer_quoted(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
503 write_output_buffer_char(output
, '"');
504 if (!is_empty_string(data
))
505 write_output_buffer(output
, data
, len
);
506 write_output_buffer_char(output
, '"');
510 /* TODO: test if we need to validate char range */
511 static HRESULT
write_output_qname(xmlwriteroutput
*output
, const WCHAR
*prefix
, int prefix_len
,
512 const WCHAR
*local_name
, int local_len
)
514 assert(prefix_len
>= 0 && local_len
>= 0);
517 write_output_buffer(output
, prefix
, prefix_len
);
519 if (prefix_len
&& local_len
)
520 write_output_buffer_char(output
, ':');
522 write_output_buffer(output
, local_name
, local_len
);
527 static void writeroutput_release_stream(xmlwriteroutput
*writeroutput
)
529 if (writeroutput
->stream
) {
530 ISequentialStream_Release(writeroutput
->stream
);
531 writeroutput
->stream
= NULL
;
535 static inline HRESULT
writeroutput_query_for_stream(xmlwriteroutput
*writeroutput
)
539 writeroutput_release_stream(writeroutput
);
540 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_IStream
, (void**)&writeroutput
->stream
);
542 hr
= IUnknown_QueryInterface(writeroutput
->output
, &IID_ISequentialStream
, (void**)&writeroutput
->stream
);
547 static HRESULT
writeroutput_flush_stream(xmlwriteroutput
*output
)
549 struct output_buffer
*buffer
;
550 ULONG written
, offset
= 0;
553 if (!output
|| !output
->stream
)
556 buffer
= &output
->buffer
;
558 /* It will loop forever until everything is written or an error occurred. */
561 hr
= ISequentialStream_Write(output
->stream
, buffer
->data
+ offset
, buffer
->written
, &written
);
563 WARN("write to stream failed %#lx.\n", hr
);
569 buffer
->written
-= written
;
570 } while (buffer
->written
> 0);
575 static HRESULT
write_encoding_bom(xmlwriter
*writer
)
577 if (!writer
->bom
|| writer
->bomwritten
) return S_OK
;
579 if (writer
->output
->encoding
== XmlEncoding_UTF16
) {
580 static const char utf16bom
[] = {0xff, 0xfe};
581 struct output_buffer
*buffer
= &writer
->output
->buffer
;
582 int len
= sizeof(utf16bom
);
585 hr
= grow_output_buffer(writer
->output
, len
);
586 if (FAILED(hr
)) return hr
;
587 memcpy(buffer
->data
+ buffer
->written
, utf16bom
, len
);
588 buffer
->written
+= len
;
591 writer
->bomwritten
= TRUE
;
595 static const WCHAR
*get_output_encoding_name(xmlwriteroutput
*output
)
597 if (output
->encoding_name
)
598 return output
->encoding_name
;
600 return get_encoding_name(output
->encoding
);
603 static HRESULT
write_xmldecl(xmlwriter
*writer
, XmlStandalone standalone
)
605 write_encoding_bom(writer
);
606 writer
->state
= XmlWriterState_DocStarted
;
607 if (writer
->omitxmldecl
) return S_OK
;
610 write_output_buffer(writer
->output
, L
"<?xml version=\"1.0\"", 19);
613 write_output_buffer(writer
->output
, L
" encoding=", 10);
614 write_output_buffer_quoted(writer
->output
, get_output_encoding_name(writer
->output
), -1);
617 if (standalone
== XmlStandalone_Omit
)
618 write_output_buffer(writer
->output
, L
"?>", 2);
621 write_output_buffer(writer
->output
, L
" standalone=\"", 13);
622 if (standalone
== XmlStandalone_Yes
)
623 write_output_buffer(writer
->output
, L
"yes\"?>", 6);
625 write_output_buffer(writer
->output
, L
"no\"?>", 5);
631 static void writer_output_ns(xmlwriter
*writer
, struct element
*element
)
635 LIST_FOR_EACH_ENTRY(ns
, &element
->ns
, struct ns
, entry
)
640 write_output_qname(writer
->output
, L
" xmlns", 6, ns
->prefix
, ns
->prefix_len
);
641 write_output_buffer_char(writer
->output
, '=');
642 write_output_buffer_quoted(writer
->output
, ns
->uri
, -1);
646 static HRESULT
writer_close_starttag(xmlwriter
*writer
)
650 if (!writer
->starttagopen
) return S_OK
;
652 writer_output_ns(writer
, LIST_ENTRY(list_head(&writer
->elements
), struct element
, entry
));
653 hr
= write_output_buffer_char(writer
->output
, '>');
654 writer
->starttagopen
= 0;
658 static void writer_inc_indent(xmlwriter
*writer
)
660 writer
->indent_level
++;
663 static void writer_dec_indent(xmlwriter
*writer
)
665 if (writer
->indent_level
)
666 writer
->indent_level
--;
669 static void write_node_indent(xmlwriter
*writer
)
671 unsigned int indent_level
= writer
->indent_level
;
673 if (!writer
->indent
|| writer
->textnode
)
675 writer
->textnode
= 0;
679 /* Do state check to prevent newline inserted after BOM. It is assumed that
680 state does not change between writing BOM and inserting indentation. */
681 if (writer
->output
->written
&& writer
->state
!= XmlWriterState_Ready
)
682 write_output_buffer(writer
->output
, L
"\r\n", 2);
683 while (indent_level
--)
684 write_output_buffer(writer
->output
, L
" ", 2);
686 writer
->textnode
= 0;
689 static HRESULT WINAPI
xmlwriter_QueryInterface(IXmlWriter
*iface
, REFIID riid
, void **ppvObject
)
691 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
693 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
695 if (IsEqualGUID(riid
, &IID_IXmlWriter
) ||
696 IsEqualGUID(riid
, &IID_IUnknown
))
702 FIXME("interface %s is not supported\n", debugstr_guid(riid
));
704 return E_NOINTERFACE
;
707 IXmlWriter_AddRef(iface
);
712 static ULONG WINAPI
xmlwriter_AddRef(IXmlWriter
*iface
)
714 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
715 ULONG ref
= InterlockedIncrement(&writer
->ref
);
716 TRACE("%p, refcount %lu.\n", iface
, ref
);
720 static ULONG WINAPI
xmlwriter_Release(IXmlWriter
*iface
)
722 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
723 ULONG ref
= InterlockedDecrement(&writer
->ref
);
725 TRACE("%p, refcount %lu.\n", iface
, ref
);
729 IMalloc
*imalloc
= writer
->imalloc
;
731 writeroutput_flush_stream(writer
->output
);
733 IUnknown_Release(&writer
->output
->IXmlWriterOutput_iface
);
735 writer_free_element_stack(writer
);
737 writer_free(writer
, writer
);
738 if (imalloc
) IMalloc_Release(imalloc
);
744 /*** IXmlWriter methods ***/
745 static HRESULT WINAPI
xmlwriter_SetOutput(IXmlWriter
*iface
, IUnknown
*output
)
747 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
748 IXmlWriterOutput
*writeroutput
;
751 TRACE("(%p)->(%p)\n", This
, output
);
754 writeroutput_release_stream(This
->output
);
755 IUnknown_Release(&This
->output
->IXmlWriterOutput_iface
);
757 This
->bomwritten
= 0;
759 This
->indent_level
= 0;
760 writer_free_element_stack(This
);
763 /* just reset current output */
765 This
->state
= XmlWriterState_Initial
;
769 /* now try IXmlWriterOutput, ISequentialStream, IStream */
770 hr
= IUnknown_QueryInterface(output
, &IID_IXmlWriterOutput
, (void**)&writeroutput
);
772 if (writeroutput
->lpVtbl
== &xmlwriteroutputvtbl
)
773 This
->output
= impl_from_IXmlWriterOutput(writeroutput
);
775 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
776 writeroutput
, writeroutput
->lpVtbl
);
777 IUnknown_Release(writeroutput
);
782 if (hr
!= S_OK
|| !writeroutput
) {
783 /* Create output for given stream. */
784 hr
= create_writer_output(output
, This
->imalloc
, XmlEncoding_UTF8
, NULL
, &This
->output
);
789 if (This
->output
->encoding
== XmlEncoding_Unknown
)
790 This
->state
= XmlWriterState_InvalidEncoding
;
792 This
->state
= XmlWriterState_Ready
;
793 return writeroutput_query_for_stream(This
->output
);
796 static HRESULT WINAPI
xmlwriter_GetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR
*value
)
798 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
800 TRACE("(%p)->(%s %p)\n", This
, debugstr_writer_prop(property
), value
);
802 if (!value
) return E_INVALIDARG
;
806 case XmlWriterProperty_Indent
:
807 *value
= This
->indent
;
809 case XmlWriterProperty_ByteOrderMark
:
812 case XmlWriterProperty_OmitXmlDeclaration
:
813 *value
= This
->omitxmldecl
;
815 case XmlWriterProperty_ConformanceLevel
:
816 *value
= This
->conformance
;
819 FIXME("Unimplemented property (%u)\n", property
);
826 static HRESULT WINAPI
xmlwriter_SetProperty(IXmlWriter
*iface
, UINT property
, LONG_PTR value
)
828 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
830 TRACE("%p, %s, %Id.\n", iface
, debugstr_writer_prop(property
), value
);
834 case XmlWriterProperty_Indent
:
835 writer
->indent
= !!value
;
837 case XmlWriterProperty_ByteOrderMark
:
838 writer
->bom
= !!value
;
840 case XmlWriterProperty_OmitXmlDeclaration
:
841 writer
->omitxmldecl
= !!value
;
844 FIXME("Unimplemented property (%u)\n", property
);
851 static HRESULT
writer_write_attribute(IXmlWriter
*writer
, IXmlReader
*reader
, BOOL write_default_attributes
)
853 const WCHAR
*prefix
, *local
, *uri
, *value
;
856 if (IXmlReader_IsDefault(reader
) && !write_default_attributes
)
859 if (FAILED(hr
= IXmlReader_GetPrefix(reader
, &prefix
, NULL
))) return hr
;
860 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &local
, NULL
))) return hr
;
861 if (FAILED(hr
= IXmlReader_GetNamespaceUri(reader
, &uri
, NULL
))) return hr
;
862 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
863 return IXmlWriter_WriteAttributeString(writer
, prefix
, local
, uri
, value
);
866 static HRESULT WINAPI
xmlwriter_WriteAttributes(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
868 XmlNodeType node_type
;
871 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
873 if (FAILED(hr
= IXmlReader_GetNodeType(reader
, &node_type
))) return hr
;
877 case XmlNodeType_Element
:
878 case XmlNodeType_XmlDeclaration
:
879 case XmlNodeType_Attribute
:
880 if (node_type
!= XmlNodeType_Attribute
)
882 if (FAILED(hr
= IXmlReader_MoveToFirstAttribute(reader
))) return hr
;
883 if (hr
== S_FALSE
) return S_OK
;
885 if (FAILED(hr
= writer_write_attribute(iface
, reader
, write_default_attributes
))) return hr
;
886 while (IXmlReader_MoveToNextAttribute(reader
) == S_OK
)
888 if (FAILED(hr
= writer_write_attribute(iface
, reader
, write_default_attributes
))) break;
890 if (node_type
!= XmlNodeType_Attribute
&& SUCCEEDED(hr
))
891 hr
= IXmlReader_MoveToElement(reader
);
894 WARN("Unexpected node type %d.\n", node_type
);
901 static void write_output_attribute(xmlwriter
*writer
, const WCHAR
*prefix
, int prefix_len
,
902 const WCHAR
*local
, int local_len
, const WCHAR
*value
)
904 write_output_buffer_char(writer
->output
, ' ');
905 write_output_qname(writer
->output
, prefix
, prefix_len
, local
, local_len
);
906 write_output_buffer_char(writer
->output
, '=');
907 write_output_buffer_quoted(writer
->output
, value
, -1);
910 static BOOL
is_valid_xml_space_value(const WCHAR
*value
)
912 return value
&& (!wcscmp(value
, L
"preserve") || !wcscmp(value
, L
"default"));
915 static HRESULT WINAPI
xmlwriter_WriteAttributeString(IXmlWriter
*iface
, LPCWSTR prefix
,
916 LPCWSTR local
, LPCWSTR uri
, LPCWSTR value
)
918 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
919 BOOL is_xmlns_prefix
, is_xmlns_local
;
920 int prefix_len
, local_len
;
924 TRACE("%p, %s, %s, %s, %s.\n", iface
, debugstr_w(prefix
), debugstr_w(local
), debugstr_w(uri
), debugstr_w(value
));
926 switch (writer
->state
)
928 case XmlWriterState_Initial
:
930 case XmlWriterState_Ready
:
931 case XmlWriterState_DocClosed
:
932 writer
->state
= XmlWriterState_DocClosed
;
933 return WR_E_INVALIDACTION
;
934 case XmlWriterState_InvalidEncoding
:
935 return MX_E_ENCODING
;
941 is_xmlns_prefix
= prefix
&& !wcscmp(prefix
, L
"xmlns");
942 if (is_xmlns_prefix
&& is_empty_string(uri
) && is_empty_string(local
))
943 return WR_E_NSPREFIXDECLARED
;
945 if (is_empty_string(local
))
948 /* Validate prefix and local name */
949 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
952 if (FAILED(hr
= is_valid_ncname(local
, &local_len
)))
955 is_xmlns_local
= !wcscmp(local
, L
"xmlns");
957 /* Trivial case, no prefix. */
958 if (prefix_len
== 0 && is_empty_string(uri
))
960 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
964 /* Predefined "xml" prefix. */
965 if (prefix_len
&& !wcscmp(prefix
, L
"xml"))
967 /* Valid "space" value is enforced. */
968 if (!wcscmp(local
, L
"space") && !is_valid_xml_space_value(value
))
969 return WR_E_INVALIDXMLSPACE
;
971 /* Redefinition is not allowed. */
972 if (!is_empty_string(uri
))
973 return WR_E_XMLPREFIXDECLARATION
;
975 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
980 if (is_xmlns_prefix
|| (prefix_len
== 0 && uri
&& !wcscmp(uri
, xmlnsuriW
)))
982 if (prefix_len
&& !is_empty_string(uri
))
983 return WR_E_XMLNSPREFIXDECLARATION
;
985 /* Look for exact match defined in current element, and write it out. */
986 if (!(ns
= writer_find_ns_current(writer
, prefix
, value
)))
987 ns
= writer_push_ns(writer
, local
, local_len
, value
);
990 write_output_attribute(writer
, L
"xmlns", 5, local
, local_len
, value
);
995 /* Ignore prefix if URI wasn't specified. */
996 if (is_xmlns_local
&& is_empty_string(uri
))
998 write_output_attribute(writer
, NULL
, 0, L
"xmlns", 5, value
);
1002 if (!(ns
= writer_find_ns(writer
, prefix
, uri
)))
1004 if (is_empty_string(prefix
) && !is_empty_string(uri
))
1006 FIXME("Prefix autogeneration is not implemented.\n");
1009 if (!is_empty_string(uri
))
1010 ns
= writer_push_ns(writer
, prefix
, prefix_len
, uri
);
1014 write_output_attribute(writer
, ns
->prefix
, ns
->prefix_len
, local
, local_len
, value
);
1016 write_output_attribute(writer
, prefix
, prefix_len
, local
, local_len
, value
);
1021 static void write_cdata_section(xmlwriteroutput
*output
, const WCHAR
*data
, int len
)
1023 write_output_buffer(output
, L
"<![CDATA[", 9);
1025 write_output_buffer(output
, data
, len
);
1026 write_output_buffer(output
, L
"]]>", 3);
1029 static HRESULT WINAPI
xmlwriter_WriteCData(IXmlWriter
*iface
, LPCWSTR data
)
1031 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1034 TRACE("%p %s\n", This
, debugstr_w(data
));
1036 switch (This
->state
)
1038 case XmlWriterState_Initial
:
1039 return E_UNEXPECTED
;
1040 case XmlWriterState_ElemStarted
:
1041 writer_close_starttag(This
);
1043 case XmlWriterState_Ready
:
1044 case XmlWriterState_DocClosed
:
1045 This
->state
= XmlWriterState_DocClosed
;
1046 return WR_E_INVALIDACTION
;
1047 case XmlWriterState_InvalidEncoding
:
1048 return MX_E_ENCODING
;
1053 len
= data
? lstrlenW(data
) : 0;
1055 write_node_indent(This
);
1057 write_cdata_section(This
->output
, NULL
, 0);
1062 const WCHAR
*str
= wcsstr(data
, L
"]]>");
1065 write_cdata_section(This
->output
, data
, str
- data
);
1070 write_cdata_section(This
->output
, data
, len
);
1079 static HRESULT WINAPI
xmlwriter_WriteCharEntity(IXmlWriter
*iface
, WCHAR ch
)
1081 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1084 TRACE("%p %#x\n", This
, ch
);
1086 switch (This
->state
)
1088 case XmlWriterState_Initial
:
1089 return E_UNEXPECTED
;
1090 case XmlWriterState_InvalidEncoding
:
1091 return MX_E_ENCODING
;
1092 case XmlWriterState_ElemStarted
:
1093 writer_close_starttag(This
);
1095 case XmlWriterState_DocClosed
:
1096 return WR_E_INVALIDACTION
;
1101 swprintf(bufW
, ARRAY_SIZE(bufW
), L
"&#x%x;", ch
);
1102 write_output_buffer(This
->output
, bufW
, -1);
1107 static HRESULT
writer_get_next_write_count(const WCHAR
*str
, unsigned int length
, unsigned int *count
)
1109 if (!is_char(*str
)) return WC_E_XMLCHARACTER
;
1111 if (IS_HIGH_SURROGATE(*str
))
1113 if (length
< 2 || !IS_LOW_SURROGATE(*(str
+ 1)))
1114 return WR_E_INVALIDSURROGATEPAIR
;
1118 else if (IS_LOW_SURROGATE(*str
))
1119 return WR_E_INVALIDSURROGATEPAIR
;
1126 static HRESULT
write_escaped_char(xmlwriter
*writer
, const WCHAR
*string
, unsigned int count
)
1133 hr
= write_output_buffer(writer
->output
, L
"<", 4);
1136 hr
= write_output_buffer(writer
->output
, L
"&", 5);
1139 hr
= write_output_buffer(writer
->output
, L
">", 4);
1142 hr
= write_output_buffer(writer
->output
, string
, count
);
1148 static HRESULT
write_escaped_string(xmlwriter
*writer
, const WCHAR
*string
, unsigned int length
)
1157 if (FAILED(hr
= writer_get_next_write_count(string
, ~0u, &count
))) return hr
;
1158 if (FAILED(hr
= write_escaped_char(writer
, string
, count
))) return hr
;
1167 if (FAILED(hr
= writer_get_next_write_count(string
, length
, &count
))) return hr
;
1168 if (FAILED(hr
= write_escaped_char(writer
, string
, count
))) return hr
;
1178 static HRESULT WINAPI
xmlwriter_WriteChars(IXmlWriter
*iface
, const WCHAR
*characters
, UINT length
)
1180 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
1182 TRACE("%p, %s, %d.\n", iface
, debugstr_wn(characters
, length
), length
);
1184 if ((characters
== NULL
&& length
!= 0))
1185 return E_INVALIDARG
;
1190 switch (writer
->state
)
1192 case XmlWriterState_Initial
:
1193 return E_UNEXPECTED
;
1194 case XmlWriterState_InvalidEncoding
:
1195 return MX_E_ENCODING
;
1196 case XmlWriterState_ElemStarted
:
1197 writer_close_starttag(writer
);
1199 case XmlWriterState_Ready
:
1200 case XmlWriterState_DocClosed
:
1201 writer
->state
= XmlWriterState_DocClosed
;
1202 return WR_E_INVALIDACTION
;
1207 writer
->textnode
= 1;
1208 return write_escaped_string(writer
, characters
, length
);
1211 static HRESULT WINAPI
xmlwriter_WriteComment(IXmlWriter
*iface
, LPCWSTR comment
)
1213 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1215 TRACE("%p %s\n", This
, debugstr_w(comment
));
1217 switch (This
->state
)
1219 case XmlWriterState_Initial
:
1220 return E_UNEXPECTED
;
1221 case XmlWriterState_InvalidEncoding
:
1222 return MX_E_ENCODING
;
1223 case XmlWriterState_ElemStarted
:
1224 writer_close_starttag(This
);
1226 case XmlWriterState_DocClosed
:
1227 return WR_E_INVALIDACTION
;
1232 write_node_indent(This
);
1233 write_output_buffer(This
->output
, L
"<!--", 4);
1235 int len
= lstrlenW(comment
), i
;
1237 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1240 for (i
= 0; i
< len
; i
++) {
1241 write_output_buffer(This
->output
, comment
+ i
, 1);
1242 if (comment
[i
] == '-' && (i
+ 1 < len
) && comment
[i
+1] == '-')
1243 write_output_buffer_char(This
->output
, ' ');
1247 write_output_buffer(This
->output
, comment
, len
);
1249 if (len
&& comment
[len
-1] == '-')
1250 write_output_buffer_char(This
->output
, ' ');
1252 write_output_buffer(This
->output
, L
"-->", 3);
1257 static HRESULT WINAPI
xmlwriter_WriteDocType(IXmlWriter
*iface
, LPCWSTR name
, LPCWSTR pubid
,
1258 LPCWSTR sysid
, LPCWSTR subset
)
1260 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1261 unsigned int name_len
, pubid_len
;
1264 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(pubid
), wine_dbgstr_w(sysid
),
1265 wine_dbgstr_w(subset
));
1267 switch (This
->state
)
1269 case XmlWriterState_Initial
:
1270 return E_UNEXPECTED
;
1271 case XmlWriterState_InvalidEncoding
:
1272 return MX_E_ENCODING
;
1273 case XmlWriterState_Content
:
1274 case XmlWriterState_DocClosed
:
1275 return WR_E_INVALIDACTION
;
1280 if (is_empty_string(name
))
1281 return E_INVALIDARG
;
1283 if (FAILED(hr
= is_valid_name(name
, &name_len
)))
1286 if (FAILED(hr
= is_valid_pubid(pubid
, &pubid_len
)))
1289 write_output_buffer(This
->output
, L
"<!DOCTYPE ", 10);
1290 write_output_buffer(This
->output
, name
, name_len
);
1294 write_output_buffer(This
->output
, L
" PUBLIC ", 8);
1295 write_output_buffer_quoted(This
->output
, pubid
, pubid_len
);
1296 write_output_buffer_char(This
->output
, ' ');
1297 write_output_buffer_quoted(This
->output
, sysid
, -1);
1301 write_output_buffer(This
->output
, L
" SYSTEM ", 8);
1302 write_output_buffer_quoted(This
->output
, sysid
, -1);
1307 write_output_buffer_char(This
->output
, ' ');
1308 write_output_buffer_char(This
->output
, '[');
1309 write_output_buffer(This
->output
, subset
, -1);
1310 write_output_buffer_char(This
->output
, ']');
1312 write_output_buffer_char(This
->output
, '>');
1314 This
->state
= XmlWriterState_Content
;
1319 static HRESULT WINAPI
xmlwriter_WriteElementString(IXmlWriter
*iface
, LPCWSTR prefix
,
1320 LPCWSTR local_name
, LPCWSTR uri
, LPCWSTR value
)
1322 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1323 int prefix_len
, local_len
;
1327 TRACE("(%p)->(%s %s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
),
1328 wine_dbgstr_w(uri
), wine_dbgstr_w(value
));
1330 switch (This
->state
)
1332 case XmlWriterState_Initial
:
1333 return E_UNEXPECTED
;
1334 case XmlWriterState_InvalidEncoding
:
1335 return MX_E_ENCODING
;
1336 case XmlWriterState_ElemStarted
:
1337 writer_close_starttag(This
);
1339 case XmlWriterState_DocClosed
:
1340 return WR_E_INVALIDACTION
;
1346 return E_INVALIDARG
;
1348 /* Validate prefix and local name */
1349 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1352 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1355 ns
= writer_find_ns(This
, prefix
, uri
);
1356 if (!ns
&& !is_empty_string(prefix
) && is_empty_string(uri
))
1357 return WR_E_NSPREFIXWITHEMPTYNSURI
;
1359 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1362 return WR_E_XMLNSPREFIXDECLARATION
;
1364 if (!is_empty_string(prefix
))
1365 return WR_E_XMLNSURIDECLARATION
;
1368 write_encoding_bom(This
);
1369 write_node_indent(This
);
1371 write_output_buffer_char(This
->output
, '<');
1373 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1375 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1377 if (!ns
&& (prefix_len
|| !is_empty_string(uri
)))
1379 write_output_qname(This
->output
, L
" xmlns", 6, prefix
, prefix_len
);
1380 write_output_buffer_char(This
->output
, '=');
1381 write_output_buffer_quoted(This
->output
, uri
, -1);
1386 write_output_buffer_char(This
->output
, '>');
1387 write_output_buffer(This
->output
, value
, -1);
1388 write_output_buffer(This
->output
, L
"</", 2);
1389 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1390 write_output_buffer_char(This
->output
, '>');
1393 write_output_buffer(This
->output
, L
" />", 3);
1395 This
->state
= XmlWriterState_Content
;
1400 static HRESULT WINAPI
xmlwriter_WriteEndDocument(IXmlWriter
*iface
)
1402 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1404 TRACE("%p\n", This
);
1406 switch (This
->state
)
1408 case XmlWriterState_Initial
:
1409 return E_UNEXPECTED
;
1410 case XmlWriterState_Ready
:
1411 case XmlWriterState_DocClosed
:
1412 This
->state
= XmlWriterState_DocClosed
;
1413 return WR_E_INVALIDACTION
;
1414 case XmlWriterState_InvalidEncoding
:
1415 return MX_E_ENCODING
;
1420 /* empty element stack */
1421 while (IXmlWriter_WriteEndElement(iface
) == S_OK
)
1424 This
->state
= XmlWriterState_DocClosed
;
1428 static HRESULT WINAPI
xmlwriter_WriteEndElement(IXmlWriter
*iface
)
1430 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1431 struct element
*element
;
1433 TRACE("%p\n", This
);
1435 switch (This
->state
)
1437 case XmlWriterState_Initial
:
1438 return E_UNEXPECTED
;
1439 case XmlWriterState_Ready
:
1440 case XmlWriterState_DocClosed
:
1441 This
->state
= XmlWriterState_DocClosed
;
1442 return WR_E_INVALIDACTION
;
1443 case XmlWriterState_InvalidEncoding
:
1444 return MX_E_ENCODING
;
1449 element
= pop_element(This
);
1451 return WR_E_INVALIDACTION
;
1453 writer_dec_indent(This
);
1455 if (This
->starttagopen
)
1457 writer_output_ns(This
, element
);
1458 write_output_buffer(This
->output
, L
" />", 3);
1459 This
->starttagopen
= 0;
1463 /* Write full end tag. */
1464 write_node_indent(This
);
1465 write_output_buffer(This
->output
, L
"</", 2);
1466 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1467 write_output_buffer_char(This
->output
, '>');
1469 writer_free_element(This
, element
);
1474 static HRESULT WINAPI
xmlwriter_WriteEntityRef(IXmlWriter
*iface
, LPCWSTR pwszName
)
1476 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1478 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1480 switch (This
->state
)
1482 case XmlWriterState_Initial
:
1483 return E_UNEXPECTED
;
1484 case XmlWriterState_InvalidEncoding
:
1485 return MX_E_ENCODING
;
1486 case XmlWriterState_DocClosed
:
1487 return WR_E_INVALIDACTION
;
1495 static HRESULT WINAPI
xmlwriter_WriteFullEndElement(IXmlWriter
*iface
)
1497 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1498 struct element
*element
;
1500 TRACE("%p\n", This
);
1502 switch (This
->state
)
1504 case XmlWriterState_Initial
:
1505 return E_UNEXPECTED
;
1506 case XmlWriterState_Ready
:
1507 case XmlWriterState_DocClosed
:
1508 This
->state
= XmlWriterState_DocClosed
;
1509 return WR_E_INVALIDACTION
;
1510 case XmlWriterState_InvalidEncoding
:
1511 return MX_E_ENCODING
;
1512 case XmlWriterState_ElemStarted
:
1513 writer_close_starttag(This
);
1519 element
= pop_element(This
);
1521 return WR_E_INVALIDACTION
;
1523 writer_dec_indent(This
);
1525 /* don't force full end tag to the next line */
1526 if (This
->state
== XmlWriterState_ElemStarted
)
1528 This
->state
= XmlWriterState_Content
;
1532 write_node_indent(This
);
1534 /* write full end tag */
1535 write_output_buffer(This
->output
, L
"</", 2);
1536 write_output_buffer(This
->output
, element
->qname
, element
->len
);
1537 write_output_buffer_char(This
->output
, '>');
1539 writer_free_element(This
, element
);
1544 static HRESULT WINAPI
xmlwriter_WriteName(IXmlWriter
*iface
, LPCWSTR pwszName
)
1546 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1548 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszName
));
1550 switch (This
->state
)
1552 case XmlWriterState_Initial
:
1553 return E_UNEXPECTED
;
1554 case XmlWriterState_Ready
:
1555 case XmlWriterState_DocClosed
:
1556 This
->state
= XmlWriterState_DocClosed
;
1557 return WR_E_INVALIDACTION
;
1558 case XmlWriterState_InvalidEncoding
:
1559 return MX_E_ENCODING
;
1567 static HRESULT WINAPI
xmlwriter_WriteNmToken(IXmlWriter
*iface
, LPCWSTR pwszNmToken
)
1569 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1571 FIXME("%p %s\n", This
, wine_dbgstr_w(pwszNmToken
));
1573 switch (This
->state
)
1575 case XmlWriterState_Initial
:
1576 return E_UNEXPECTED
;
1577 case XmlWriterState_Ready
:
1578 case XmlWriterState_DocClosed
:
1579 This
->state
= XmlWriterState_DocClosed
;
1580 return WR_E_INVALIDACTION
;
1581 case XmlWriterState_InvalidEncoding
:
1582 return MX_E_ENCODING
;
1590 static HRESULT
writer_write_node(IXmlWriter
*writer
, IXmlReader
*reader
, BOOL shallow
, BOOL write_default_attributes
)
1592 XmlStandalone standalone
= XmlStandalone_Omit
;
1593 const WCHAR
*name
, *value
, *prefix
, *uri
;
1594 unsigned int start_depth
= 0, depth
;
1595 XmlNodeType node_type
;
1598 if (FAILED(hr
= IXmlReader_GetNodeType(reader
, &node_type
))) return hr
;
1602 case XmlNodeType_None
:
1603 if (shallow
) return S_OK
;
1604 while ((hr
= IXmlReader_Read(reader
, NULL
)) == S_OK
)
1606 if (FAILED(hr
= writer_write_node(writer
, reader
, FALSE
, write_default_attributes
))) return hr
;
1609 case XmlNodeType_Element
:
1610 if (FAILED(hr
= IXmlReader_GetPrefix(reader
, &prefix
, NULL
))) return hr
;
1611 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1612 if (FAILED(hr
= IXmlReader_GetNamespaceUri(reader
, &uri
, NULL
))) return hr
;
1613 if (FAILED(hr
= IXmlWriter_WriteStartElement(writer
, prefix
, name
, uri
))) return hr
;
1614 if (FAILED(hr
= IXmlWriter_WriteAttributes(writer
, reader
, write_default_attributes
))) return hr
;
1615 if (IXmlReader_IsEmptyElement(reader
))
1617 hr
= IXmlWriter_WriteEndElement(writer
);
1621 if (shallow
) return S_OK
;
1622 if (FAILED(hr
= IXmlReader_MoveToElement(reader
))) return hr
;
1623 if (FAILED(hr
= IXmlReader_GetDepth(reader
, &start_depth
))) return hr
;
1624 while ((hr
= IXmlReader_Read(reader
, &node_type
)) == S_OK
)
1626 if (FAILED(hr
= writer_write_node(writer
, reader
, FALSE
, write_default_attributes
))) return hr
;
1627 if (FAILED(hr
= IXmlReader_MoveToElement(reader
))) return hr
;
1630 if (FAILED(hr
= IXmlReader_GetDepth(reader
, &depth
))) return hr
;
1631 if (node_type
== XmlNodeType_EndElement
&& (start_depth
== depth
- 1)) break;
1635 case XmlNodeType_Attribute
:
1637 case XmlNodeType_Text
:
1638 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1639 hr
= IXmlWriter_WriteRaw(writer
, value
);
1641 case XmlNodeType_CDATA
:
1642 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1643 hr
= IXmlWriter_WriteCData(writer
, value
);
1645 case XmlNodeType_ProcessingInstruction
:
1646 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1647 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1648 hr
= IXmlWriter_WriteProcessingInstruction(writer
, name
, value
);
1650 case XmlNodeType_Comment
:
1651 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1652 hr
= IXmlWriter_WriteComment(writer
, value
);
1654 case XmlNodeType_Whitespace
:
1655 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1656 hr
= IXmlWriter_WriteWhitespace(writer
, value
);
1658 case XmlNodeType_EndElement
:
1659 hr
= IXmlWriter_WriteFullEndElement(writer
);
1661 case XmlNodeType_XmlDeclaration
:
1662 while ((hr
= IXmlReader_MoveToNextAttribute(reader
)) == S_OK
)
1664 if (FAILED(hr
= IXmlReader_GetLocalName(reader
, &name
, NULL
))) return hr
;
1665 if (!wcscmp(name
, L
"standalone"))
1667 if (FAILED(hr
= IXmlReader_GetValue(reader
, &value
, NULL
))) return hr
;
1668 standalone
= !wcscmp(value
, L
"yes") ? XmlStandalone_Yes
: XmlStandalone_No
;
1672 hr
= IXmlWriter_WriteStartDocument(writer
, standalone
);
1675 WARN("Unknown node type %d.\n", node_type
);
1676 return E_UNEXPECTED
;
1682 static HRESULT WINAPI
xmlwriter_WriteNode(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
1686 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
1689 return E_INVALIDARG
;
1691 if (SUCCEEDED(hr
= writer_write_node(iface
, reader
, FALSE
, write_default_attributes
)))
1692 hr
= IXmlReader_Read(reader
, NULL
);
1697 static HRESULT WINAPI
xmlwriter_WriteNodeShallow(IXmlWriter
*iface
, IXmlReader
*reader
, BOOL write_default_attributes
)
1699 TRACE("%p, %p, %d.\n", iface
, reader
, write_default_attributes
);
1702 return E_INVALIDARG
;
1704 return writer_write_node(iface
, reader
, TRUE
, write_default_attributes
);
1707 static HRESULT WINAPI
xmlwriter_WriteProcessingInstruction(IXmlWriter
*iface
, LPCWSTR name
,
1710 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1712 TRACE("(%p)->(%s %s)\n", This
, wine_dbgstr_w(name
), wine_dbgstr_w(text
));
1714 switch (This
->state
)
1716 case XmlWriterState_Initial
:
1717 return E_UNEXPECTED
;
1718 case XmlWriterState_InvalidEncoding
:
1719 return MX_E_ENCODING
;
1720 case XmlWriterState_DocStarted
:
1721 if (!wcscmp(name
, L
"xml"))
1722 return WR_E_INVALIDACTION
;
1724 case XmlWriterState_ElemStarted
:
1725 writer_close_starttag(This
);
1727 case XmlWriterState_DocClosed
:
1728 return WR_E_INVALIDACTION
;
1733 write_encoding_bom(This
);
1734 write_node_indent(This
);
1735 write_output_buffer(This
->output
, L
"<?", 2);
1736 write_output_buffer(This
->output
, name
, -1);
1737 write_output_buffer_char(This
->output
, ' ');
1738 write_output_buffer(This
->output
, text
, -1);
1739 write_output_buffer(This
->output
, L
"?>", 2);
1741 if (!wcscmp(name
, L
"xml"))
1742 This
->state
= XmlWriterState_PIDocStarted
;
1747 static HRESULT WINAPI
xmlwriter_WriteQualifiedName(IXmlWriter
*iface
, LPCWSTR pwszLocalName
,
1748 LPCWSTR pwszNamespaceUri
)
1750 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1752 FIXME("%p %s %s\n", This
, wine_dbgstr_w(pwszLocalName
), wine_dbgstr_w(pwszNamespaceUri
));
1754 switch (This
->state
)
1756 case XmlWriterState_Initial
:
1757 return E_UNEXPECTED
;
1758 case XmlWriterState_InvalidEncoding
:
1759 return MX_E_ENCODING
;
1760 case XmlWriterState_DocClosed
:
1761 return WR_E_INVALIDACTION
;
1769 static HRESULT WINAPI
xmlwriter_WriteRaw(IXmlWriter
*iface
, LPCWSTR data
)
1771 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1775 TRACE("%p %s\n", This
, debugstr_w(data
));
1780 switch (This
->state
)
1782 case XmlWriterState_Initial
:
1783 return E_UNEXPECTED
;
1784 case XmlWriterState_Ready
:
1785 write_xmldecl(This
, XmlStandalone_Omit
);
1787 case XmlWriterState_DocStarted
:
1788 case XmlWriterState_PIDocStarted
:
1790 case XmlWriterState_InvalidEncoding
:
1791 return MX_E_ENCODING
;
1792 case XmlWriterState_ElemStarted
:
1793 writer_close_starttag(This
);
1796 This
->state
= XmlWriterState_DocClosed
;
1797 return WR_E_INVALIDACTION
;
1802 if (FAILED(hr
= writer_get_next_write_count(data
, ~0u, &count
))) return hr
;
1803 if (FAILED(hr
= write_output_buffer(This
->output
, data
, count
))) return hr
;
1811 static HRESULT WINAPI
xmlwriter_WriteRawChars(IXmlWriter
*iface
, const WCHAR
*characters
, UINT length
)
1813 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
1817 TRACE("%p, %s, %d.\n", iface
, debugstr_wn(characters
, length
), length
);
1819 if ((characters
== NULL
&& length
!= 0))
1820 return E_INVALIDARG
;
1825 switch (writer
->state
)
1827 case XmlWriterState_Initial
:
1828 return E_UNEXPECTED
;
1829 case XmlWriterState_InvalidEncoding
:
1830 return MX_E_ENCODING
;
1831 case XmlWriterState_DocClosed
:
1832 return WR_E_INVALIDACTION
;
1833 case XmlWriterState_Ready
:
1834 write_xmldecl(writer
, XmlStandalone_Omit
);
1836 case XmlWriterState_ElemStarted
:
1837 writer_close_starttag(writer
);
1844 if (FAILED(hr
= writer_get_next_write_count(characters
, length
, &count
))) return hr
;
1845 if (FAILED(hr
= write_output_buffer(writer
->output
, characters
, count
))) return hr
;
1847 characters
+= count
;
1854 static HRESULT WINAPI
xmlwriter_WriteStartDocument(IXmlWriter
*iface
, XmlStandalone standalone
)
1856 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1858 TRACE("(%p)->(%d)\n", This
, standalone
);
1860 switch (This
->state
)
1862 case XmlWriterState_Initial
:
1863 return E_UNEXPECTED
;
1864 case XmlWriterState_PIDocStarted
:
1865 This
->state
= XmlWriterState_DocStarted
;
1867 case XmlWriterState_Ready
:
1869 case XmlWriterState_InvalidEncoding
:
1870 return MX_E_ENCODING
;
1872 This
->state
= XmlWriterState_DocClosed
;
1873 return WR_E_INVALIDACTION
;
1876 return write_xmldecl(This
, standalone
);
1879 static HRESULT WINAPI
xmlwriter_WriteStartElement(IXmlWriter
*iface
, LPCWSTR prefix
, LPCWSTR local_name
, LPCWSTR uri
)
1881 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1882 int prefix_len
, local_len
;
1883 struct element
*element
;
1887 TRACE("(%p)->(%s %s %s)\n", This
, wine_dbgstr_w(prefix
), wine_dbgstr_w(local_name
), wine_dbgstr_w(uri
));
1890 return E_INVALIDARG
;
1892 switch (This
->state
)
1894 case XmlWriterState_Initial
:
1895 return E_UNEXPECTED
;
1896 case XmlWriterState_InvalidEncoding
:
1897 return MX_E_ENCODING
;
1898 case XmlWriterState_DocClosed
:
1899 return WR_E_INVALIDACTION
;
1900 case XmlWriterState_ElemStarted
:
1901 writer_close_starttag(This
);
1907 /* Validate prefix and local name */
1908 if (FAILED(hr
= is_valid_ncname(prefix
, &prefix_len
)))
1911 if (FAILED(hr
= is_valid_ncname(local_name
, &local_len
)))
1914 if (uri
&& !wcscmp(uri
, xmlnsuriW
))
1917 return WR_E_XMLNSPREFIXDECLARATION
;
1919 if (!is_empty_string(prefix
))
1920 return WR_E_XMLNSURIDECLARATION
;
1923 ns
= writer_find_ns(This
, prefix
, uri
);
1925 element
= alloc_element(This
, prefix
, local_name
);
1927 return E_OUTOFMEMORY
;
1929 write_encoding_bom(This
);
1930 write_node_indent(This
);
1932 This
->state
= XmlWriterState_ElemStarted
;
1933 This
->starttagopen
= 1;
1935 writer_push_element(This
, element
);
1937 if (!ns
&& !is_empty_string(uri
))
1938 writer_push_ns(This
, prefix
, prefix_len
, uri
);
1940 write_output_buffer_char(This
->output
, '<');
1942 write_output_qname(This
->output
, ns
->prefix
, ns
->prefix_len
, local_name
, local_len
);
1944 write_output_qname(This
->output
, prefix
, prefix_len
, local_name
, local_len
);
1945 writer_inc_indent(This
);
1950 static HRESULT WINAPI
xmlwriter_WriteString(IXmlWriter
*iface
, const WCHAR
*string
)
1952 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
1954 TRACE("%p %s\n", This
, debugstr_w(string
));
1959 switch (This
->state
)
1961 case XmlWriterState_Initial
:
1962 return E_UNEXPECTED
;
1963 case XmlWriterState_ElemStarted
:
1964 writer_close_starttag(This
);
1966 case XmlWriterState_Ready
:
1967 case XmlWriterState_DocClosed
:
1968 This
->state
= XmlWriterState_DocClosed
;
1969 return WR_E_INVALIDACTION
;
1970 case XmlWriterState_InvalidEncoding
:
1971 return MX_E_ENCODING
;
1977 return write_escaped_string(This
, string
, ~0u);
1980 static HRESULT WINAPI
xmlwriter_WriteSurrogateCharEntity(IXmlWriter
*iface
, WCHAR wchLow
, WCHAR wchHigh
)
1982 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
1986 TRACE("%p, %d, %d.\n", iface
, wchLow
, wchHigh
);
1988 if (!IS_SURROGATE_PAIR(wchHigh
, wchLow
))
1989 return WC_E_XMLCHARACTER
;
1991 switch (writer
->state
)
1993 case XmlWriterState_Initial
:
1994 return E_UNEXPECTED
;
1995 case XmlWriterState_InvalidEncoding
:
1996 return MX_E_ENCODING
;
1997 case XmlWriterState_ElemStarted
:
1998 writer_close_starttag(writer
);
2000 case XmlWriterState_DocClosed
:
2001 return WR_E_INVALIDACTION
;
2006 codepoint
= ((wchHigh
- 0xd800) * 0x400) + (wchLow
- 0xdc00) + 0x10000;
2007 swprintf(bufW
, ARRAY_SIZE(bufW
), L
"&#x%X;", codepoint
);
2008 write_output_buffer(writer
->output
, bufW
, -1);
2013 static HRESULT WINAPI
xmlwriter_WriteWhitespace(IXmlWriter
*iface
, LPCWSTR text
)
2015 xmlwriter
*writer
= impl_from_IXmlWriter(iface
);
2018 TRACE("%p, %s.\n", iface
, wine_dbgstr_w(text
));
2020 switch (writer
->state
)
2022 case XmlWriterState_Initial
:
2023 return E_UNEXPECTED
;
2024 case XmlWriterState_ElemStarted
:
2025 writer_close_starttag(writer
);
2027 case XmlWriterState_InvalidEncoding
:
2028 return MX_E_ENCODING
;
2029 case XmlWriterState_Ready
:
2032 return WR_E_INVALIDACTION
;
2035 while (text
[length
])
2037 if (!is_wchar_space(text
[length
])) return WR_E_NONWHITESPACE
;
2041 write_output_buffer(writer
->output
, text
, length
);
2045 static HRESULT WINAPI
xmlwriter_Flush(IXmlWriter
*iface
)
2047 xmlwriter
*This
= impl_from_IXmlWriter(iface
);
2049 TRACE("%p\n", This
);
2051 return writeroutput_flush_stream(This
->output
);
2054 static const struct IXmlWriterVtbl xmlwriter_vtbl
=
2056 xmlwriter_QueryInterface
,
2059 xmlwriter_SetOutput
,
2060 xmlwriter_GetProperty
,
2061 xmlwriter_SetProperty
,
2062 xmlwriter_WriteAttributes
,
2063 xmlwriter_WriteAttributeString
,
2064 xmlwriter_WriteCData
,
2065 xmlwriter_WriteCharEntity
,
2066 xmlwriter_WriteChars
,
2067 xmlwriter_WriteComment
,
2068 xmlwriter_WriteDocType
,
2069 xmlwriter_WriteElementString
,
2070 xmlwriter_WriteEndDocument
,
2071 xmlwriter_WriteEndElement
,
2072 xmlwriter_WriteEntityRef
,
2073 xmlwriter_WriteFullEndElement
,
2074 xmlwriter_WriteName
,
2075 xmlwriter_WriteNmToken
,
2076 xmlwriter_WriteNode
,
2077 xmlwriter_WriteNodeShallow
,
2078 xmlwriter_WriteProcessingInstruction
,
2079 xmlwriter_WriteQualifiedName
,
2081 xmlwriter_WriteRawChars
,
2082 xmlwriter_WriteStartDocument
,
2083 xmlwriter_WriteStartElement
,
2084 xmlwriter_WriteString
,
2085 xmlwriter_WriteSurrogateCharEntity
,
2086 xmlwriter_WriteWhitespace
,
2090 /** IXmlWriterOutput **/
2091 static HRESULT WINAPI
xmlwriteroutput_QueryInterface(IXmlWriterOutput
*iface
, REFIID riid
, void** ppvObject
)
2093 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
2095 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppvObject
);
2097 if (IsEqualGUID(riid
, &IID_IXmlWriterOutput
) ||
2098 IsEqualGUID(riid
, &IID_IUnknown
))
2104 WARN("interface %s not implemented\n", debugstr_guid(riid
));
2106 return E_NOINTERFACE
;
2109 IUnknown_AddRef(iface
);
2114 static ULONG WINAPI
xmlwriteroutput_AddRef(IXmlWriterOutput
*iface
)
2116 xmlwriteroutput
*output
= impl_from_IXmlWriterOutput(iface
);
2117 ULONG ref
= InterlockedIncrement(&output
->ref
);
2118 TRACE("%p, refcount %ld.\n", iface
, ref
);
2122 static ULONG WINAPI
xmlwriteroutput_Release(IXmlWriterOutput
*iface
)
2124 xmlwriteroutput
*This
= impl_from_IXmlWriterOutput(iface
);
2125 LONG ref
= InterlockedDecrement(&This
->ref
);
2127 TRACE("%p, refcount %ld.\n", iface
, ref
);
2131 IMalloc
*imalloc
= This
->imalloc
;
2132 if (This
->output
) IUnknown_Release(This
->output
);
2133 if (This
->stream
) ISequentialStream_Release(This
->stream
);
2134 free_output_buffer(This
);
2135 writeroutput_free(This
, This
->encoding_name
);
2136 writeroutput_free(This
, This
);
2137 if (imalloc
) IMalloc_Release(imalloc
);
2143 static const struct IUnknownVtbl xmlwriteroutputvtbl
=
2145 xmlwriteroutput_QueryInterface
,
2146 xmlwriteroutput_AddRef
,
2147 xmlwriteroutput_Release
2150 HRESULT WINAPI
CreateXmlWriter(REFIID riid
, void **obj
, IMalloc
*imalloc
)
2155 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
2157 if (!(writer
= m_alloc(imalloc
, sizeof(*writer
))))
2158 return E_OUTOFMEMORY
;
2159 memset(writer
, 0, sizeof(*writer
));
2161 writer
->IXmlWriter_iface
.lpVtbl
= &xmlwriter_vtbl
;
2163 writer
->imalloc
= imalloc
;
2164 if (imalloc
) IMalloc_AddRef(imalloc
);
2166 writer
->conformance
= XmlConformanceLevel_Document
;
2167 writer
->state
= XmlWriterState_Initial
;
2168 list_init(&writer
->elements
);
2170 hr
= IXmlWriter_QueryInterface(&writer
->IXmlWriter_iface
, riid
, obj
);
2171 IXmlWriter_Release(&writer
->IXmlWriter_iface
);
2173 TRACE("returning iface %p, hr %#lx.\n", *obj
, hr
);
2178 static HRESULT
create_writer_output(IUnknown
*stream
, IMalloc
*imalloc
, xml_encoding encoding
,
2179 const WCHAR
*encoding_name
, xmlwriteroutput
**out
)
2181 xmlwriteroutput
*writeroutput
;
2186 if (!(writeroutput
= m_alloc(imalloc
, sizeof(*writeroutput
))))
2187 return E_OUTOFMEMORY
;
2188 memset(writeroutput
, 0, sizeof(*writeroutput
));
2190 writeroutput
->IXmlWriterOutput_iface
.lpVtbl
= &xmlwriteroutputvtbl
;
2191 writeroutput
->ref
= 1;
2192 writeroutput
->imalloc
= imalloc
;
2194 IMalloc_AddRef(imalloc
);
2195 writeroutput
->encoding
= encoding
;
2196 hr
= init_output_buffer(writeroutput
);
2198 IUnknown_Release(&writeroutput
->IXmlWriterOutput_iface
);
2204 unsigned int size
= (lstrlenW(encoding_name
) + 1) * sizeof(WCHAR
);
2205 writeroutput
->encoding_name
= writeroutput_alloc(writeroutput
, size
);
2206 memcpy(writeroutput
->encoding_name
, encoding_name
, size
);
2209 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&writeroutput
->output
);
2211 *out
= writeroutput
;
2213 TRACE("Created writer output %p\n", *out
);
2218 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingName(IUnknown
*stream
, IMalloc
*imalloc
, const WCHAR
*encoding
,
2219 IXmlWriterOutput
**out
)
2221 xmlwriteroutput
*output
;
2222 xml_encoding xml_enc
;
2225 TRACE("%p %p %s %p\n", stream
, imalloc
, debugstr_w(encoding
), out
);
2227 if (!stream
|| !out
)
2228 return E_INVALIDARG
;
2232 xml_enc
= encoding
? parse_encoding_name(encoding
, -1) : XmlEncoding_UTF8
;
2233 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, encoding
, &output
)))
2234 *out
= &output
->IXmlWriterOutput_iface
;
2239 HRESULT WINAPI
CreateXmlWriterOutputWithEncodingCodePage(IUnknown
*stream
, IMalloc
*imalloc
, UINT codepage
,
2240 IXmlWriterOutput
**out
)
2242 xmlwriteroutput
*output
;
2243 xml_encoding xml_enc
;
2246 TRACE("%p %p %u %p\n", stream
, imalloc
, codepage
, out
);
2248 if (!stream
|| !out
)
2249 return E_INVALIDARG
;
2253 xml_enc
= get_encoding_from_codepage(codepage
);
2254 if (SUCCEEDED(hr
= create_writer_output(stream
, imalloc
, xml_enc
, NULL
, &output
)))
2255 *out
= &output
->IXmlWriterOutput_iface
;