hidclass.sys: Correct input vs output parameters for IOCTL_HID_SET_FEATURE.
[wine.git] / dlls / xmllite / writer.c
blob65d0bccb63c334d9659e50059b06a7433149c28f
1 /*
2 * IXmlWriter implementation
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014 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 <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "objbase.h"
27 #include "xmllite.h"
28 #include "xmllite_private.h"
29 #include "initguid.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
37 /* not defined in public headers */
38 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
40 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
42 static const WCHAR closeelementW[] = {'<','/'};
43 static const WCHAR closepiW[] = {'?','>'};
44 static const WCHAR ltW[] = {'<'};
45 static const WCHAR gtW[] = {'>'};
46 static const WCHAR spaceW[] = {' '};
48 struct output_buffer
50 char *data;
51 unsigned int allocated;
52 unsigned int written;
53 UINT codepage;
56 typedef enum
58 XmlWriterState_Initial, /* output is not set yet */
59 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
60 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
61 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
62 XmlWriterState_ElemStarted, /* writing element */
63 XmlWriterState_Content, /* content is accepted at this point */
64 XmlWriterState_DocClosed /* WriteEndDocument was called */
65 } XmlWriterState;
67 typedef struct
69 IXmlWriterOutput IXmlWriterOutput_iface;
70 LONG ref;
71 IUnknown *output;
72 ISequentialStream *stream;
73 IMalloc *imalloc;
74 xml_encoding encoding;
75 struct output_buffer buffer;
76 } xmlwriteroutput;
78 static const struct IUnknownVtbl xmlwriteroutputvtbl;
80 struct element
82 struct list entry;
83 WCHAR *qname;
84 unsigned int len; /* qname length in chars */
87 typedef struct _xmlwriter
89 IXmlWriter IXmlWriter_iface;
90 LONG ref;
91 IMalloc *imalloc;
92 xmlwriteroutput *output;
93 BOOL indent;
94 BOOL bom;
95 BOOL omitxmldecl;
96 XmlConformanceLevel conformance;
97 XmlWriterState state;
98 BOOL bomwritten;
99 BOOL starttagopen;
100 struct list elements;
101 } xmlwriter;
103 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
105 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
108 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
110 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
113 static const char *debugstr_writer_prop(XmlWriterProperty prop)
115 static const char * const prop_names[] =
117 "MultiLanguage",
118 "Indent",
119 "ByteOrderMark",
120 "OmitXmlDeclaration",
121 "ConformanceLevel"
124 if (prop > _XmlWriterProperty_Last)
125 return wine_dbg_sprintf("unknown property=%d", prop);
127 return prop_names[prop];
130 /* writer output memory allocation functions */
131 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
133 return m_alloc(output->imalloc, len);
136 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
138 m_free(output->imalloc, mem);
141 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
143 return m_realloc(output->imalloc, mem, len);
146 /* writer memory allocation functions */
147 static inline void *writer_alloc(xmlwriter *writer, size_t len)
149 return m_alloc(writer->imalloc, len);
152 static inline void writer_free(xmlwriter *writer, void *mem)
154 m_free(writer->imalloc, mem);
157 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
159 struct element *ret;
160 int len;
162 ret = writer_alloc(writer, sizeof(*ret));
163 if (!ret) return ret;
165 len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
166 len += strlenW(local);
168 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
169 ret->len = len;
170 if (prefix) {
171 static const WCHAR colonW[] = {':',0};
172 strcpyW(ret->qname, prefix);
173 strcatW(ret->qname, colonW);
175 else
176 ret->qname[0] = 0;
177 strcatW(ret->qname, local);
179 return ret;
182 static void free_element(xmlwriter *writer, struct element *element)
184 writer_free(writer, element->qname);
185 writer_free(writer, element);
188 static void push_element(xmlwriter *writer, struct element *element)
190 list_add_head(&writer->elements, &element->entry);
193 static struct element *pop_element(xmlwriter *writer)
195 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
197 if (element)
198 list_remove(&element->entry);
200 return element;
203 static HRESULT init_output_buffer(xmlwriteroutput *output)
205 struct output_buffer *buffer = &output->buffer;
206 const int initial_len = 0x2000;
207 HRESULT hr;
208 UINT cp;
210 hr = get_code_page(output->encoding, &cp);
211 if (FAILED(hr)) return hr;
213 buffer->data = writeroutput_alloc(output, initial_len);
214 if (!buffer->data) return E_OUTOFMEMORY;
216 memset(buffer->data, 0, 4);
217 buffer->allocated = initial_len;
218 buffer->written = 0;
219 buffer->codepage = cp;
221 return S_OK;
224 static void free_output_buffer(xmlwriteroutput *output)
226 struct output_buffer *buffer = &output->buffer;
227 writeroutput_free(output, buffer->data);
228 buffer->data = NULL;
229 buffer->allocated = 0;
230 buffer->written = 0;
233 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
235 struct output_buffer *buffer = &output->buffer;
236 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
237 if (buffer->allocated < buffer->written + length + 4) {
238 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
239 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
240 if (!ptr) return E_OUTOFMEMORY;
241 buffer->data = ptr;
242 buffer->allocated = grown_size;
245 return S_OK;
248 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
250 struct output_buffer *buffer = &output->buffer;
251 int length;
252 HRESULT hr;
253 char *ptr;
255 if (buffer->codepage != ~0) {
256 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
257 hr = grow_output_buffer(output, length);
258 if (FAILED(hr)) return hr;
259 ptr = buffer->data + buffer->written;
260 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
261 buffer->written += len == -1 ? length-1 : length;
263 else {
264 /* WCHAR data just copied */
265 length = len == -1 ? strlenW(data) : len;
266 if (length) {
267 length *= sizeof(WCHAR);
269 hr = grow_output_buffer(output, length);
270 if (FAILED(hr)) return hr;
271 ptr = buffer->data + buffer->written;
273 memcpy(ptr, data, length);
274 buffer->written += length;
275 ptr += length;
276 /* null termination */
277 *(WCHAR*)ptr = 0;
281 return S_OK;
284 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
286 static const WCHAR quoteW[] = {'"'};
287 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
288 write_output_buffer(output, data, len);
289 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
290 return S_OK;
293 /* TODO: test if we need to validate char range */
294 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
296 if (prefix) {
297 static const WCHAR colW[] = {':'};
298 write_output_buffer(output, prefix, -1);
299 write_output_buffer(output, colW, ARRAY_SIZE(colW));
302 write_output_buffer(output, local_name, -1);
304 return S_OK;
307 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
309 if (writeroutput->stream) {
310 ISequentialStream_Release(writeroutput->stream);
311 writeroutput->stream = NULL;
315 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
317 HRESULT hr;
319 writeroutput_release_stream(writeroutput);
320 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
321 if (hr != S_OK)
322 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
324 return hr;
327 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
329 struct output_buffer *buffer;
330 ULONG written, offset = 0;
331 HRESULT hr;
333 if (!output || !output->stream)
334 return S_OK;
336 buffer = &output->buffer;
338 /* It will loop forever until everything is written or an error occurred. */
339 do {
340 written = 0;
341 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
342 if (FAILED(hr)) {
343 WARN("write to stream failed (0x%08x)\n", hr);
344 buffer->written = 0;
345 return hr;
348 offset += written;
349 buffer->written -= written;
350 } while (buffer->written > 0);
352 return S_OK;
355 static HRESULT write_encoding_bom(xmlwriter *writer)
357 if (!writer->bom || writer->bomwritten) return S_OK;
359 if (writer->output->encoding == XmlEncoding_UTF16) {
360 static const char utf16bom[] = {0xff, 0xfe};
361 struct output_buffer *buffer = &writer->output->buffer;
362 int len = sizeof(utf16bom);
363 HRESULT hr;
365 hr = grow_output_buffer(writer->output, len);
366 if (FAILED(hr)) return hr;
367 memcpy(buffer->data + buffer->written, utf16bom, len);
368 buffer->written += len;
371 writer->bomwritten = TRUE;
372 return S_OK;
375 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
377 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
378 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
380 write_encoding_bom(writer);
381 writer->state = XmlWriterState_DocStarted;
382 if (writer->omitxmldecl) return S_OK;
384 /* version */
385 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
387 /* encoding */
388 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
389 write_output_buffer_quoted(writer->output, get_encoding_name(writer->output->encoding), -1);
391 /* standalone */
392 if (standalone == XmlStandalone_Omit)
393 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
394 else {
395 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
396 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
397 static const WCHAR noW[] = {'n','o','\"','?','>'};
399 write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
400 if (standalone == XmlStandalone_Yes)
401 write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
402 else
403 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
406 return S_OK;
409 static HRESULT writer_close_starttag(xmlwriter *writer)
411 HRESULT hr;
413 if (!writer->starttagopen) return S_OK;
414 hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
415 writer->starttagopen = FALSE;
416 writer->state = XmlWriterState_Content;
417 return hr;
420 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
422 xmlwriter *This = impl_from_IXmlWriter(iface);
424 TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
426 if (IsEqualGUID(riid, &IID_IUnknown) ||
427 IsEqualGUID(riid, &IID_IXmlWriter))
429 *ppvObject = iface;
432 IXmlWriter_AddRef(iface);
434 return S_OK;
437 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
439 xmlwriter *This = impl_from_IXmlWriter(iface);
440 TRACE("%p\n", This);
441 return InterlockedIncrement(&This->ref);
444 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
446 xmlwriter *This = impl_from_IXmlWriter(iface);
447 LONG ref;
449 TRACE("%p\n", This);
451 ref = InterlockedDecrement(&This->ref);
452 if (ref == 0) {
453 struct element *element, *element2;
454 IMalloc *imalloc = This->imalloc;
456 IXmlWriter_Flush(iface);
457 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
459 /* element stack */
460 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
461 list_remove(&element->entry);
462 free_element(This, element);
465 writer_free(This, This);
466 if (imalloc) IMalloc_Release(imalloc);
469 return ref;
472 /*** IXmlWriter methods ***/
473 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
475 xmlwriter *This = impl_from_IXmlWriter(iface);
476 IXmlWriterOutput *writeroutput;
477 HRESULT hr;
479 TRACE("(%p)->(%p)\n", This, output);
481 if (This->output) {
482 writeroutput_release_stream(This->output);
483 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
484 This->output = NULL;
485 This->bomwritten = FALSE;
488 /* just reset current output */
489 if (!output) {
490 This->state = XmlWriterState_Initial;
491 return S_OK;
494 /* now try IXmlWriterOutput, ISequentialStream, IStream */
495 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
496 if (hr == S_OK) {
497 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
498 This->output = impl_from_IXmlWriterOutput(writeroutput);
499 else {
500 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
501 writeroutput, writeroutput->lpVtbl);
502 IUnknown_Release(writeroutput);
503 return E_FAIL;
508 if (hr != S_OK || !writeroutput) {
509 /* create IXmlWriterOutput basing on supplied interface */
510 hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
511 if (hr != S_OK) return hr;
512 This->output = impl_from_IXmlWriterOutput(writeroutput);
515 This->state = XmlWriterState_Ready;
516 return writeroutput_query_for_stream(This->output);
519 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
521 xmlwriter *This = impl_from_IXmlWriter(iface);
523 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
525 if (!value) return E_INVALIDARG;
527 switch (property)
529 case XmlWriterProperty_Indent:
530 *value = This->indent;
531 break;
532 case XmlWriterProperty_ByteOrderMark:
533 *value = This->bom;
534 break;
535 case XmlWriterProperty_OmitXmlDeclaration:
536 *value = This->omitxmldecl;
537 break;
538 case XmlWriterProperty_ConformanceLevel:
539 *value = This->conformance;
540 break;
541 default:
542 FIXME("Unimplemented property (%u)\n", property);
543 return E_NOTIMPL;
546 return S_OK;
549 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
551 xmlwriter *This = impl_from_IXmlWriter(iface);
553 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
555 switch (property)
557 case XmlWriterProperty_ByteOrderMark:
558 This->bom = !!value;
559 break;
560 case XmlWriterProperty_OmitXmlDeclaration:
561 This->omitxmldecl = !!value;
562 break;
563 default:
564 FIXME("Unimplemented property (%u)\n", property);
565 return E_NOTIMPL;
568 return S_OK;
571 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
572 BOOL fWriteDefaultAttributes)
574 xmlwriter *This = impl_from_IXmlWriter(iface);
576 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
578 return E_NOTIMPL;
581 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR pwszPrefix,
582 LPCWSTR pwszLocalName, LPCWSTR pwszNamespaceUri,
583 LPCWSTR pwszValue)
585 xmlwriter *This = impl_from_IXmlWriter(iface);
587 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszPrefix), wine_dbgstr_w(pwszLocalName),
588 wine_dbgstr_w(pwszNamespaceUri), wine_dbgstr_w(pwszValue));
590 switch (This->state)
592 case XmlWriterState_Initial:
593 return E_UNEXPECTED;
594 case XmlWriterState_Ready:
595 case XmlWriterState_DocClosed:
596 This->state = XmlWriterState_DocClosed;
597 return WR_E_INVALIDACTION;
598 default:
602 return E_NOTIMPL;
605 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
607 static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
608 static const WCHAR cdatacloseW[] = {']',']','>'};
609 write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
610 if (data)
611 write_output_buffer(output, data, len);
612 write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
615 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
617 xmlwriter *This = impl_from_IXmlWriter(iface);
618 int len;
620 TRACE("%p %s\n", This, debugstr_w(data));
622 switch (This->state)
624 case XmlWriterState_Initial:
625 return E_UNEXPECTED;
626 case XmlWriterState_ElemStarted:
627 writer_close_starttag(This);
628 break;
629 case XmlWriterState_Ready:
630 case XmlWriterState_DocClosed:
631 This->state = XmlWriterState_DocClosed;
632 return WR_E_INVALIDACTION;
633 default:
637 len = data ? strlenW(data) : 0;
639 if (!len)
640 write_cdata_section(This->output, NULL, 0);
641 else {
642 static const WCHAR cdatacloseW[] = {']',']','>',0};
643 while (len) {
644 const WCHAR *str = strstrW(data, cdatacloseW);
645 if (str) {
646 str += 2;
647 write_cdata_section(This->output, data, str - data);
648 len -= str - data;
649 data = str;
651 else {
652 write_cdata_section(This->output, data, len);
653 break;
658 return S_OK;
661 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
663 xmlwriter *This = impl_from_IXmlWriter(iface);
665 FIXME("%p %x\n", This, ch);
667 switch (This->state)
669 case XmlWriterState_Initial:
670 return E_UNEXPECTED;
671 case XmlWriterState_DocClosed:
672 return WR_E_INVALIDACTION;
673 default:
677 return E_NOTIMPL;
680 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
682 xmlwriter *This = impl_from_IXmlWriter(iface);
684 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
686 switch (This->state)
688 case XmlWriterState_Initial:
689 return E_UNEXPECTED;
690 case XmlWriterState_DocClosed:
691 return WR_E_INVALIDACTION;
692 default:
696 return E_NOTIMPL;
699 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
701 static const WCHAR copenW[] = {'<','!','-','-'};
702 static const WCHAR ccloseW[] = {'-','-','>'};
703 xmlwriter *This = impl_from_IXmlWriter(iface);
705 TRACE("%p %s\n", This, debugstr_w(comment));
707 switch (This->state)
709 case XmlWriterState_Initial:
710 return E_UNEXPECTED;
711 case XmlWriterState_ElemStarted:
712 writer_close_starttag(This);
713 break;
714 case XmlWriterState_DocClosed:
715 return WR_E_INVALIDACTION;
716 default:
720 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
721 if (comment) {
722 int len = strlenW(comment), i;
724 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
725 comment string */
726 if (len > 1) {
727 for (i = 0; i < len; i++) {
728 write_output_buffer(This->output, comment + i, 1);
729 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
730 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
733 else
734 write_output_buffer(This->output, comment, len);
736 if (len && comment[len-1] == '-')
737 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
739 write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
741 return S_OK;
744 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
745 LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
747 xmlwriter *This = impl_from_IXmlWriter(iface);
749 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
750 wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
752 return E_NOTIMPL;
755 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
756 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
758 xmlwriter *This = impl_from_IXmlWriter(iface);
760 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
761 wine_dbgstr_w(uri), wine_dbgstr_w(value));
763 switch (This->state)
765 case XmlWriterState_Initial:
766 return E_UNEXPECTED;
767 case XmlWriterState_ElemStarted:
768 writer_close_starttag(This);
769 break;
770 case XmlWriterState_Ready:
771 case XmlWriterState_DocStarted:
772 case XmlWriterState_PIDocStarted:
773 break;
774 default:
775 This->state = XmlWriterState_DocClosed;
776 return WR_E_INVALIDACTION;
779 write_encoding_bom(This);
780 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
781 write_output_qname(This->output, prefix, local_name);
782 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
784 if (value)
785 write_output_buffer(This->output, value, -1);
787 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
788 write_output_qname(This->output, prefix, local_name);
789 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
790 This->state = XmlWriterState_Content;
792 return S_OK;
795 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
797 xmlwriter *This = impl_from_IXmlWriter(iface);
799 TRACE("%p\n", This);
801 switch (This->state)
803 case XmlWriterState_Initial:
804 return E_UNEXPECTED;
805 case XmlWriterState_Ready:
806 case XmlWriterState_DocClosed:
807 This->state = XmlWriterState_DocClosed;
808 return WR_E_INVALIDACTION;
809 default:
813 /* empty element stack */
814 while (IXmlWriter_WriteEndElement(iface) == S_OK)
817 This->state = XmlWriterState_DocClosed;
818 return S_OK;
821 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
823 xmlwriter *This = impl_from_IXmlWriter(iface);
824 struct element *element;
826 TRACE("%p\n", This);
828 switch (This->state)
830 case XmlWriterState_Initial:
831 return E_UNEXPECTED;
832 case XmlWriterState_Ready:
833 case XmlWriterState_DocClosed:
834 This->state = XmlWriterState_DocClosed;
835 return WR_E_INVALIDACTION;
836 default:
840 element = pop_element(This);
841 if (!element)
842 return WR_E_INVALIDACTION;
844 if (This->starttagopen) {
845 static WCHAR closetagW[] = {' ','/','>'};
846 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
847 This->starttagopen = FALSE;
849 else {
850 /* write full end tag */
851 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
852 write_output_buffer(This->output, element->qname, element->len);
853 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
856 return S_OK;
859 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
861 xmlwriter *This = impl_from_IXmlWriter(iface);
863 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
865 switch (This->state)
867 case XmlWriterState_Initial:
868 return E_UNEXPECTED;
869 case XmlWriterState_DocClosed:
870 return WR_E_INVALIDACTION;
871 default:
875 return E_NOTIMPL;
878 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
880 xmlwriter *This = impl_from_IXmlWriter(iface);
881 struct element *element;
883 TRACE("%p\n", This);
885 switch (This->state)
887 case XmlWriterState_Initial:
888 return E_UNEXPECTED;
889 case XmlWriterState_Ready:
890 case XmlWriterState_DocClosed:
891 This->state = XmlWriterState_DocClosed;
892 return WR_E_INVALIDACTION;
893 default:
897 element = pop_element(This);
898 if (!element)
899 return WR_E_INVALIDACTION;
901 /* write full end tag */
902 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
903 write_output_buffer(This->output, element->qname, element->len);
904 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
905 This->starttagopen = FALSE;
907 return S_OK;
910 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
912 xmlwriter *This = impl_from_IXmlWriter(iface);
914 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
916 switch (This->state)
918 case XmlWriterState_Initial:
919 return E_UNEXPECTED;
920 case XmlWriterState_Ready:
921 case XmlWriterState_DocClosed:
922 This->state = XmlWriterState_DocClosed;
923 return WR_E_INVALIDACTION;
924 default:
928 return E_NOTIMPL;
931 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
933 xmlwriter *This = impl_from_IXmlWriter(iface);
935 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
937 switch (This->state)
939 case XmlWriterState_Initial:
940 return E_UNEXPECTED;
941 case XmlWriterState_Ready:
942 case XmlWriterState_DocClosed:
943 This->state = XmlWriterState_DocClosed;
944 return WR_E_INVALIDACTION;
945 default:
949 return E_NOTIMPL;
952 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
953 BOOL fWriteDefaultAttributes)
955 xmlwriter *This = impl_from_IXmlWriter(iface);
957 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
959 return E_NOTIMPL;
962 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
963 BOOL fWriteDefaultAttributes)
965 xmlwriter *This = impl_from_IXmlWriter(iface);
967 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
969 return E_NOTIMPL;
972 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
973 LPCWSTR text)
975 xmlwriter *This = impl_from_IXmlWriter(iface);
976 static const WCHAR xmlW[] = {'x','m','l',0};
977 static const WCHAR openpiW[] = {'<','?'};
979 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
981 switch (This->state)
983 case XmlWriterState_Initial:
984 return E_UNEXPECTED;
985 case XmlWriterState_DocStarted:
986 if (!strcmpW(name, xmlW))
987 return WR_E_INVALIDACTION;
988 break;
989 case XmlWriterState_ElemStarted:
990 case XmlWriterState_DocClosed:
991 return WR_E_INVALIDACTION;
992 default:
996 write_encoding_bom(This);
997 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
998 write_output_buffer(This->output, name, -1);
999 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
1000 write_output_buffer(This->output, text, -1);
1001 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1003 if (!strcmpW(name, xmlW))
1004 This->state = XmlWriterState_PIDocStarted;
1006 return S_OK;
1009 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1010 LPCWSTR pwszNamespaceUri)
1012 xmlwriter *This = impl_from_IXmlWriter(iface);
1014 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1016 switch (This->state)
1018 case XmlWriterState_Initial:
1019 return E_UNEXPECTED;
1020 case XmlWriterState_DocClosed:
1021 return WR_E_INVALIDACTION;
1022 default:
1026 return E_NOTIMPL;
1029 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1031 xmlwriter *This = impl_from_IXmlWriter(iface);
1033 TRACE("%p %s\n", This, debugstr_w(data));
1035 if (!data)
1036 return S_OK;
1038 switch (This->state)
1040 case XmlWriterState_Initial:
1041 return E_UNEXPECTED;
1042 case XmlWriterState_Ready:
1043 write_xmldecl(This, XmlStandalone_Omit);
1044 /* fallthrough */
1045 case XmlWriterState_DocStarted:
1046 case XmlWriterState_PIDocStarted:
1047 break;
1048 default:
1049 This->state = XmlWriterState_DocClosed;
1050 return WR_E_INVALIDACTION;
1053 write_output_buffer(This->output, data, -1);
1054 return S_OK;
1057 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1059 xmlwriter *This = impl_from_IXmlWriter(iface);
1061 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1063 switch (This->state)
1065 case XmlWriterState_Initial:
1066 return E_UNEXPECTED;
1067 case XmlWriterState_DocClosed:
1068 return WR_E_INVALIDACTION;
1069 default:
1073 return E_NOTIMPL;
1076 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1078 xmlwriter *This = impl_from_IXmlWriter(iface);
1080 TRACE("(%p)->(%d)\n", This, standalone);
1082 switch (This->state)
1084 case XmlWriterState_Initial:
1085 return E_UNEXPECTED;
1086 case XmlWriterState_PIDocStarted:
1087 This->state = XmlWriterState_DocStarted;
1088 return S_OK;
1089 case XmlWriterState_Ready:
1090 break;
1091 default:
1092 This->state = XmlWriterState_DocClosed;
1093 return WR_E_INVALIDACTION;
1096 return write_xmldecl(This, standalone);
1099 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1101 xmlwriter *This = impl_from_IXmlWriter(iface);
1102 struct element *element;
1104 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1106 if (!local_name)
1107 return E_INVALIDARG;
1109 switch (This->state)
1111 case XmlWriterState_Initial:
1112 return E_UNEXPECTED;
1113 case XmlWriterState_DocClosed:
1114 return WR_E_INVALIDACTION;
1115 default:
1119 /* close pending element */
1120 if (This->starttagopen)
1121 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1123 element = alloc_element(This, prefix, local_name);
1124 if (!element)
1125 return E_OUTOFMEMORY;
1127 write_encoding_bom(This);
1128 This->state = XmlWriterState_ElemStarted;
1129 This->starttagopen = TRUE;
1131 push_element(This, element);
1133 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
1134 write_output_qname(This->output, prefix, local_name);
1136 return S_OK;
1139 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, LPCWSTR pwszText)
1141 xmlwriter *This = impl_from_IXmlWriter(iface);
1143 FIXME("%p %s\n", This, wine_dbgstr_w(pwszText));
1145 switch (This->state)
1147 case XmlWriterState_Initial:
1148 return E_UNEXPECTED;
1149 case XmlWriterState_Ready:
1150 case XmlWriterState_DocClosed:
1151 This->state = XmlWriterState_DocClosed;
1152 return WR_E_INVALIDACTION;
1153 default:
1157 return E_NOTIMPL;
1160 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1162 xmlwriter *This = impl_from_IXmlWriter(iface);
1164 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1166 return E_NOTIMPL;
1169 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1171 xmlwriter *This = impl_from_IXmlWriter(iface);
1173 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1175 return E_NOTIMPL;
1178 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1180 xmlwriter *This = impl_from_IXmlWriter(iface);
1182 TRACE("%p\n", This);
1184 return writeroutput_flush_stream(This->output);
1187 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1189 xmlwriter_QueryInterface,
1190 xmlwriter_AddRef,
1191 xmlwriter_Release,
1192 xmlwriter_SetOutput,
1193 xmlwriter_GetProperty,
1194 xmlwriter_SetProperty,
1195 xmlwriter_WriteAttributes,
1196 xmlwriter_WriteAttributeString,
1197 xmlwriter_WriteCData,
1198 xmlwriter_WriteCharEntity,
1199 xmlwriter_WriteChars,
1200 xmlwriter_WriteComment,
1201 xmlwriter_WriteDocType,
1202 xmlwriter_WriteElementString,
1203 xmlwriter_WriteEndDocument,
1204 xmlwriter_WriteEndElement,
1205 xmlwriter_WriteEntityRef,
1206 xmlwriter_WriteFullEndElement,
1207 xmlwriter_WriteName,
1208 xmlwriter_WriteNmToken,
1209 xmlwriter_WriteNode,
1210 xmlwriter_WriteNodeShallow,
1211 xmlwriter_WriteProcessingInstruction,
1212 xmlwriter_WriteQualifiedName,
1213 xmlwriter_WriteRaw,
1214 xmlwriter_WriteRawChars,
1215 xmlwriter_WriteStartDocument,
1216 xmlwriter_WriteStartElement,
1217 xmlwriter_WriteString,
1218 xmlwriter_WriteSurrogateCharEntity,
1219 xmlwriter_WriteWhitespace,
1220 xmlwriter_Flush
1223 /** IXmlWriterOutput **/
1224 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1226 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1228 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1230 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1231 IsEqualGUID(riid, &IID_IUnknown))
1233 *ppvObject = iface;
1235 else
1237 WARN("interface %s not implemented\n", debugstr_guid(riid));
1238 *ppvObject = NULL;
1239 return E_NOINTERFACE;
1242 IUnknown_AddRef(iface);
1244 return S_OK;
1247 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1249 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1250 ULONG ref = InterlockedIncrement(&This->ref);
1251 TRACE("(%p)->(%d)\n", This, ref);
1252 return ref;
1255 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1257 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1258 LONG ref = InterlockedDecrement(&This->ref);
1260 TRACE("(%p)->(%d)\n", This, ref);
1262 if (ref == 0)
1264 IMalloc *imalloc = This->imalloc;
1265 if (This->output) IUnknown_Release(This->output);
1266 if (This->stream) ISequentialStream_Release(This->stream);
1267 free_output_buffer(This);
1268 writeroutput_free(This, This);
1269 if (imalloc) IMalloc_Release(imalloc);
1272 return ref;
1275 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1277 xmlwriteroutput_QueryInterface,
1278 xmlwriteroutput_AddRef,
1279 xmlwriteroutput_Release
1282 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1284 xmlwriter *writer;
1286 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1288 if (!IsEqualGUID(riid, &IID_IXmlWriter))
1290 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1291 return E_FAIL;
1294 if (imalloc)
1295 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1296 else
1297 writer = heap_alloc(sizeof(*writer));
1298 if(!writer) return E_OUTOFMEMORY;
1300 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1301 writer->ref = 1;
1302 writer->imalloc = imalloc;
1303 if (imalloc) IMalloc_AddRef(imalloc);
1304 writer->output = NULL;
1305 writer->indent = FALSE;
1306 writer->bom = TRUE;
1307 writer->omitxmldecl = FALSE;
1308 writer->conformance = XmlConformanceLevel_Document;
1309 writer->state = XmlWriterState_Initial;
1310 writer->bomwritten = FALSE;
1311 writer->starttagopen = FALSE;
1312 list_init(&writer->elements);
1314 *obj = &writer->IXmlWriter_iface;
1316 TRACE("returning iface %p\n", *obj);
1318 return S_OK;
1321 static HRESULT create_writer(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1322 IXmlWriterOutput **output)
1324 xmlwriteroutput *writeroutput;
1325 HRESULT hr;
1327 *output = NULL;
1329 if (imalloc)
1330 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1331 else
1332 writeroutput = heap_alloc(sizeof(*writeroutput));
1333 if(!writeroutput) return E_OUTOFMEMORY;
1335 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1336 writeroutput->ref = 1;
1337 writeroutput->imalloc = imalloc;
1338 if (imalloc) IMalloc_AddRef(imalloc);
1339 writeroutput->encoding = encoding;
1340 writeroutput->stream = NULL;
1341 hr = init_output_buffer(writeroutput);
1342 if (FAILED(hr)) {
1343 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1344 return hr;
1347 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1349 *output = &writeroutput->IXmlWriterOutput_iface;
1351 TRACE("returning iface %p\n", *output);
1353 return S_OK;
1356 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1357 IMalloc *imalloc,
1358 LPCWSTR encoding,
1359 IXmlWriterOutput **output)
1361 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1362 xml_encoding xml_enc;
1364 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1366 if (!stream || !output) return E_INVALIDARG;
1368 xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1);
1369 return create_writer(stream, imalloc, xml_enc, output);
1372 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream,
1373 IMalloc *imalloc,
1374 UINT codepage,
1375 IXmlWriterOutput **output)
1377 xml_encoding xml_enc;
1379 TRACE("%p %p %u %p\n", stream, imalloc, codepage, output);
1381 if (!stream || !output) return E_INVALIDARG;
1383 xml_enc = get_encoding_from_codepage(codepage);
1384 return create_writer(stream, imalloc, xml_enc, output);