dbghelp: Fix memory leak on error path in dwarf2_read_range (cppcheck).
[wine.git] / dlls / xmllite / writer.c
blobac02bd4c2006311c01e4ec951031563005df2895
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 %#lx.\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 *writer = impl_from_IXmlWriter(iface);
704 ULONG ref = InterlockedIncrement(&writer->ref);
705 TRACE("%p, refcount %lu.\n", iface, ref);
706 return ref;
709 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
711 xmlwriter *writer = impl_from_IXmlWriter(iface);
712 ULONG ref = InterlockedDecrement(&writer->ref);
714 TRACE("%p, refcount %lu.\n", iface, ref);
716 if (!ref)
718 IMalloc *imalloc = writer->imalloc;
720 writeroutput_flush_stream(writer->output);
721 if (writer->output)
722 IUnknown_Release(&writer->output->IXmlWriterOutput_iface);
724 writer_free_element_stack(writer);
726 writer_free(writer, writer);
727 if (imalloc) IMalloc_Release(imalloc);
730 return ref;
733 /*** IXmlWriter methods ***/
734 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
736 xmlwriter *This = impl_from_IXmlWriter(iface);
737 IXmlWriterOutput *writeroutput;
738 HRESULT hr;
740 TRACE("(%p)->(%p)\n", This, output);
742 if (This->output) {
743 writeroutput_release_stream(This->output);
744 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
745 This->output = NULL;
746 This->bomwritten = 0;
747 This->textnode = 0;
748 This->indent_level = 0;
749 writer_free_element_stack(This);
752 /* just reset current output */
753 if (!output) {
754 This->state = XmlWriterState_Initial;
755 return S_OK;
758 /* now try IXmlWriterOutput, ISequentialStream, IStream */
759 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
760 if (hr == S_OK) {
761 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
762 This->output = impl_from_IXmlWriterOutput(writeroutput);
763 else {
764 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
765 writeroutput, writeroutput->lpVtbl);
766 IUnknown_Release(writeroutput);
767 return E_FAIL;
771 if (hr != S_OK || !writeroutput) {
772 /* Create output for given stream. */
773 hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
774 if (hr != S_OK)
775 return hr;
778 if (This->output->encoding == XmlEncoding_Unknown)
779 This->state = XmlWriterState_InvalidEncoding;
780 else
781 This->state = XmlWriterState_Ready;
782 return writeroutput_query_for_stream(This->output);
785 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
787 xmlwriter *This = impl_from_IXmlWriter(iface);
789 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
791 if (!value) return E_INVALIDARG;
793 switch (property)
795 case XmlWriterProperty_Indent:
796 *value = This->indent;
797 break;
798 case XmlWriterProperty_ByteOrderMark:
799 *value = This->bom;
800 break;
801 case XmlWriterProperty_OmitXmlDeclaration:
802 *value = This->omitxmldecl;
803 break;
804 case XmlWriterProperty_ConformanceLevel:
805 *value = This->conformance;
806 break;
807 default:
808 FIXME("Unimplemented property (%u)\n", property);
809 return E_NOTIMPL;
812 return S_OK;
815 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
817 xmlwriter *writer = impl_from_IXmlWriter(iface);
819 TRACE("%p, %s, %Id.\n", iface, debugstr_writer_prop(property), value);
821 switch (property)
823 case XmlWriterProperty_Indent:
824 writer->indent = !!value;
825 break;
826 case XmlWriterProperty_ByteOrderMark:
827 writer->bom = !!value;
828 break;
829 case XmlWriterProperty_OmitXmlDeclaration:
830 writer->omitxmldecl = !!value;
831 break;
832 default:
833 FIXME("Unimplemented property (%u)\n", property);
834 return E_NOTIMPL;
837 return S_OK;
840 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
841 BOOL fWriteDefaultAttributes)
843 xmlwriter *This = impl_from_IXmlWriter(iface);
845 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
847 return E_NOTIMPL;
850 static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
851 const WCHAR *local, int local_len, const WCHAR *value)
853 write_output_buffer_char(writer->output, ' ');
854 write_output_qname(writer->output, prefix, prefix_len, local, local_len);
855 write_output_buffer_char(writer->output, '=');
856 write_output_buffer_quoted(writer->output, value, -1);
859 static BOOL is_valid_xml_space_value(const WCHAR *value)
861 return value && (!wcscmp(value, L"preserve") || !wcscmp(value, L"default"));
864 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
865 LPCWSTR local, LPCWSTR uri, LPCWSTR value)
867 xmlwriter *This = impl_from_IXmlWriter(iface);
868 BOOL is_xmlns_prefix, is_xmlns_local;
869 int prefix_len, local_len;
870 struct ns *ns;
871 HRESULT hr;
873 TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
875 switch (This->state)
877 case XmlWriterState_Initial:
878 return E_UNEXPECTED;
879 case XmlWriterState_Ready:
880 case XmlWriterState_DocClosed:
881 This->state = XmlWriterState_DocClosed;
882 return WR_E_INVALIDACTION;
883 case XmlWriterState_InvalidEncoding:
884 return MX_E_ENCODING;
885 default:
889 /* Prefix "xmlns" */
890 is_xmlns_prefix = prefix && !wcscmp(prefix, L"xmlns");
891 if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
892 return WR_E_NSPREFIXDECLARED;
894 if (!local)
895 return E_INVALIDARG;
897 /* Validate prefix and local name */
898 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
899 return hr;
901 if (FAILED(hr = is_valid_ncname(local, &local_len)))
902 return hr;
904 is_xmlns_local = !wcscmp(local, L"xmlns");
906 /* Trivial case, no prefix. */
907 if (prefix_len == 0 && is_empty_string(uri))
909 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
910 return S_OK;
913 /* Predefined "xml" prefix. */
914 if (prefix_len && !wcscmp(prefix, L"xml"))
916 /* Valid "space" value is enforced. */
917 if (!wcscmp(local, L"space") && !is_valid_xml_space_value(value))
918 return WR_E_INVALIDXMLSPACE;
920 /* Redefinition is not allowed. */
921 if (!is_empty_string(uri))
922 return WR_E_XMLPREFIXDECLARATION;
924 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
926 return S_OK;
929 if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
931 if (prefix_len && !is_empty_string(uri))
932 return WR_E_XMLNSPREFIXDECLARATION;
934 /* Look for exact match defined in current element, and write it out. */
935 if (!(ns = writer_find_ns_current(This, prefix, value)))
936 ns = writer_push_ns(This, local, local_len, value);
937 ns->emitted = TRUE;
939 write_output_attribute(This, L"xmlns", 5, local, local_len, value);
941 return S_OK;
944 /* Ignore prefix is URI wasn't specified. */
945 if (is_xmlns_local && is_empty_string(uri))
947 write_output_attribute(This, NULL, 0, L"xmlns", 5, value);
948 return S_OK;
951 if (!(ns = writer_find_ns(This, prefix, uri)))
953 if (is_empty_string(prefix) && !is_empty_string(uri))
955 FIXME("Prefix autogeneration is not implemented.\n");
956 return E_NOTIMPL;
958 if (!is_empty_string(uri))
959 ns = writer_push_ns(This, prefix, prefix_len, uri);
962 if (ns)
963 write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
964 else
965 write_output_attribute(This, prefix, prefix_len, local, local_len, value);
967 return S_OK;
970 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
972 write_output_buffer(output, L"<![CDATA[", 9);
973 if (data)
974 write_output_buffer(output, data, len);
975 write_output_buffer(output, L"]]>", 3);
978 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
980 xmlwriter *This = impl_from_IXmlWriter(iface);
981 int len;
983 TRACE("%p %s\n", This, debugstr_w(data));
985 switch (This->state)
987 case XmlWriterState_Initial:
988 return E_UNEXPECTED;
989 case XmlWriterState_ElemStarted:
990 writer_close_starttag(This);
991 break;
992 case XmlWriterState_Ready:
993 case XmlWriterState_DocClosed:
994 This->state = XmlWriterState_DocClosed;
995 return WR_E_INVALIDACTION;
996 case XmlWriterState_InvalidEncoding:
997 return MX_E_ENCODING;
998 default:
1002 len = data ? lstrlenW(data) : 0;
1004 write_node_indent(This);
1005 if (!len)
1006 write_cdata_section(This->output, NULL, 0);
1007 else
1009 while (len)
1011 const WCHAR *str = wcsstr(data, L"]]>");
1012 if (str) {
1013 str += 2;
1014 write_cdata_section(This->output, data, str - data);
1015 len -= str - data;
1016 data = str;
1018 else {
1019 write_cdata_section(This->output, data, len);
1020 break;
1025 return S_OK;
1028 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1030 xmlwriter *This = impl_from_IXmlWriter(iface);
1031 WCHAR bufW[16];
1033 TRACE("%p %#x\n", This, ch);
1035 switch (This->state)
1037 case XmlWriterState_Initial:
1038 return E_UNEXPECTED;
1039 case XmlWriterState_InvalidEncoding:
1040 return MX_E_ENCODING;
1041 case XmlWriterState_ElemStarted:
1042 writer_close_starttag(This);
1043 break;
1044 case XmlWriterState_DocClosed:
1045 return WR_E_INVALIDACTION;
1046 default:
1050 swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%x;", ch);
1051 write_output_buffer(This->output, bufW, -1);
1053 return S_OK;
1056 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1058 xmlwriter *This = impl_from_IXmlWriter(iface);
1060 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1062 switch (This->state)
1064 case XmlWriterState_Initial:
1065 return E_UNEXPECTED;
1066 case XmlWriterState_InvalidEncoding:
1067 return MX_E_ENCODING;
1068 case XmlWriterState_DocClosed:
1069 return WR_E_INVALIDACTION;
1070 default:
1074 return E_NOTIMPL;
1078 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1080 xmlwriter *This = impl_from_IXmlWriter(iface);
1082 TRACE("%p %s\n", This, debugstr_w(comment));
1084 switch (This->state)
1086 case XmlWriterState_Initial:
1087 return E_UNEXPECTED;
1088 case XmlWriterState_InvalidEncoding:
1089 return MX_E_ENCODING;
1090 case XmlWriterState_ElemStarted:
1091 writer_close_starttag(This);
1092 break;
1093 case XmlWriterState_DocClosed:
1094 return WR_E_INVALIDACTION;
1095 default:
1099 write_node_indent(This);
1100 write_output_buffer(This->output, L"<!--", 4);
1101 if (comment) {
1102 int len = lstrlenW(comment), i;
1104 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
1105 comment string */
1106 if (len > 1) {
1107 for (i = 0; i < len; i++) {
1108 write_output_buffer(This->output, comment + i, 1);
1109 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1110 write_output_buffer_char(This->output, ' ');
1113 else
1114 write_output_buffer(This->output, comment, len);
1116 if (len && comment[len-1] == '-')
1117 write_output_buffer_char(This->output, ' ');
1119 write_output_buffer(This->output, L"-->", 3);
1121 return S_OK;
1124 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
1125 LPCWSTR sysid, LPCWSTR subset)
1127 xmlwriter *This = impl_from_IXmlWriter(iface);
1128 unsigned int name_len, pubid_len;
1129 HRESULT hr;
1131 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
1132 wine_dbgstr_w(subset));
1134 switch (This->state)
1136 case XmlWriterState_Initial:
1137 return E_UNEXPECTED;
1138 case XmlWriterState_InvalidEncoding:
1139 return MX_E_ENCODING;
1140 case XmlWriterState_Content:
1141 case XmlWriterState_DocClosed:
1142 return WR_E_INVALIDACTION;
1143 default:
1147 if (is_empty_string(name))
1148 return E_INVALIDARG;
1150 if (FAILED(hr = is_valid_name(name, &name_len)))
1151 return hr;
1153 if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
1154 return hr;
1156 write_output_buffer(This->output, L"<!DOCTYPE ", 10);
1157 write_output_buffer(This->output, name, name_len);
1159 if (pubid)
1161 write_output_buffer(This->output, L" PUBLIC ", 8);
1162 write_output_buffer_quoted(This->output, pubid, pubid_len);
1163 write_output_buffer_char(This->output, ' ');
1164 write_output_buffer_quoted(This->output, sysid, -1);
1166 else if (sysid)
1168 write_output_buffer(This->output, L" SYSTEM ", 8);
1169 write_output_buffer_quoted(This->output, sysid, -1);
1172 if (subset)
1174 write_output_buffer_char(This->output, ' ');
1175 write_output_buffer_char(This->output, '[');
1176 write_output_buffer(This->output, subset, -1);
1177 write_output_buffer_char(This->output, ']');
1179 write_output_buffer_char(This->output, '>');
1181 This->state = XmlWriterState_Content;
1183 return S_OK;
1186 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
1187 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1189 xmlwriter *This = impl_from_IXmlWriter(iface);
1190 int prefix_len, local_len;
1191 struct ns *ns;
1192 HRESULT hr;
1194 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
1195 wine_dbgstr_w(uri), wine_dbgstr_w(value));
1197 switch (This->state)
1199 case XmlWriterState_Initial:
1200 return E_UNEXPECTED;
1201 case XmlWriterState_InvalidEncoding:
1202 return MX_E_ENCODING;
1203 case XmlWriterState_ElemStarted:
1204 writer_close_starttag(This);
1205 break;
1206 case XmlWriterState_DocClosed:
1207 return WR_E_INVALIDACTION;
1208 default:
1212 if (!local_name)
1213 return E_INVALIDARG;
1215 /* Validate prefix and local name */
1216 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1217 return hr;
1219 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1220 return hr;
1222 ns = writer_find_ns(This, prefix, uri);
1223 if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
1224 return WR_E_NSPREFIXWITHEMPTYNSURI;
1226 if (uri && !wcscmp(uri, xmlnsuriW))
1228 if (!prefix)
1229 return WR_E_XMLNSPREFIXDECLARATION;
1231 if (!is_empty_string(prefix))
1232 return WR_E_XMLNSURIDECLARATION;
1235 write_encoding_bom(This);
1236 write_node_indent(This);
1238 write_output_buffer_char(This->output, '<');
1239 if (ns)
1240 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1241 else
1242 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1244 if (!ns && (prefix_len || !is_empty_string(uri)))
1246 write_output_qname(This->output, L" xmlns", 6, prefix, prefix_len);
1247 write_output_buffer_char(This->output, '=');
1248 write_output_buffer_quoted(This->output, uri, -1);
1251 if (value)
1253 write_output_buffer_char(This->output, '>');
1254 write_output_buffer(This->output, value, -1);
1255 write_output_buffer(This->output, L"</", 2);
1256 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1257 write_output_buffer_char(This->output, '>');
1259 else
1260 write_output_buffer(This->output, L" />", 3);
1262 This->state = XmlWriterState_Content;
1264 return S_OK;
1267 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
1269 xmlwriter *This = impl_from_IXmlWriter(iface);
1271 TRACE("%p\n", This);
1273 switch (This->state)
1275 case XmlWriterState_Initial:
1276 return E_UNEXPECTED;
1277 case XmlWriterState_Ready:
1278 case XmlWriterState_DocClosed:
1279 This->state = XmlWriterState_DocClosed;
1280 return WR_E_INVALIDACTION;
1281 case XmlWriterState_InvalidEncoding:
1282 return MX_E_ENCODING;
1283 default:
1287 /* empty element stack */
1288 while (IXmlWriter_WriteEndElement(iface) == S_OK)
1291 This->state = XmlWriterState_DocClosed;
1292 return S_OK;
1295 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
1297 xmlwriter *This = impl_from_IXmlWriter(iface);
1298 struct element *element;
1300 TRACE("%p\n", This);
1302 switch (This->state)
1304 case XmlWriterState_Initial:
1305 return E_UNEXPECTED;
1306 case XmlWriterState_Ready:
1307 case XmlWriterState_DocClosed:
1308 This->state = XmlWriterState_DocClosed;
1309 return WR_E_INVALIDACTION;
1310 case XmlWriterState_InvalidEncoding:
1311 return MX_E_ENCODING;
1312 default:
1316 element = pop_element(This);
1317 if (!element)
1318 return WR_E_INVALIDACTION;
1320 writer_dec_indent(This);
1322 if (This->starttagopen)
1324 writer_output_ns(This, element);
1325 write_output_buffer(This->output, L" />", 3);
1326 This->starttagopen = 0;
1328 else
1330 /* Write full end tag. */
1331 write_node_indent(This);
1332 write_output_buffer(This->output, L"</", 2);
1333 write_output_buffer(This->output, element->qname, element->len);
1334 write_output_buffer_char(This->output, '>');
1336 writer_free_element(This, element);
1338 return S_OK;
1341 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
1343 xmlwriter *This = impl_from_IXmlWriter(iface);
1345 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1347 switch (This->state)
1349 case XmlWriterState_Initial:
1350 return E_UNEXPECTED;
1351 case XmlWriterState_InvalidEncoding:
1352 return MX_E_ENCODING;
1353 case XmlWriterState_DocClosed:
1354 return WR_E_INVALIDACTION;
1355 default:
1359 return E_NOTIMPL;
1362 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
1364 xmlwriter *This = impl_from_IXmlWriter(iface);
1365 struct element *element;
1367 TRACE("%p\n", This);
1369 switch (This->state)
1371 case XmlWriterState_Initial:
1372 return E_UNEXPECTED;
1373 case XmlWriterState_Ready:
1374 case XmlWriterState_DocClosed:
1375 This->state = XmlWriterState_DocClosed;
1376 return WR_E_INVALIDACTION;
1377 case XmlWriterState_InvalidEncoding:
1378 return MX_E_ENCODING;
1379 case XmlWriterState_ElemStarted:
1380 writer_close_starttag(This);
1381 break;
1382 default:
1386 element = pop_element(This);
1387 if (!element)
1388 return WR_E_INVALIDACTION;
1390 writer_dec_indent(This);
1392 /* don't force full end tag to the next line */
1393 if (This->state == XmlWriterState_ElemStarted)
1395 This->state = XmlWriterState_Content;
1396 This->textnode = 0;
1398 else
1399 write_node_indent(This);
1401 /* write full end tag */
1402 write_output_buffer(This->output, L"</", 2);
1403 write_output_buffer(This->output, element->qname, element->len);
1404 write_output_buffer_char(This->output, '>');
1406 writer_free_element(This, element);
1408 return S_OK;
1411 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
1413 xmlwriter *This = impl_from_IXmlWriter(iface);
1415 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
1417 switch (This->state)
1419 case XmlWriterState_Initial:
1420 return E_UNEXPECTED;
1421 case XmlWriterState_Ready:
1422 case XmlWriterState_DocClosed:
1423 This->state = XmlWriterState_DocClosed;
1424 return WR_E_INVALIDACTION;
1425 case XmlWriterState_InvalidEncoding:
1426 return MX_E_ENCODING;
1427 default:
1431 return E_NOTIMPL;
1434 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
1436 xmlwriter *This = impl_from_IXmlWriter(iface);
1438 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
1440 switch (This->state)
1442 case XmlWriterState_Initial:
1443 return E_UNEXPECTED;
1444 case XmlWriterState_Ready:
1445 case XmlWriterState_DocClosed:
1446 This->state = XmlWriterState_DocClosed;
1447 return WR_E_INVALIDACTION;
1448 case XmlWriterState_InvalidEncoding:
1449 return MX_E_ENCODING;
1450 default:
1454 return E_NOTIMPL;
1457 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1458 BOOL fWriteDefaultAttributes)
1460 xmlwriter *This = impl_from_IXmlWriter(iface);
1462 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1464 return E_NOTIMPL;
1467 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1468 BOOL fWriteDefaultAttributes)
1470 xmlwriter *This = impl_from_IXmlWriter(iface);
1472 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1474 return E_NOTIMPL;
1477 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1478 LPCWSTR text)
1480 xmlwriter *This = impl_from_IXmlWriter(iface);
1482 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1484 switch (This->state)
1486 case XmlWriterState_Initial:
1487 return E_UNEXPECTED;
1488 case XmlWriterState_InvalidEncoding:
1489 return MX_E_ENCODING;
1490 case XmlWriterState_DocStarted:
1491 if (!wcscmp(name, L"xml"))
1492 return WR_E_INVALIDACTION;
1493 break;
1494 case XmlWriterState_ElemStarted:
1495 case XmlWriterState_DocClosed:
1496 return WR_E_INVALIDACTION;
1497 default:
1501 write_encoding_bom(This);
1502 write_node_indent(This);
1503 write_output_buffer(This->output, L"<?", 2);
1504 write_output_buffer(This->output, name, -1);
1505 write_output_buffer_char(This->output, ' ');
1506 write_output_buffer(This->output, text, -1);
1507 write_output_buffer(This->output, L"?>", 2);
1509 if (!wcscmp(name, L"xml"))
1510 This->state = XmlWriterState_PIDocStarted;
1512 return S_OK;
1515 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1516 LPCWSTR pwszNamespaceUri)
1518 xmlwriter *This = impl_from_IXmlWriter(iface);
1520 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1522 switch (This->state)
1524 case XmlWriterState_Initial:
1525 return E_UNEXPECTED;
1526 case XmlWriterState_InvalidEncoding:
1527 return MX_E_ENCODING;
1528 case XmlWriterState_DocClosed:
1529 return WR_E_INVALIDACTION;
1530 default:
1534 return E_NOTIMPL;
1537 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1539 xmlwriter *This = impl_from_IXmlWriter(iface);
1541 TRACE("%p %s\n", This, debugstr_w(data));
1543 if (!data)
1544 return S_OK;
1546 switch (This->state)
1548 case XmlWriterState_Initial:
1549 return E_UNEXPECTED;
1550 case XmlWriterState_Ready:
1551 write_xmldecl(This, XmlStandalone_Omit);
1552 /* fallthrough */
1553 case XmlWriterState_DocStarted:
1554 case XmlWriterState_PIDocStarted:
1555 break;
1556 case XmlWriterState_InvalidEncoding:
1557 return MX_E_ENCODING;
1558 default:
1559 This->state = XmlWriterState_DocClosed;
1560 return WR_E_INVALIDACTION;
1563 write_output_buffer(This->output, data, -1);
1564 return S_OK;
1567 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1569 xmlwriter *This = impl_from_IXmlWriter(iface);
1571 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1573 switch (This->state)
1575 case XmlWriterState_Initial:
1576 return E_UNEXPECTED;
1577 case XmlWriterState_InvalidEncoding:
1578 return MX_E_ENCODING;
1579 case XmlWriterState_DocClosed:
1580 return WR_E_INVALIDACTION;
1581 default:
1585 return E_NOTIMPL;
1588 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1590 xmlwriter *This = impl_from_IXmlWriter(iface);
1592 TRACE("(%p)->(%d)\n", This, standalone);
1594 switch (This->state)
1596 case XmlWriterState_Initial:
1597 return E_UNEXPECTED;
1598 case XmlWriterState_PIDocStarted:
1599 This->state = XmlWriterState_DocStarted;
1600 return S_OK;
1601 case XmlWriterState_Ready:
1602 break;
1603 case XmlWriterState_InvalidEncoding:
1604 return MX_E_ENCODING;
1605 default:
1606 This->state = XmlWriterState_DocClosed;
1607 return WR_E_INVALIDACTION;
1610 return write_xmldecl(This, standalone);
1613 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1615 xmlwriter *This = impl_from_IXmlWriter(iface);
1616 int prefix_len, local_len;
1617 struct element *element;
1618 struct ns *ns;
1619 HRESULT hr;
1621 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1623 if (!local_name)
1624 return E_INVALIDARG;
1626 switch (This->state)
1628 case XmlWriterState_Initial:
1629 return E_UNEXPECTED;
1630 case XmlWriterState_InvalidEncoding:
1631 return MX_E_ENCODING;
1632 case XmlWriterState_DocClosed:
1633 return WR_E_INVALIDACTION;
1634 case XmlWriterState_ElemStarted:
1635 writer_close_starttag(This);
1636 break;
1637 default:
1641 /* Validate prefix and local name */
1642 if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
1643 return hr;
1645 if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
1646 return hr;
1648 if (uri && !wcscmp(uri, xmlnsuriW))
1650 if (!prefix)
1651 return WR_E_XMLNSPREFIXDECLARATION;
1653 if (!is_empty_string(prefix))
1654 return WR_E_XMLNSURIDECLARATION;
1657 ns = writer_find_ns(This, prefix, uri);
1659 element = alloc_element(This, prefix, local_name);
1660 if (!element)
1661 return E_OUTOFMEMORY;
1663 write_encoding_bom(This);
1664 write_node_indent(This);
1666 This->state = XmlWriterState_ElemStarted;
1667 This->starttagopen = 1;
1669 writer_push_element(This, element);
1671 if (!ns && uri)
1672 writer_push_ns(This, prefix, prefix_len, uri);
1674 write_output_buffer_char(This->output, '<');
1675 if (ns)
1676 write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
1677 else
1678 write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1679 writer_inc_indent(This);
1681 return S_OK;
1684 static void write_escaped_string(xmlwriter *writer, const WCHAR *string)
1686 while (*string)
1688 switch (*string)
1690 case '<':
1691 write_output_buffer(writer->output, L"&lt;", 4);
1692 break;
1693 case '&':
1694 write_output_buffer(writer->output, L"&amp;", 5);
1695 break;
1696 case '>':
1697 write_output_buffer(writer->output, L"&gt;", 4);
1698 break;
1699 default:
1700 write_output_buffer(writer->output, string, 1);
1703 string++;
1707 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1709 xmlwriter *This = impl_from_IXmlWriter(iface);
1711 TRACE("%p %s\n", This, debugstr_w(string));
1713 if (!string)
1714 return S_OK;
1716 switch (This->state)
1718 case XmlWriterState_Initial:
1719 return E_UNEXPECTED;
1720 case XmlWriterState_ElemStarted:
1721 writer_close_starttag(This);
1722 break;
1723 case XmlWriterState_Ready:
1724 case XmlWriterState_DocClosed:
1725 This->state = XmlWriterState_DocClosed;
1726 return WR_E_INVALIDACTION;
1727 case XmlWriterState_InvalidEncoding:
1728 return MX_E_ENCODING;
1729 default:
1733 This->textnode = 1;
1734 write_escaped_string(This, string);
1735 return S_OK;
1738 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1740 xmlwriter *This = impl_from_IXmlWriter(iface);
1742 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1744 return E_NOTIMPL;
1747 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1749 xmlwriter *This = impl_from_IXmlWriter(iface);
1751 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1753 return E_NOTIMPL;
1756 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1758 xmlwriter *This = impl_from_IXmlWriter(iface);
1760 TRACE("%p\n", This);
1762 return writeroutput_flush_stream(This->output);
1765 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1767 xmlwriter_QueryInterface,
1768 xmlwriter_AddRef,
1769 xmlwriter_Release,
1770 xmlwriter_SetOutput,
1771 xmlwriter_GetProperty,
1772 xmlwriter_SetProperty,
1773 xmlwriter_WriteAttributes,
1774 xmlwriter_WriteAttributeString,
1775 xmlwriter_WriteCData,
1776 xmlwriter_WriteCharEntity,
1777 xmlwriter_WriteChars,
1778 xmlwriter_WriteComment,
1779 xmlwriter_WriteDocType,
1780 xmlwriter_WriteElementString,
1781 xmlwriter_WriteEndDocument,
1782 xmlwriter_WriteEndElement,
1783 xmlwriter_WriteEntityRef,
1784 xmlwriter_WriteFullEndElement,
1785 xmlwriter_WriteName,
1786 xmlwriter_WriteNmToken,
1787 xmlwriter_WriteNode,
1788 xmlwriter_WriteNodeShallow,
1789 xmlwriter_WriteProcessingInstruction,
1790 xmlwriter_WriteQualifiedName,
1791 xmlwriter_WriteRaw,
1792 xmlwriter_WriteRawChars,
1793 xmlwriter_WriteStartDocument,
1794 xmlwriter_WriteStartElement,
1795 xmlwriter_WriteString,
1796 xmlwriter_WriteSurrogateCharEntity,
1797 xmlwriter_WriteWhitespace,
1798 xmlwriter_Flush
1801 /** IXmlWriterOutput **/
1802 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1804 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1806 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1808 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1809 IsEqualGUID(riid, &IID_IUnknown))
1811 *ppvObject = iface;
1813 else
1815 WARN("interface %s not implemented\n", debugstr_guid(riid));
1816 *ppvObject = NULL;
1817 return E_NOINTERFACE;
1820 IUnknown_AddRef(iface);
1822 return S_OK;
1825 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1827 xmlwriteroutput *output = impl_from_IXmlWriterOutput(iface);
1828 ULONG ref = InterlockedIncrement(&output->ref);
1829 TRACE("%p, refcount %ld.\n", iface, ref);
1830 return ref;
1833 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1835 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1836 LONG ref = InterlockedDecrement(&This->ref);
1838 TRACE("%p, refcount %ld.\n", iface, ref);
1840 if (ref == 0)
1842 IMalloc *imalloc = This->imalloc;
1843 if (This->output) IUnknown_Release(This->output);
1844 if (This->stream) ISequentialStream_Release(This->stream);
1845 free_output_buffer(This);
1846 writeroutput_free(This, This->encoding_name);
1847 writeroutput_free(This, This);
1848 if (imalloc) IMalloc_Release(imalloc);
1851 return ref;
1854 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1856 xmlwriteroutput_QueryInterface,
1857 xmlwriteroutput_AddRef,
1858 xmlwriteroutput_Release
1861 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1863 xmlwriter *writer;
1864 HRESULT hr;
1866 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1868 if (imalloc)
1869 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1870 else
1871 writer = heap_alloc(sizeof(*writer));
1872 if (!writer)
1873 return E_OUTOFMEMORY;
1875 memset(writer, 0, sizeof(*writer));
1877 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1878 writer->ref = 1;
1879 writer->imalloc = imalloc;
1880 if (imalloc) IMalloc_AddRef(imalloc);
1881 writer->bom = TRUE;
1882 writer->conformance = XmlConformanceLevel_Document;
1883 writer->state = XmlWriterState_Initial;
1884 list_init(&writer->elements);
1886 hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
1887 IXmlWriter_Release(&writer->IXmlWriter_iface);
1889 TRACE("returning iface %p, hr %#lx.\n", *obj, hr);
1891 return hr;
1894 static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1895 const WCHAR *encoding_name, xmlwriteroutput **out)
1897 xmlwriteroutput *writeroutput;
1898 HRESULT hr;
1900 *out = NULL;
1902 if (imalloc)
1903 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1904 else
1905 writeroutput = heap_alloc(sizeof(*writeroutput));
1906 if (!writeroutput)
1907 return E_OUTOFMEMORY;
1909 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1910 writeroutput->ref = 1;
1911 writeroutput->imalloc = imalloc;
1912 if (imalloc)
1913 IMalloc_AddRef(imalloc);
1914 writeroutput->encoding = encoding;
1915 writeroutput->stream = NULL;
1916 hr = init_output_buffer(writeroutput);
1917 if (FAILED(hr)) {
1918 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1919 return hr;
1922 if (encoding_name) {
1923 unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
1924 writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
1925 memcpy(writeroutput->encoding_name, encoding_name, size);
1927 else
1928 writeroutput->encoding_name = NULL;
1929 writeroutput->written = 0;
1931 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1933 *out = writeroutput;
1935 TRACE("Created writer output %p\n", *out);
1937 return S_OK;
1940 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
1941 IXmlWriterOutput **out)
1943 xmlwriteroutput *output;
1944 xml_encoding xml_enc;
1945 HRESULT hr;
1947 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
1949 if (!stream || !out)
1950 return E_INVALIDARG;
1952 *out = NULL;
1954 xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
1955 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
1956 *out = &output->IXmlWriterOutput_iface;
1958 return hr;
1961 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
1962 IXmlWriterOutput **out)
1964 xmlwriteroutput *output;
1965 xml_encoding xml_enc;
1966 HRESULT hr;
1968 TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
1970 if (!stream || !out)
1971 return E_INVALIDARG;
1973 *out = NULL;
1975 xml_enc = get_encoding_from_codepage(codepage);
1976 if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
1977 *out = &output->IXmlWriterOutput_iface;
1979 return hr;