mfplat: Remove duplicated GUID entry from attribute tracing.
[wine/zf.git] / dlls / xmllite / writer.c
blobe4553ccf01d095ec5dfcbb0fff7a09146e230ae9
1 /*
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
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "xmllite.h"
30 #include "xmllite_private.h"
31 #include "initguid.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/";
43 struct output_buffer
45 char *data;
46 unsigned int allocated;
47 unsigned int written;
48 UINT codepage;
51 typedef enum
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 */
61 } XmlWriterState;
63 typedef struct
65 IXmlWriterOutput IXmlWriterOutput_iface;
66 LONG ref;
67 IUnknown *output;
68 ISequentialStream *stream;
69 IMalloc *imalloc;
70 xml_encoding encoding;
71 WCHAR *encoding_name; /* exactly as specified on output creation */
72 struct output_buffer buffer;
73 DWORD written : 1;
74 } xmlwriteroutput;
76 static const struct IUnknownVtbl xmlwriteroutputvtbl;
78 struct element
80 struct list entry;
81 WCHAR *qname;
82 unsigned int len; /* qname length in chars */
83 struct list ns;
86 struct ns
88 struct list entry;
89 WCHAR *prefix;
90 int prefix_len;
91 WCHAR *uri;
92 BOOL emitted;
93 struct element *element;
96 typedef struct _xmlwriter
98 IXmlWriter IXmlWriter_iface;
99 LONG ref;
100 IMalloc *imalloc;
101 xmlwriteroutput *output;
102 unsigned int indent_level;
103 BOOL indent;
104 BOOL bom;
105 BOOL omitxmldecl;
106 XmlConformanceLevel conformance;
107 XmlWriterState state;
108 struct list elements;
109 DWORD bomwritten : 1;
110 DWORD starttagopen : 1;
111 DWORD textnode : 1;
112 } xmlwriter;
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[] =
128 "MultiLanguage",
129 "Indent",
130 "ByteOrderMark",
131 "OmitXmlDeclaration",
132 "ConformanceLevel"
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 struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
173 struct element *ret;
174 int len;
176 ret = writer_alloc(writer, sizeof(*ret));
177 if (!ret) return ret;
179 len = prefix ? lstrlenW(prefix) + 1 /* ':' */ : 0;
180 len += lstrlenW(local);
182 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
183 ret->len = len;
184 if (prefix)
186 lstrcpyW(ret->qname, prefix);
187 lstrcatW(ret->qname, L":");
189 else
190 ret->qname[0] = 0;
191 lstrcatW(ret->qname, local);
192 list_init(&ret->ns);
194 return ret;
197 static void writer_free_element(xmlwriter *writer, struct element *element)
199 struct ns *ns, *ns2;
201 LIST_FOR_EACH_ENTRY_SAFE(ns, ns2, &element->ns, struct ns, entry)
203 list_remove(&ns->entry);
204 writer_free(writer, ns->prefix);
205 writer_free(writer, ns->uri);
206 writer_free(writer, ns);
209 writer_free(writer, element->qname);
210 writer_free(writer, element);
213 static void writer_free_element_stack(xmlwriter *writer)
215 struct element *element, *element2;
217 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &writer->elements, struct element, entry)
219 list_remove(&element->entry);
220 writer_free_element(writer, element);
224 static void writer_push_element(xmlwriter *writer, struct element *element)
226 list_add_head(&writer->elements, &element->entry);
229 static struct element *pop_element(xmlwriter *writer)
231 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
233 if (element)
234 list_remove(&element->entry);
236 return element;
239 static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len)
241 size_t size;
242 WCHAR *ret;
244 if (!str)
245 return NULL;
247 if (len == -1)
248 len = lstrlenW(str);
250 size = (len + 1) * sizeof(WCHAR);
251 ret = writer_alloc(writer, size);
252 memcpy(ret, str, size);
253 return ret;
256 static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
258 return writer_strndupW(writer, str, -1);
261 static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
263 struct element *element;
264 struct ns *ns;
266 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
267 if (!element)
268 return NULL;
270 if ((ns = writer_alloc(writer, sizeof(*ns))))
272 ns->prefix = writer_strndupW(writer, prefix, prefix_len);
273 ns->prefix_len = prefix_len;
274 ns->uri = writer_strdupW(writer, uri);
275 ns->emitted = FALSE;
276 ns->element = element;
277 list_add_tail(&element->ns, &ns->entry);
280 return ns;
283 static BOOL is_empty_string(const WCHAR *str)
285 return !str || !*str;
288 static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
290 struct element *element;
291 struct ns *ns;
293 if (is_empty_string(prefix) || is_empty_string(uri))
294 return NULL;
296 element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
298 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
300 if (!wcscmp(uri, ns->uri) && !wcscmp(prefix, ns->prefix))
301 return ns;
304 return NULL;
307 static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
309 struct element *element;
310 struct ns *ns;
312 if (is_empty_string(prefix) && is_empty_string(uri))
313 return NULL;
315 LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
317 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
319 if (!uri)
321 if (!ns->prefix) continue;
322 if (!wcscmp(ns->prefix, prefix))
323 return ns;
325 else if (!wcscmp(uri, ns->uri))
327 if (prefix && !*prefix)
328 return NULL;
329 if (!prefix || !wcscmp(prefix, ns->prefix))
330 return ns;
335 return NULL;
338 static HRESULT is_valid_ncname(const WCHAR *str, int *out)
340 int len = 0;
342 *out = 0;
344 if (!str || !*str)
345 return S_OK;
347 while (*str)
349 if (!is_ncnamechar(*str))
350 return WC_E_NAMECHARACTER;
351 len++;
352 str++;
355 *out = len;
356 return S_OK;
359 static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
361 unsigned int len = 1;
363 *out = 0;
365 if (!str || !*str)
366 return S_OK;
368 if (!is_namestartchar(*str++))
369 return WC_E_NAMECHARACTER;
371 while (*str++)
373 if (!is_namechar(*str))
374 return WC_E_NAMECHARACTER;
375 len++;
378 *out = len;
379 return S_OK;
382 static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
384 unsigned int len = 0;
386 *out = 0;
388 if (!str || !*str)
389 return S_OK;
391 while (*str)
393 if (!is_pubchar(*str++))
394 return WC_E_PUBLICID;
395 len++;
398 *out = len;
400 return S_OK;
403 static HRESULT init_output_buffer(xmlwriteroutput *output)
405 struct output_buffer *buffer = &output->buffer;
406 const int initial_len = 0x2000;
407 UINT cp = ~0u;
408 HRESULT hr;
410 if (FAILED(hr = get_code_page(output->encoding, &cp)))
411 WARN("Failed to get code page for specified encoding.\n");
413 buffer->data = writeroutput_alloc(output, initial_len);
414 if (!buffer->data) return E_OUTOFMEMORY;
416 memset(buffer->data, 0, 4);
417 buffer->allocated = initial_len;
418 buffer->written = 0;
419 buffer->codepage = cp;
421 return S_OK;
424 static void free_output_buffer(xmlwriteroutput *output)
426 struct output_buffer *buffer = &output->buffer;
427 writeroutput_free(output, buffer->data);
428 buffer->data = NULL;
429 buffer->allocated = 0;
430 buffer->written = 0;
433 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
435 struct output_buffer *buffer = &output->buffer;
436 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
437 if (buffer->allocated < buffer->written + length + 4) {
438 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
439 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
440 if (!ptr) return E_OUTOFMEMORY;
441 buffer->data = ptr;
442 buffer->allocated = grown_size;
445 return S_OK;
448 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
450 struct output_buffer *buffer = &output->buffer;
451 int length;
452 HRESULT hr;
453 char *ptr;
455 if (buffer->codepage == 1200) {
456 /* For UTF-16 encoding just copy. */
457 length = len == -1 ? lstrlenW(data) : len;
458 if (length) {
459 length *= sizeof(WCHAR);
461 hr = grow_output_buffer(output, length);
462 if (FAILED(hr)) return hr;
463 ptr = buffer->data + buffer->written;
465 memcpy(ptr, data, length);
466 buffer->written += length;
467 ptr += length;
468 /* null termination */
469 *(WCHAR*)ptr = 0;
472 else {
473 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
474 hr = grow_output_buffer(output, length);
475 if (FAILED(hr)) return hr;
476 ptr = buffer->data + buffer->written;
477 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
478 buffer->written += len == -1 ? length-1 : length;
480 output->written = length != 0;
482 return S_OK;
485 static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch)
487 return write_output_buffer(output, &ch, 1);
490 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
492 write_output_buffer_char(output, '"');
493 if (!is_empty_string(data))
494 write_output_buffer(output, data, len);
495 write_output_buffer_char(output, '"');
496 return S_OK;
499 /* TODO: test if we need to validate char range */
500 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len,
501 const WCHAR *local_name, int local_len)
503 assert(prefix_len >= 0 && local_len >= 0);
505 if (prefix_len)
506 write_output_buffer(output, prefix, prefix_len);
508 if (prefix_len && local_len)
509 write_output_buffer_char(output, ':');
511 write_output_buffer(output, local_name, local_len);
513 return S_OK;
516 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
518 if (writeroutput->stream) {
519 ISequentialStream_Release(writeroutput->stream);
520 writeroutput->stream = NULL;
524 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
526 HRESULT hr;
528 writeroutput_release_stream(writeroutput);
529 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
530 if (hr != S_OK)
531 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
533 return hr;
536 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
538 struct output_buffer *buffer;
539 ULONG written, offset = 0;
540 HRESULT hr;
542 if (!output || !output->stream)
543 return S_OK;
545 buffer = &output->buffer;
547 /* It will loop forever until everything is written or an error occurred. */
548 do {
549 written = 0;
550 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
551 if (FAILED(hr)) {
552 WARN("write to stream failed (0x%08x)\n", hr);
553 buffer->written = 0;
554 return hr;
557 offset += written;
558 buffer->written -= written;
559 } while (buffer->written > 0);
561 return S_OK;
564 static HRESULT write_encoding_bom(xmlwriter *writer)
566 if (!writer->bom || writer->bomwritten) return S_OK;
568 if (writer->output->encoding == XmlEncoding_UTF16) {
569 static const char utf16bom[] = {0xff, 0xfe};
570 struct output_buffer *buffer = &writer->output->buffer;
571 int len = sizeof(utf16bom);
572 HRESULT hr;
574 hr = grow_output_buffer(writer->output, len);
575 if (FAILED(hr)) return hr;
576 memcpy(buffer->data + buffer->written, utf16bom, len);
577 buffer->written += len;
580 writer->bomwritten = TRUE;
581 return S_OK;
584 static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
586 if (output->encoding_name)
587 return output->encoding_name;
589 return get_encoding_name(output->encoding);
592 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
594 write_encoding_bom(writer);
595 writer->state = XmlWriterState_DocStarted;
596 if (writer->omitxmldecl) return S_OK;
598 /* version */
599 write_output_buffer(writer->output, L"<?xml version=\"1.0\"", 19);
601 /* encoding */
602 write_output_buffer(writer->output, L" encoding=", 10);
603 write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
605 /* standalone */
606 if (standalone == XmlStandalone_Omit)
607 write_output_buffer(writer->output, L"?>", 2);
608 else
610 write_output_buffer(writer->output, L" standalone=\"", 13);
611 if (standalone == XmlStandalone_Yes)
612 write_output_buffer(writer->output, L"yes\"?>", 6);
613 else
614 write_output_buffer(writer->output, L"no\"?>", 5);
617 return S_OK;
620 static void writer_output_ns(xmlwriter *writer, struct element *element)
622 struct ns *ns;
624 LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
626 if (ns->emitted)
627 continue;
629 write_output_qname(writer->output, L" xmlns", 6, ns->prefix, ns->prefix_len);
630 write_output_buffer_char(writer->output, '=');
631 write_output_buffer_quoted(writer->output, ns->uri, -1);
635 static HRESULT writer_close_starttag(xmlwriter *writer)
637 HRESULT hr;
639 if (!writer->starttagopen) return S_OK;
641 writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry));
642 hr = write_output_buffer_char(writer->output, '>');
643 writer->starttagopen = 0;
644 return hr;
647 static void writer_inc_indent(xmlwriter *writer)
649 writer->indent_level++;
652 static void writer_dec_indent(xmlwriter *writer)
654 if (writer->indent_level)
655 writer->indent_level--;
658 static void write_node_indent(xmlwriter *writer)
660 unsigned int indent_level = writer->indent_level;
662 if (!writer->indent || writer->textnode)
664 writer->textnode = 0;
665 return;
668 /* Do state check to prevent newline inserted after BOM. It is assumed that
669 state does not change between writing BOM and inserting indentation. */
670 if (writer->output->written && writer->state != XmlWriterState_Ready)
671 write_output_buffer(writer->output, L"\r\n", 2);
672 while (indent_level--)
673 write_output_buffer(writer->output, L" ", 2);
675 writer->textnode = 0;
678 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
680 xmlwriter *This = impl_from_IXmlWriter(iface);
682 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
684 if (IsEqualGUID(riid, &IID_IXmlWriter) ||
685 IsEqualGUID(riid, &IID_IUnknown))
687 *ppvObject = iface;
689 else
691 FIXME("interface %s is not supported\n", debugstr_guid(riid));
692 *ppvObject = NULL;
693 return E_NOINTERFACE;
696 IXmlWriter_AddRef(iface);
698 return S_OK;
701 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
703 xmlwriter *This = impl_from_IXmlWriter(iface);
704 ULONG ref = InterlockedIncrement(&This->ref);
705 TRACE("(%p)->(%u)\n", This, ref);
706 return ref;
709 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
711 xmlwriter *This = impl_from_IXmlWriter(iface);
712 ULONG ref = InterlockedDecrement(&This->ref);
714 TRACE("(%p)->(%u)\n", This, ref);
716 if (ref == 0) {
717 IMalloc *imalloc = This->imalloc;
719 writeroutput_flush_stream(This->output);
720 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
722 writer_free_element_stack(This);
724 writer_free(This, This);
725 if (imalloc) IMalloc_Release(imalloc);
728 return ref;
731 /*** IXmlWriter methods ***/
732 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
734 xmlwriter *This = impl_from_IXmlWriter(iface);
735 IXmlWriterOutput *writeroutput;
736 HRESULT hr;
738 TRACE("(%p)->(%p)\n", This, output);
740 if (This->output) {
741 writeroutput_release_stream(This->output);
742 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
743 This->output = NULL;
744 This->bomwritten = 0;
745 This->textnode = 0;
746 This->indent_level = 0;
747 writer_free_element_stack(This);
750 /* just reset current output */
751 if (!output) {
752 This->state = XmlWriterState_Initial;
753 return S_OK;
756 /* now try IXmlWriterOutput, ISequentialStream, IStream */
757 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
758 if (hr == S_OK) {
759 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
760 This->output = impl_from_IXmlWriterOutput(writeroutput);
761 else {
762 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
763 writeroutput, writeroutput->lpVtbl);
764 IUnknown_Release(writeroutput);
765 return E_FAIL;
769 if (hr != S_OK || !writeroutput) {
770 /* Create output for given stream. */
771 hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
772 if (hr != S_OK)
773 return hr;
776 if (This->output->encoding == XmlEncoding_Unknown)
777 This->state = XmlWriterState_InvalidEncoding;
778 else
779 This->state = XmlWriterState_Ready;
780 return writeroutput_query_for_stream(This->output);
783 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
785 xmlwriter *This = impl_from_IXmlWriter(iface);
787 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
789 if (!value) return E_INVALIDARG;
791 switch (property)
793 case XmlWriterProperty_Indent:
794 *value = This->indent;
795 break;
796 case XmlWriterProperty_ByteOrderMark:
797 *value = This->bom;
798 break;
799 case XmlWriterProperty_OmitXmlDeclaration:
800 *value = This->omitxmldecl;
801 break;
802 case XmlWriterProperty_ConformanceLevel:
803 *value = This->conformance;
804 break;
805 default:
806 FIXME("Unimplemented property (%u)\n", property);
807 return E_NOTIMPL;
810 return S_OK;
813 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
815 xmlwriter *This = impl_from_IXmlWriter(iface);
817 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
819 switch (property)
821 case XmlWriterProperty_Indent:
822 This->indent = !!value;
823 break;
824 case XmlWriterProperty_ByteOrderMark:
825 This->bom = !!value;
826 break;
827 case XmlWriterProperty_OmitXmlDeclaration:
828 This->omitxmldecl = !!value;
829 break;
830 default:
831 FIXME("Unimplemented property (%u)\n", property);
832 return E_NOTIMPL;
835 return S_OK;
838 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
839 BOOL fWriteDefaultAttributes)
841 xmlwriter *This = impl_from_IXmlWriter(iface);
843 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
845 return E_NOTIMPL;
848 static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
849 const WCHAR *local, int local_len, const WCHAR *value)
851 write_output_buffer_char(writer->output, ' ');
852 write_output_qname(writer->output, prefix, prefix_len, local, local_len);
853 write_output_buffer_char(writer->output, '=');
854 write_output_buffer_quoted(writer->output, value, -1);
857 static BOOL is_valid_xml_space_value(const WCHAR *value)
859 return value && (!wcscmp(value, L"preserve") || !wcscmp(value, L"default"));
862 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
863 LPCWSTR local, LPCWSTR uri, LPCWSTR value)
865 xmlwriter *This = impl_from_IXmlWriter(iface);
866 BOOL is_xmlns_prefix, is_xmlns_local;
867 int prefix_len, local_len;
868 struct ns *ns;
869 HRESULT hr;
871 TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
873 switch (This->state)
875 case XmlWriterState_Initial:
876 return E_UNEXPECTED;
877 case XmlWriterState_Ready:
878 case XmlWriterState_DocClosed:
879 This->state = XmlWriterState_DocClosed;
880 return WR_E_INVALIDACTION;
881 case XmlWriterState_InvalidEncoding:
882 return MX_E_ENCODING;
883 default:
887 /* Prefix "xmlns" */
888 is_xmlns_prefix = prefix && !wcscmp(prefix, L"xmlns");
889 if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
890 return WR_E_NSPREFIXDECLARED;
892 if (!local)
893 return E_INVALIDARG;
895 /* Validate prefix and local name */
896 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
897 return hr;
899 if (FAILED(hr = is_valid_ncname(local, &local_len)))
900 return hr;
902 is_xmlns_local = !wcscmp(local, L"xmlns");
904 /* Trivial case, no prefix. */
905 if (prefix_len == 0 && is_empty_string(uri))
907 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
908 return S_OK;
911 /* Predefined "xml" prefix. */
912 if (prefix_len && !wcscmp(prefix, L"xml"))
914 /* Valid "space" value is enforced. */
915 if (!wcscmp(local, L"space") && !is_valid_xml_space_value(value))
916 return WR_E_INVALIDXMLSPACE;
918 /* Redefinition is not allowed. */
919 if (!is_empty_string(uri))
920 return WR_E_XMLPREFIXDECLARATION;
922 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
924 return S_OK;
927 if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
929 if (prefix_len && !is_empty_string(uri))
930 return WR_E_XMLNSPREFIXDECLARATION;
932 /* Look for exact match defined in current element, and write it out. */
933 if (!(ns = writer_find_ns_current(This, prefix, value)))
934 ns = writer_push_ns(This, local, local_len, value);
935 ns->emitted = TRUE;
937 write_output_attribute(This, L"xmlns", 5, local, local_len, value);
939 return S_OK;
942 /* Ignore prefix is URI wasn't specified. */
943 if (is_xmlns_local && is_empty_string(uri))
945 write_output_attribute(This, NULL, 0, L"xmlns", 5, value);
946 return S_OK;
949 if (!(ns = writer_find_ns(This, prefix, uri)))
951 if (is_empty_string(prefix) && !is_empty_string(uri))
953 FIXME("Prefix autogeneration is not implemented.\n");
954 return E_NOTIMPL;
956 if (!is_empty_string(uri))
957 ns = writer_push_ns(This, prefix, prefix_len, uri);
960 if (ns)
961 write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
962 else
963 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
965 return S_OK;
968 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
970 write_output_buffer(output, L"<![CDATA[", 9);
971 if (data)
972 write_output_buffer(output, data, len);
973 write_output_buffer(output, L"]]>", 3);
976 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
978 xmlwriter *This = impl_from_IXmlWriter(iface);
979 int len;
981 TRACE("%p %s\n", This, debugstr_w(data));
983 switch (This->state)
985 case XmlWriterState_Initial:
986 return E_UNEXPECTED;
987 case XmlWriterState_ElemStarted:
988 writer_close_starttag(This);
989 break;
990 case XmlWriterState_Ready:
991 case XmlWriterState_DocClosed:
992 This->state = XmlWriterState_DocClosed;
993 return WR_E_INVALIDACTION;
994 case XmlWriterState_InvalidEncoding:
995 return MX_E_ENCODING;
996 default:
1000 len = data ? lstrlenW(data) : 0;
1002 write_node_indent(This);
1003 if (!len)
1004 write_cdata_section(This->output, NULL, 0);
1005 else
1007 while (len)
1009 const WCHAR *str = wcsstr(data, L"]]>");
1010 if (str) {
1011 str += 2;
1012 write_cdata_section(This->output, data, str - data);
1013 len -= str - data;
1014 data = str;
1016 else {
1017 write_cdata_section(This->output, data, len);
1018 break;
1023 return S_OK;
1026 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1028 xmlwriter *This = impl_from_IXmlWriter(iface);
1029 WCHAR bufW[16];
1031 TRACE("%p %#x\n", This, ch);
1033 switch (This->state)
1035 case XmlWriterState_Initial:
1036 return E_UNEXPECTED;
1037 case XmlWriterState_InvalidEncoding:
1038 return MX_E_ENCODING;
1039 case XmlWriterState_ElemStarted:
1040 writer_close_starttag(This);
1041 break;
1042 case XmlWriterState_DocClosed:
1043 return WR_E_INVALIDACTION;
1044 default:
1048 swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%x;", ch);
1049 write_output_buffer(This->output, bufW, -1);
1051 return S_OK;
1054 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1056 xmlwriter *This = impl_from_IXmlWriter(iface);
1058 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1060 switch (This->state)
1062 case XmlWriterState_Initial:
1063 return E_UNEXPECTED;
1064 case XmlWriterState_InvalidEncoding:
1065 return MX_E_ENCODING;
1066 case XmlWriterState_DocClosed:
1067 return WR_E_INVALIDACTION;
1068 default:
1072 return E_NOTIMPL;
1076 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1078 xmlwriter *This = impl_from_IXmlWriter(iface);
1080 TRACE("%p %s\n", This, debugstr_w(comment));
1082 switch (This->state)
1084 case XmlWriterState_Initial:
1085 return E_UNEXPECTED;
1086 case XmlWriterState_InvalidEncoding:
1087 return MX_E_ENCODING;
1088 case XmlWriterState_ElemStarted:
1089 writer_close_starttag(This);
1090 break;
1091 case XmlWriterState_DocClosed:
1092 return WR_E_INVALIDACTION;
1093 default:
1097 write_node_indent(This);
1098 write_output_buffer(This->output, L"<!--", 4);
1099 if (comment) {
1100 int len = lstrlenW(comment), i;
1102 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1103 comment string */
1104 if (len > 1) {
1105 for (i = 0; i < len; i++) {
1106 write_output_buffer(This->output, comment + i, 1);
1107 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1108 write_output_buffer_char(This->output, ' ');
1111 else
1112 write_output_buffer(This->output, comment, len);
1114 if (len && comment[len-1] == '-')
1115 write_output_buffer_char(This->output, ' ');
1117 write_output_buffer(This->output, L"-->", 3);
1119 return S_OK;
1122 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
1123 LPCWSTR sysid, LPCWSTR subset)
1125 xmlwriter *This = impl_from_IXmlWriter(iface);
1126 unsigned int name_len, pubid_len;
1127 HRESULT hr;
1129 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
1130 wine_dbgstr_w(subset));
1132 switch (This->state)
1134 case XmlWriterState_Initial:
1135 return E_UNEXPECTED;
1136 case XmlWriterState_InvalidEncoding:
1137 return MX_E_ENCODING;
1138 case XmlWriterState_Content:
1139 case XmlWriterState_DocClosed:
1140 return WR_E_INVALIDACTION;
1141 default:
1145 if (is_empty_string(name))
1146 return E_INVALIDARG;
1148 if (FAILED(hr = is_valid_name(name, &name_len)))
1149 return hr;
1151 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1152 return hr;
1154 write_output_buffer(This->output, L"<!DOCTYPE ", 10);
1155 write_output_buffer(This->output, name, name_len);
1157 if (pubid)
1159 write_output_buffer(This->output, L" PUBLIC ", 8);
1160 write_output_buffer_quoted(This->output, pubid, pubid_len);
1161 write_output_buffer_char(This->output, ' ');
1162 write_output_buffer_quoted(This->output, sysid, -1);
1164 else if (sysid)
1166 write_output_buffer(This->output, L" SYSTEM ", 8);
1167 write_output_buffer_quoted(This->output, sysid, -1);
1170 if (subset)
1172 write_output_buffer_char(This->output, ' ');
1173 write_output_buffer_char(This->output, '[');
1174 write_output_buffer(This->output, subset, -1);
1175 write_output_buffer_char(This->output, ']');
1177 write_output_buffer_char(This->output, '>');
1179 This->state = XmlWriterState_Content;
1181 return S_OK;
1184 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
1185 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1187 xmlwriter *This = impl_from_IXmlWriter(iface);
1188 int prefix_len, local_len;
1189 struct ns *ns;
1190 HRESULT hr;
1192 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
1193 wine_dbgstr_w(uri), wine_dbgstr_w(value));
1195 switch (This->state)
1197 case XmlWriterState_Initial:
1198 return E_UNEXPECTED;
1199 case XmlWriterState_InvalidEncoding:
1200 return MX_E_ENCODING;
1201 case XmlWriterState_ElemStarted:
1202 writer_close_starttag(This);
1203 break;
1204 case XmlWriterState_DocClosed:
1205 return WR_E_INVALIDACTION;
1206 default:
1210 if (!local_name)
1211 return E_INVALIDARG;
1213 /* Validate prefix and local name */
1214 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1215 return hr;
1217 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1218 return hr;
1220 ns = writer_find_ns(This, prefix, uri);
1221 if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
1222 return WR_E_NSPREFIXWITHEMPTYNSURI;
1224 if (uri && !wcscmp(uri, xmlnsuriW))
1226 if (!prefix)
1227 return WR_E_XMLNSPREFIXDECLARATION;
1229 if (!is_empty_string(prefix))
1230 return WR_E_XMLNSURIDECLARATION;
1233 write_encoding_bom(This);
1234 write_node_indent(This);
1236 write_output_buffer_char(This->output, '<');
1237 if (ns)
1238 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1239 else
1240 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1242 if (!ns && (prefix_len || !is_empty_string(uri)))
1244 write_output_qname(This->output, L" xmlns", 6, prefix, prefix_len);
1245 write_output_buffer_char(This->output, '=');
1246 write_output_buffer_quoted(This->output, uri, -1);
1249 if (value)
1251 write_output_buffer_char(This->output, '>');
1252 write_output_buffer(This->output, value, -1);
1253 write_output_buffer(This->output, L"</", 2);
1254 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1255 write_output_buffer_char(This->output, '>');
1257 else
1258 write_output_buffer(This->output, L" />", 3);
1260 This->state = XmlWriterState_Content;
1262 return S_OK;
1265 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
1267 xmlwriter *This = impl_from_IXmlWriter(iface);
1269 TRACE("%p\n", This);
1271 switch (This->state)
1273 case XmlWriterState_Initial:
1274 return E_UNEXPECTED;
1275 case XmlWriterState_Ready:
1276 case XmlWriterState_DocClosed:
1277 This->state = XmlWriterState_DocClosed;
1278 return WR_E_INVALIDACTION;
1279 case XmlWriterState_InvalidEncoding:
1280 return MX_E_ENCODING;
1281 default:
1285 /* empty element stack */
1286 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1289 This->state = XmlWriterState_DocClosed;
1290 return S_OK;
1293 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
1295 xmlwriter *This = impl_from_IXmlWriter(iface);
1296 struct element *element;
1298 TRACE("%p\n", This);
1300 switch (This->state)
1302 case XmlWriterState_Initial:
1303 return E_UNEXPECTED;
1304 case XmlWriterState_Ready:
1305 case XmlWriterState_DocClosed:
1306 This->state = XmlWriterState_DocClosed;
1307 return WR_E_INVALIDACTION;
1308 case XmlWriterState_InvalidEncoding:
1309 return MX_E_ENCODING;
1310 default:
1314 element = pop_element(This);
1315 if (!element)
1316 return WR_E_INVALIDACTION;
1318 writer_dec_indent(This);
1320 if (This->starttagopen)
1322 writer_output_ns(This, element);
1323 write_output_buffer(This->output, L" />", 3);
1324 This->starttagopen = 0;
1326 else
1328 /* Write full end tag. */
1329 write_node_indent(This);
1330 write_output_buffer(This->output, L"</", 2);
1331 write_output_buffer(This->output, element->qname, element->len);
1332 write_output_buffer_char(This->output, '>');
1334 writer_free_element(This, element);
1336 return S_OK;
1339 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
1341 xmlwriter *This = impl_from_IXmlWriter(iface);
1343 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1345 switch (This->state)
1347 case XmlWriterState_Initial:
1348 return E_UNEXPECTED;
1349 case XmlWriterState_InvalidEncoding:
1350 return MX_E_ENCODING;
1351 case XmlWriterState_DocClosed:
1352 return WR_E_INVALIDACTION;
1353 default:
1357 return E_NOTIMPL;
1360 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
1362 xmlwriter *This = impl_from_IXmlWriter(iface);
1363 struct element *element;
1365 TRACE("%p\n", This);
1367 switch (This->state)
1369 case XmlWriterState_Initial:
1370 return E_UNEXPECTED;
1371 case XmlWriterState_Ready:
1372 case XmlWriterState_DocClosed:
1373 This->state = XmlWriterState_DocClosed;
1374 return WR_E_INVALIDACTION;
1375 case XmlWriterState_InvalidEncoding:
1376 return MX_E_ENCODING;
1377 case XmlWriterState_ElemStarted:
1378 writer_close_starttag(This);
1379 break;
1380 default:
1384 element = pop_element(This);
1385 if (!element)
1386 return WR_E_INVALIDACTION;
1388 writer_dec_indent(This);
1390 /* don't force full end tag to the next line */
1391 if (This->state == XmlWriterState_ElemStarted)
1393 This->state = XmlWriterState_Content;
1394 This->textnode = 0;
1396 else
1397 write_node_indent(This);
1399 /* write full end tag */
1400 write_output_buffer(This->output, L"</", 2);
1401 write_output_buffer(This->output, element->qname, element->len);
1402 write_output_buffer_char(This->output, '>');
1404 writer_free_element(This, element);
1406 return S_OK;
1409 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1411 xmlwriter *This = impl_from_IXmlWriter(iface);
1413 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1415 switch (This->state)
1417 case XmlWriterState_Initial:
1418 return E_UNEXPECTED;
1419 case XmlWriterState_Ready:
1420 case XmlWriterState_DocClosed:
1421 This->state = XmlWriterState_DocClosed;
1422 return WR_E_INVALIDACTION;
1423 case XmlWriterState_InvalidEncoding:
1424 return MX_E_ENCODING;
1425 default:
1429 return E_NOTIMPL;
1432 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1434 xmlwriter *This = impl_from_IXmlWriter(iface);
1436 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1438 switch (This->state)
1440 case XmlWriterState_Initial:
1441 return E_UNEXPECTED;
1442 case XmlWriterState_Ready:
1443 case XmlWriterState_DocClosed:
1444 This->state = XmlWriterState_DocClosed;
1445 return WR_E_INVALIDACTION;
1446 case XmlWriterState_InvalidEncoding:
1447 return MX_E_ENCODING;
1448 default:
1452 return E_NOTIMPL;
1455 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1456 BOOL fWriteDefaultAttributes)
1458 xmlwriter *This = impl_from_IXmlWriter(iface);
1460 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1462 return E_NOTIMPL;
1465 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1466 BOOL fWriteDefaultAttributes)
1468 xmlwriter *This = impl_from_IXmlWriter(iface);
1470 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1472 return E_NOTIMPL;
1475 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1476 LPCWSTR text)
1478 xmlwriter *This = impl_from_IXmlWriter(iface);
1480 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1482 switch (This->state)
1484 case XmlWriterState_Initial:
1485 return E_UNEXPECTED;
1486 case XmlWriterState_InvalidEncoding:
1487 return MX_E_ENCODING;
1488 case XmlWriterState_DocStarted:
1489 if (!wcscmp(name, L"xml"))
1490 return WR_E_INVALIDACTION;
1491 break;
1492 case XmlWriterState_ElemStarted:
1493 case XmlWriterState_DocClosed:
1494 return WR_E_INVALIDACTION;
1495 default:
1499 write_encoding_bom(This);
1500 write_node_indent(This);
1501 write_output_buffer(This->output, L"<?", 2);
1502 write_output_buffer(This->output, name, -1);
1503 write_output_buffer_char(This->output, ' ');
1504 write_output_buffer(This->output, text, -1);
1505 write_output_buffer(This->output, L"?>", 2);
1507 if (!wcscmp(name, L"xml"))
1508 This->state = XmlWriterState_PIDocStarted;
1510 return S_OK;
1513 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1514 LPCWSTR pwszNamespaceUri)
1516 xmlwriter *This = impl_from_IXmlWriter(iface);
1518 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1520 switch (This->state)
1522 case XmlWriterState_Initial:
1523 return E_UNEXPECTED;
1524 case XmlWriterState_InvalidEncoding:
1525 return MX_E_ENCODING;
1526 case XmlWriterState_DocClosed:
1527 return WR_E_INVALIDACTION;
1528 default:
1532 return E_NOTIMPL;
1535 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1537 xmlwriter *This = impl_from_IXmlWriter(iface);
1539 TRACE("%p %s\n", This, debugstr_w(data));
1541 if (!data)
1542 return S_OK;
1544 switch (This->state)
1546 case XmlWriterState_Initial:
1547 return E_UNEXPECTED;
1548 case XmlWriterState_Ready:
1549 write_xmldecl(This, XmlStandalone_Omit);
1550 /* fallthrough */
1551 case XmlWriterState_DocStarted:
1552 case XmlWriterState_PIDocStarted:
1553 break;
1554 case XmlWriterState_InvalidEncoding:
1555 return MX_E_ENCODING;
1556 default:
1557 This->state = XmlWriterState_DocClosed;
1558 return WR_E_INVALIDACTION;
1561 write_output_buffer(This->output, data, -1);
1562 return S_OK;
1565 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1567 xmlwriter *This = impl_from_IXmlWriter(iface);
1569 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1571 switch (This->state)
1573 case XmlWriterState_Initial:
1574 return E_UNEXPECTED;
1575 case XmlWriterState_InvalidEncoding:
1576 return MX_E_ENCODING;
1577 case XmlWriterState_DocClosed:
1578 return WR_E_INVALIDACTION;
1579 default:
1583 return E_NOTIMPL;
1586 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1588 xmlwriter *This = impl_from_IXmlWriter(iface);
1590 TRACE("(%p)->(%d)\n", This, standalone);
1592 switch (This->state)
1594 case XmlWriterState_Initial:
1595 return E_UNEXPECTED;
1596 case XmlWriterState_PIDocStarted:
1597 This->state = XmlWriterState_DocStarted;
1598 return S_OK;
1599 case XmlWriterState_Ready:
1600 break;
1601 case XmlWriterState_InvalidEncoding:
1602 return MX_E_ENCODING;
1603 default:
1604 This->state = XmlWriterState_DocClosed;
1605 return WR_E_INVALIDACTION;
1608 return write_xmldecl(This, standalone);
1611 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1613 xmlwriter *This = impl_from_IXmlWriter(iface);
1614 int prefix_len, local_len;
1615 struct element *element;
1616 struct ns *ns;
1617 HRESULT hr;
1619 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1621 if (!local_name)
1622 return E_INVALIDARG;
1624 switch (This->state)
1626 case XmlWriterState_Initial:
1627 return E_UNEXPECTED;
1628 case XmlWriterState_InvalidEncoding:
1629 return MX_E_ENCODING;
1630 case XmlWriterState_DocClosed:
1631 return WR_E_INVALIDACTION;
1632 case XmlWriterState_ElemStarted:
1633 writer_close_starttag(This);
1634 break;
1635 default:
1639 /* Validate prefix and local name */
1640 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1641 return hr;
1643 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1644 return hr;
1646 if (uri && !wcscmp(uri, xmlnsuriW))
1648 if (!prefix)
1649 return WR_E_XMLNSPREFIXDECLARATION;
1651 if (!is_empty_string(prefix))
1652 return WR_E_XMLNSURIDECLARATION;
1655 ns = writer_find_ns(This, prefix, uri);
1657 element = alloc_element(This, prefix, local_name);
1658 if (!element)
1659 return E_OUTOFMEMORY;
1661 write_encoding_bom(This);
1662 write_node_indent(This);
1664 This->state = XmlWriterState_ElemStarted;
1665 This->starttagopen = 1;
1667 writer_push_element(This, element);
1669 if (!ns && uri)
1670 writer_push_ns(This, prefix, prefix_len, uri);
1672 write_output_buffer_char(This->output, '<');
1673 if (ns)
1674 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1675 else
1676 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1677 writer_inc_indent(This);
1679 return S_OK;
1682 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1684 while (*string)
1686 switch (*string)
1688 case '<':
1689 write_output_buffer(writer->output, L"&lt;", 4);
1690 break;
1691 case '&':
1692 write_output_buffer(writer->output, L"&amp;", 5);
1693 break;
1694 case '>':
1695 write_output_buffer(writer->output, L"&gt;", 4);
1696 break;
1697 default:
1698 write_output_buffer(writer->output, string, 1);
1701 string++;
1705 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1707 xmlwriter *This = impl_from_IXmlWriter(iface);
1709 TRACE("%p %s\n", This, debugstr_w(string));
1711 if (!string)
1712 return S_OK;
1714 switch (This->state)
1716 case XmlWriterState_Initial:
1717 return E_UNEXPECTED;
1718 case XmlWriterState_ElemStarted:
1719 writer_close_starttag(This);
1720 break;
1721 case XmlWriterState_Ready:
1722 case XmlWriterState_DocClosed:
1723 This->state = XmlWriterState_DocClosed;
1724 return WR_E_INVALIDACTION;
1725 case XmlWriterState_InvalidEncoding:
1726 return MX_E_ENCODING;
1727 default:
1731 This->textnode = 1;
1732 write_escaped_string(This, string);
1733 return S_OK;
1736 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1738 xmlwriter *This = impl_from_IXmlWriter(iface);
1740 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1742 return E_NOTIMPL;
1745 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1747 xmlwriter *This = impl_from_IXmlWriter(iface);
1749 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1751 return E_NOTIMPL;
1754 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1756 xmlwriter *This = impl_from_IXmlWriter(iface);
1758 TRACE("%p\n", This);
1760 return writeroutput_flush_stream(This->output);
1763 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1765 xmlwriter_QueryInterface,
1766 xmlwriter_AddRef,
1767 xmlwriter_Release,
1768 xmlwriter_SetOutput,
1769 xmlwriter_GetProperty,
1770 xmlwriter_SetProperty,
1771 xmlwriter_WriteAttributes,
1772 xmlwriter_WriteAttributeString,
1773 xmlwriter_WriteCData,
1774 xmlwriter_WriteCharEntity,
1775 xmlwriter_WriteChars,
1776 xmlwriter_WriteComment,
1777 xmlwriter_WriteDocType,
1778 xmlwriter_WriteElementString,
1779 xmlwriter_WriteEndDocument,
1780 xmlwriter_WriteEndElement,
1781 xmlwriter_WriteEntityRef,
1782 xmlwriter_WriteFullEndElement,
1783 xmlwriter_WriteName,
1784 xmlwriter_WriteNmToken,
1785 xmlwriter_WriteNode,
1786 xmlwriter_WriteNodeShallow,
1787 xmlwriter_WriteProcessingInstruction,
1788 xmlwriter_WriteQualifiedName,
1789 xmlwriter_WriteRaw,
1790 xmlwriter_WriteRawChars,
1791 xmlwriter_WriteStartDocument,
1792 xmlwriter_WriteStartElement,
1793 xmlwriter_WriteString,
1794 xmlwriter_WriteSurrogateCharEntity,
1795 xmlwriter_WriteWhitespace,
1796 xmlwriter_Flush
1799 /** IXmlWriterOutput **/
1800 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1802 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1804 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1806 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1807 IsEqualGUID(riid, &IID_IUnknown))
1809 *ppvObject = iface;
1811 else
1813 WARN("interface %s not implemented\n", debugstr_guid(riid));
1814 *ppvObject = NULL;
1815 return E_NOINTERFACE;
1818 IUnknown_AddRef(iface);
1820 return S_OK;
1823 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1825 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1826 ULONG ref = InterlockedIncrement(&This->ref);
1827 TRACE("(%p)->(%d)\n", This, ref);
1828 return ref;
1831 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1833 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1834 LONG ref = InterlockedDecrement(&This->ref);
1836 TRACE("(%p)->(%d)\n", This, ref);
1838 if (ref == 0)
1840 IMalloc *imalloc = This->imalloc;
1841 if (This->output) IUnknown_Release(This->output);
1842 if (This->stream) ISequentialStream_Release(This->stream);
1843 free_output_buffer(This);
1844 writeroutput_free(This, This->encoding_name);
1845 writeroutput_free(This, This);
1846 if (imalloc) IMalloc_Release(imalloc);
1849 return ref;
1852 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1854 xmlwriteroutput_QueryInterface,
1855 xmlwriteroutput_AddRef,
1856 xmlwriteroutput_Release
1859 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1861 xmlwriter *writer;
1862 HRESULT hr;
1864 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1866 if (imalloc)
1867 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1868 else
1869 writer = heap_alloc(sizeof(*writer));
1870 if (!writer)
1871 return E_OUTOFMEMORY;
1873 memset(writer, 0, sizeof(*writer));
1875 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1876 writer->ref = 1;
1877 writer->imalloc = imalloc;
1878 if (imalloc) IMalloc_AddRef(imalloc);
1879 writer->bom = TRUE;
1880 writer->conformance = XmlConformanceLevel_Document;
1881 writer->state = XmlWriterState_Initial;
1882 list_init(&writer->elements);
1884 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
1885 IXmlWriter_Release(&writer->IXmlWriter_iface);
1887 TRACE("returning iface %p, hr %#x\n", *obj, hr);
1889 return hr;
1892 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1893 const WCHAR *encoding_name, xmlwriteroutput **out)
1895 xmlwriteroutput *writeroutput;
1896 HRESULT hr;
1898 *out = NULL;
1900 if (imalloc)
1901 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1902 else
1903 writeroutput = heap_alloc(sizeof(*writeroutput));
1904 if (!writeroutput)
1905 return E_OUTOFMEMORY;
1907 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1908 writeroutput->ref = 1;
1909 writeroutput->imalloc = imalloc;
1910 if (imalloc)
1911 IMalloc_AddRef(imalloc);
1912 writeroutput->encoding = encoding;
1913 writeroutput->stream = NULL;
1914 hr = init_output_buffer(writeroutput);
1915 if (FAILED(hr)) {
1916 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1917 return hr;
1920 if (encoding_name) {
1921 unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
1922 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
1923 memcpy(writeroutput->encoding_name, encoding_name, size);
1925 else
1926 writeroutput->encoding_name = NULL;
1927 writeroutput->written = 0;
1929 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1931 *out = writeroutput;
1933 TRACE("Created writer output %p\n", *out);
1935 return S_OK;
1938 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
1939 IXmlWriterOutput **out)
1941 xmlwriteroutput *output;
1942 xml_encoding xml_enc;
1943 HRESULT hr;
1945 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
1947 if (!stream || !out)
1948 return E_INVALIDARG;
1950 *out = NULL;
1952 xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
1953 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
1954 *out = &output->IXmlWriterOutput_iface;
1956 return hr;
1959 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
1960 IXmlWriterOutput **out)
1962 xmlwriteroutput *output;
1963 xml_encoding xml_enc;
1964 HRESULT hr;
1966 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
1968 if (!stream || !out)
1969 return E_INVALIDARG;
1971 *out = NULL;
1973 xml_enc = get_encoding_from_codepage(codepage);
1974 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
1975 *out = &output->IXmlWriterOutput_iface;
1977 return hr;