[XMLLITE] Sync with Wine Staging 2.2. CORE-12823
[reactos.git] / reactos / dll / win32 / xmllite / writer.c
1 /*
2 * IXmlWriter implementation
3 *
4 * Copyright 2011 Alistair Leslie-Hughes
5 * Copyright 2014, 2016 Nikolay Sivov for CodeWeavers
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include "xmllite_private.h"
23
24 #include <wine/list.h>
25 #include <wine/unicode.h>
26
27 /* not defined in public headers */
28 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
29
30 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
31
32 static const WCHAR closeelementW[] = {'<','/'};
33 static const WCHAR closepiW[] = {'?','>'};
34 static const WCHAR ltW[] = {'<'};
35 static const WCHAR gtW[] = {'>'};
36 static const WCHAR spaceW[] = {' '};
37 static const WCHAR quoteW[] = {'"'};
38
39 struct output_buffer
40 {
41 char *data;
42 unsigned int allocated;
43 unsigned int written;
44 UINT codepage;
45 };
46
47 typedef enum
48 {
49 XmlWriterState_Initial, /* output is not set yet */
50 XmlWriterState_Ready, /* SetOutput() was called, ready to start */
51 XmlWriterState_PIDocStarted, /* document was started with manually added 'xml' PI */
52 XmlWriterState_DocStarted, /* document was started with WriteStartDocument() */
53 XmlWriterState_ElemStarted, /* writing element */
54 XmlWriterState_Content, /* content is accepted at this point */
55 XmlWriterState_DocClosed /* WriteEndDocument was called */
56 } XmlWriterState;
57
58 typedef struct
59 {
60 IXmlWriterOutput IXmlWriterOutput_iface;
61 LONG ref;
62 IUnknown *output;
63 ISequentialStream *stream;
64 IMalloc *imalloc;
65 xml_encoding encoding;
66 struct output_buffer buffer;
67 } xmlwriteroutput;
68
69 static const struct IUnknownVtbl xmlwriteroutputvtbl;
70
71 struct element
72 {
73 struct list entry;
74 WCHAR *qname;
75 unsigned int len; /* qname length in chars */
76 };
77
78 typedef struct _xmlwriter
79 {
80 IXmlWriter IXmlWriter_iface;
81 LONG ref;
82 IMalloc *imalloc;
83 xmlwriteroutput *output;
84 unsigned int indent_level;
85 BOOL indent;
86 BOOL bom;
87 BOOL omitxmldecl;
88 XmlConformanceLevel conformance;
89 XmlWriterState state;
90 BOOL bomwritten;
91 BOOL starttagopen;
92 struct list elements;
93 } xmlwriter;
94
95 static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
96 {
97 return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
98 }
99
100 static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
101 {
102 return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
103 }
104
105 static const char *debugstr_writer_prop(XmlWriterProperty prop)
106 {
107 static const char * const prop_names[] =
108 {
109 "MultiLanguage",
110 "Indent",
111 "ByteOrderMark",
112 "OmitXmlDeclaration",
113 "ConformanceLevel"
114 };
115
116 if (prop > _XmlWriterProperty_Last)
117 return wine_dbg_sprintf("unknown property=%d", prop);
118
119 return prop_names[prop];
120 }
121
122 /* writer output memory allocation functions */
123 static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
124 {
125 return m_alloc(output->imalloc, len);
126 }
127
128 static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
129 {
130 m_free(output->imalloc, mem);
131 }
132
133 static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
134 {
135 return m_realloc(output->imalloc, mem, len);
136 }
137
138 /* writer memory allocation functions */
139 static inline void *writer_alloc(xmlwriter *writer, size_t len)
140 {
141 return m_alloc(writer->imalloc, len);
142 }
143
144 static inline void writer_free(xmlwriter *writer, void *mem)
145 {
146 m_free(writer->imalloc, mem);
147 }
148
149 static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
150 {
151 struct element *ret;
152 int len;
153
154 ret = writer_alloc(writer, sizeof(*ret));
155 if (!ret) return ret;
156
157 len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
158 len += strlenW(local);
159
160 ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
161 ret->len = len;
162 if (prefix) {
163 static const WCHAR colonW[] = {':',0};
164 strcpyW(ret->qname, prefix);
165 strcatW(ret->qname, colonW);
166 }
167 else
168 ret->qname[0] = 0;
169 strcatW(ret->qname, local);
170
171 return ret;
172 }
173
174 static void free_element(xmlwriter *writer, struct element *element)
175 {
176 writer_free(writer, element->qname);
177 writer_free(writer, element);
178 }
179
180 static void push_element(xmlwriter *writer, struct element *element)
181 {
182 list_add_head(&writer->elements, &element->entry);
183 }
184
185 static struct element *pop_element(xmlwriter *writer)
186 {
187 struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
188
189 if (element)
190 list_remove(&element->entry);
191
192 return element;
193 }
194
195 static HRESULT init_output_buffer(xmlwriteroutput *output)
196 {
197 struct output_buffer *buffer = &output->buffer;
198 const int initial_len = 0x2000;
199 HRESULT hr;
200 UINT cp;
201
202 hr = get_code_page(output->encoding, &cp);
203 if (FAILED(hr)) return hr;
204
205 buffer->data = writeroutput_alloc(output, initial_len);
206 if (!buffer->data) return E_OUTOFMEMORY;
207
208 memset(buffer->data, 0, 4);
209 buffer->allocated = initial_len;
210 buffer->written = 0;
211 buffer->codepage = cp;
212
213 return S_OK;
214 }
215
216 static void free_output_buffer(xmlwriteroutput *output)
217 {
218 struct output_buffer *buffer = &output->buffer;
219 writeroutput_free(output, buffer->data);
220 buffer->data = NULL;
221 buffer->allocated = 0;
222 buffer->written = 0;
223 }
224
225 static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
226 {
227 struct output_buffer *buffer = &output->buffer;
228 /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
229 if (buffer->allocated < buffer->written + length + 4) {
230 int grown_size = max(2*buffer->allocated, buffer->allocated + length);
231 char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
232 if (!ptr) return E_OUTOFMEMORY;
233 buffer->data = ptr;
234 buffer->allocated = grown_size;
235 }
236
237 return S_OK;
238 }
239
240 static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
241 {
242 struct output_buffer *buffer = &output->buffer;
243 int length;
244 HRESULT hr;
245 char *ptr;
246
247 if (buffer->codepage != ~0) {
248 length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
249 hr = grow_output_buffer(output, length);
250 if (FAILED(hr)) return hr;
251 ptr = buffer->data + buffer->written;
252 length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
253 buffer->written += len == -1 ? length-1 : length;
254 }
255 else {
256 /* WCHAR data just copied */
257 length = len == -1 ? strlenW(data) : len;
258 if (length) {
259 length *= sizeof(WCHAR);
260
261 hr = grow_output_buffer(output, length);
262 if (FAILED(hr)) return hr;
263 ptr = buffer->data + buffer->written;
264
265 memcpy(ptr, data, length);
266 buffer->written += length;
267 ptr += length;
268 /* null termination */
269 *(WCHAR*)ptr = 0;
270 }
271 }
272
273 return S_OK;
274 }
275
276 static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
277 {
278 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
279 write_output_buffer(output, data, len);
280 write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
281 return S_OK;
282 }
283
284 /* TODO: test if we need to validate char range */
285 static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, const WCHAR *local_name)
286 {
287 if (prefix) {
288 static const WCHAR colW[] = {':'};
289 write_output_buffer(output, prefix, -1);
290 write_output_buffer(output, colW, ARRAY_SIZE(colW));
291 }
292
293 write_output_buffer(output, local_name, -1);
294
295 return S_OK;
296 }
297
298 static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
299 {
300 if (writeroutput->stream) {
301 ISequentialStream_Release(writeroutput->stream);
302 writeroutput->stream = NULL;
303 }
304 }
305
306 static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
307 {
308 HRESULT hr;
309
310 writeroutput_release_stream(writeroutput);
311 hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
312 if (hr != S_OK)
313 hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);
314
315 return hr;
316 }
317
318 static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
319 {
320 struct output_buffer *buffer;
321 ULONG written, offset = 0;
322 HRESULT hr;
323
324 if (!output || !output->stream)
325 return S_OK;
326
327 buffer = &output->buffer;
328
329 /* It will loop forever until everything is written or an error occurred. */
330 do {
331 written = 0;
332 hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
333 if (FAILED(hr)) {
334 WARN("write to stream failed (0x%08x)\n", hr);
335 buffer->written = 0;
336 return hr;
337 }
338
339 offset += written;
340 buffer->written -= written;
341 } while (buffer->written > 0);
342
343 return S_OK;
344 }
345
346 static HRESULT write_encoding_bom(xmlwriter *writer)
347 {
348 if (!writer->bom || writer->bomwritten) return S_OK;
349
350 if (writer->output->encoding == XmlEncoding_UTF16) {
351 static const char utf16bom[] = {0xff, 0xfe};
352 struct output_buffer *buffer = &writer->output->buffer;
353 int len = sizeof(utf16bom);
354 HRESULT hr;
355
356 hr = grow_output_buffer(writer->output, len);
357 if (FAILED(hr)) return hr;
358 memcpy(buffer->data + buffer->written, utf16bom, len);
359 buffer->written += len;
360 }
361
362 writer->bomwritten = TRUE;
363 return S_OK;
364 }
365
366 static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
367 {
368 static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"'};
369 static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','='};
370
371 write_encoding_bom(writer);
372 writer->state = XmlWriterState_DocStarted;
373 if (writer->omitxmldecl) return S_OK;
374
375 /* version */
376 write_output_buffer(writer->output, versionW, ARRAY_SIZE(versionW));
377
378 /* encoding */
379 write_output_buffer(writer->output, encodingW, ARRAY_SIZE(encodingW));
380 write_output_buffer_quoted(writer->output, get_encoding_name(writer->output->encoding), -1);
381
382 /* standalone */
383 if (standalone == XmlStandalone_Omit)
384 write_output_buffer(writer->output, closepiW, ARRAY_SIZE(closepiW));
385 else {
386 static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
387 static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
388 static const WCHAR noW[] = {'n','o','\"','?','>'};
389
390 write_output_buffer(writer->output, standaloneW, ARRAY_SIZE(standaloneW));
391 if (standalone == XmlStandalone_Yes)
392 write_output_buffer(writer->output, yesW, ARRAY_SIZE(yesW));
393 else
394 write_output_buffer(writer->output, noW, ARRAY_SIZE(noW));
395 }
396
397 return S_OK;
398 }
399
400 static HRESULT writer_close_starttag(xmlwriter *writer)
401 {
402 HRESULT hr;
403
404 if (!writer->starttagopen) return S_OK;
405 hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
406 writer->starttagopen = FALSE;
407 return hr;
408 }
409
410 static void writer_inc_indent(xmlwriter *writer)
411 {
412 writer->indent_level++;
413 }
414
415 static void writer_dec_indent(xmlwriter *writer)
416 {
417 if (writer->indent_level)
418 writer->indent_level--;
419 }
420
421 static void write_node_indent(xmlwriter *writer)
422 {
423 static const WCHAR dblspaceW[] = {' ',' '};
424 static const WCHAR crlfW[] = {'\r','\n'};
425 unsigned int indent_level = writer->indent_level;
426
427 if (!writer->indent)
428 return;
429
430 if (writer->output->buffer.written)
431 write_output_buffer(writer->output, crlfW, ARRAY_SIZE(crlfW));
432 while (indent_level--)
433 write_output_buffer(writer->output, dblspaceW, ARRAY_SIZE(dblspaceW));
434 }
435
436 static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
437 {
438 xmlwriter *This = impl_from_IXmlWriter(iface);
439
440 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
441
442 if (IsEqualGUID(riid, &IID_IUnknown) ||
443 IsEqualGUID(riid, &IID_IXmlWriter))
444 {
445 *ppvObject = iface;
446 }
447
448 IXmlWriter_AddRef(iface);
449
450 return S_OK;
451 }
452
453 static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
454 {
455 xmlwriter *This = impl_from_IXmlWriter(iface);
456 ULONG ref = InterlockedIncrement(&This->ref);
457 TRACE("(%p)->(%u)\n", This, ref);
458 return ref;
459 }
460
461 static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
462 {
463 xmlwriter *This = impl_from_IXmlWriter(iface);
464 ULONG ref = InterlockedDecrement(&This->ref);
465
466 TRACE("(%p)->>(%u)\n", This, ref);
467
468 if (ref == 0) {
469 struct element *element, *element2;
470 IMalloc *imalloc = This->imalloc;
471
472 writeroutput_flush_stream(This->output);
473 if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
474
475 /* element stack */
476 LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
477 list_remove(&element->entry);
478 free_element(This, element);
479 }
480
481 writer_free(This, This);
482 if (imalloc) IMalloc_Release(imalloc);
483 }
484
485 return ref;
486 }
487
488 /*** IXmlWriter methods ***/
489 static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
490 {
491 xmlwriter *This = impl_from_IXmlWriter(iface);
492 IXmlWriterOutput *writeroutput;
493 HRESULT hr;
494
495 TRACE("(%p)->(%p)\n", This, output);
496
497 if (This->output) {
498 writeroutput_release_stream(This->output);
499 IUnknown_Release(&This->output->IXmlWriterOutput_iface);
500 This->output = NULL;
501 This->bomwritten = FALSE;
502 This->indent_level = 0;
503 }
504
505 /* just reset current output */
506 if (!output) {
507 This->state = XmlWriterState_Initial;
508 return S_OK;
509 }
510
511 /* now try IXmlWriterOutput, ISequentialStream, IStream */
512 hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
513 if (hr == S_OK) {
514 if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
515 This->output = impl_from_IXmlWriterOutput(writeroutput);
516 else {
517 ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
518 writeroutput, writeroutput->lpVtbl);
519 IUnknown_Release(writeroutput);
520 return E_FAIL;
521
522 }
523 }
524
525 if (hr != S_OK || !writeroutput) {
526 /* create IXmlWriterOutput basing on supplied interface */
527 hr = CreateXmlWriterOutputWithEncodingName(output, This->imalloc, NULL, &writeroutput);
528 if (hr != S_OK) return hr;
529 This->output = impl_from_IXmlWriterOutput(writeroutput);
530 }
531
532 This->state = XmlWriterState_Ready;
533 return writeroutput_query_for_stream(This->output);
534 }
535
536 static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
537 {
538 xmlwriter *This = impl_from_IXmlWriter(iface);
539
540 TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
541
542 if (!value) return E_INVALIDARG;
543
544 switch (property)
545 {
546 case XmlWriterProperty_Indent:
547 *value = This->indent;
548 break;
549 case XmlWriterProperty_ByteOrderMark:
550 *value = This->bom;
551 break;
552 case XmlWriterProperty_OmitXmlDeclaration:
553 *value = This->omitxmldecl;
554 break;
555 case XmlWriterProperty_ConformanceLevel:
556 *value = This->conformance;
557 break;
558 default:
559 FIXME("Unimplemented property (%u)\n", property);
560 return E_NOTIMPL;
561 }
562
563 return S_OK;
564 }
565
566 static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
567 {
568 xmlwriter *This = impl_from_IXmlWriter(iface);
569
570 TRACE("(%p)->(%s %lu)\n", This, debugstr_writer_prop(property), value);
571
572 switch (property)
573 {
574 case XmlWriterProperty_Indent:
575 This->indent = !!value;
576 break;
577 case XmlWriterProperty_ByteOrderMark:
578 This->bom = !!value;
579 break;
580 case XmlWriterProperty_OmitXmlDeclaration:
581 This->omitxmldecl = !!value;
582 break;
583 default:
584 FIXME("Unimplemented property (%u)\n", property);
585 return E_NOTIMPL;
586 }
587
588 return S_OK;
589 }
590
591 static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *pReader,
592 BOOL fWriteDefaultAttributes)
593 {
594 xmlwriter *This = impl_from_IXmlWriter(iface);
595
596 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
597
598 return E_NOTIMPL;
599 }
600
601 static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
602 LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
603 {
604 static const WCHAR eqW[] = {'=','"'};
605 xmlwriter *This = impl_from_IXmlWriter(iface);
606
607 TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
608 debugstr_w(ns_uri), debugstr_w(value));
609
610 switch (This->state)
611 {
612 case XmlWriterState_Initial:
613 return E_UNEXPECTED;
614 case XmlWriterState_Ready:
615 case XmlWriterState_DocClosed:
616 This->state = XmlWriterState_DocClosed;
617 return WR_E_INVALIDACTION;
618 default:
619 ;
620 }
621
622 if (ns_prefix || ns_uri)
623 {
624 FIXME("namespaces are not supported.\n");
625 return E_NOTIMPL;
626 }
627
628 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
629 write_output_buffer(This->output, local_name, -1);
630 write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
631 write_output_buffer(This->output, value, -1);
632 write_output_buffer(This->output, quoteW, ARRAY_SIZE(quoteW));
633
634 return S_OK;
635 }
636
637 static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
638 {
639 static const WCHAR cdataopenW[] = {'<','!','[','C','D','A','T','A','['};
640 static const WCHAR cdatacloseW[] = {']',']','>'};
641 write_output_buffer(output, cdataopenW, ARRAY_SIZE(cdataopenW));
642 if (data)
643 write_output_buffer(output, data, len);
644 write_output_buffer(output, cdatacloseW, ARRAY_SIZE(cdatacloseW));
645 }
646
647 static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
648 {
649 xmlwriter *This = impl_from_IXmlWriter(iface);
650 int len;
651
652 TRACE("%p %s\n", This, debugstr_w(data));
653
654 switch (This->state)
655 {
656 case XmlWriterState_Initial:
657 return E_UNEXPECTED;
658 case XmlWriterState_ElemStarted:
659 writer_close_starttag(This);
660 break;
661 case XmlWriterState_Ready:
662 case XmlWriterState_DocClosed:
663 This->state = XmlWriterState_DocClosed;
664 return WR_E_INVALIDACTION;
665 default:
666 ;
667 }
668
669 len = data ? strlenW(data) : 0;
670
671 write_node_indent(This);
672 if (!len)
673 write_cdata_section(This->output, NULL, 0);
674 else {
675 static const WCHAR cdatacloseW[] = {']',']','>',0};
676 while (len) {
677 const WCHAR *str = strstrW(data, cdatacloseW);
678 if (str) {
679 str += 2;
680 write_cdata_section(This->output, data, str - data);
681 len -= str - data;
682 data = str;
683 }
684 else {
685 write_cdata_section(This->output, data, len);
686 break;
687 }
688 }
689 }
690
691 return S_OK;
692 }
693
694 static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
695 {
696 static const WCHAR fmtW[] = {'&','#','x','%','x',';',0};
697 xmlwriter *This = impl_from_IXmlWriter(iface);
698 WCHAR bufW[16];
699
700 TRACE("%p %#x\n", This, ch);
701
702 switch (This->state)
703 {
704 case XmlWriterState_Initial:
705 return E_UNEXPECTED;
706 case XmlWriterState_ElemStarted:
707 writer_close_starttag(This);
708 break;
709 case XmlWriterState_DocClosed:
710 return WR_E_INVALIDACTION;
711 default:
712 ;
713 }
714
715 sprintfW(bufW, fmtW, ch);
716 write_output_buffer(This->output, bufW, -1);
717
718 return S_OK;
719 }
720
721 static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
722 {
723 xmlwriter *This = impl_from_IXmlWriter(iface);
724
725 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
726
727 switch (This->state)
728 {
729 case XmlWriterState_Initial:
730 return E_UNEXPECTED;
731 case XmlWriterState_DocClosed:
732 return WR_E_INVALIDACTION;
733 default:
734 ;
735 }
736
737 return E_NOTIMPL;
738 }
739
740
741 static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
742 {
743 static const WCHAR copenW[] = {'<','!','-','-'};
744 static const WCHAR ccloseW[] = {'-','-','>'};
745 xmlwriter *This = impl_from_IXmlWriter(iface);
746
747 TRACE("%p %s\n", This, debugstr_w(comment));
748
749 switch (This->state)
750 {
751 case XmlWriterState_Initial:
752 return E_UNEXPECTED;
753 case XmlWriterState_ElemStarted:
754 writer_close_starttag(This);
755 break;
756 case XmlWriterState_DocClosed:
757 return WR_E_INVALIDACTION;
758 default:
759 ;
760 }
761
762 write_node_indent(This);
763 write_output_buffer(This->output, copenW, ARRAY_SIZE(copenW));
764 if (comment) {
765 int len = strlenW(comment), i;
766
767 /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
768 comment string */
769 if (len > 1) {
770 for (i = 0; i < len; i++) {
771 write_output_buffer(This->output, comment + i, 1);
772 if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
773 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
774 }
775 }
776 else
777 write_output_buffer(This->output, comment, len);
778
779 if (len && comment[len-1] == '-')
780 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
781 }
782 write_output_buffer(This->output, ccloseW, ARRAY_SIZE(ccloseW));
783
784 return S_OK;
785 }
786
787 static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName, LPCWSTR pwszPublicId,
788 LPCWSTR pwszSystemId, LPCWSTR pwszSubset)
789 {
790 xmlwriter *This = impl_from_IXmlWriter(iface);
791
792 FIXME("%p %s %s %s %s\n", This, wine_dbgstr_w(pwszName), wine_dbgstr_w(pwszPublicId),
793 wine_dbgstr_w(pwszSystemId), wine_dbgstr_w(pwszSubset));
794
795 return E_NOTIMPL;
796 }
797
798 static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
799 LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
800 {
801 xmlwriter *This = impl_from_IXmlWriter(iface);
802
803 TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
804 wine_dbgstr_w(uri), wine_dbgstr_w(value));
805
806 switch (This->state)
807 {
808 case XmlWriterState_Initial:
809 return E_UNEXPECTED;
810 case XmlWriterState_ElemStarted:
811 writer_close_starttag(This);
812 break;
813 case XmlWriterState_Ready:
814 case XmlWriterState_DocStarted:
815 case XmlWriterState_PIDocStarted:
816 break;
817 default:
818 This->state = XmlWriterState_DocClosed;
819 return WR_E_INVALIDACTION;
820 }
821
822 write_encoding_bom(This);
823 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
824 write_output_qname(This->output, prefix, local_name);
825 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
826
827 if (value)
828 write_output_buffer(This->output, value, -1);
829
830 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
831 write_output_qname(This->output, prefix, local_name);
832 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
833 This->state = XmlWriterState_Content;
834
835 return S_OK;
836 }
837
838 static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
839 {
840 xmlwriter *This = impl_from_IXmlWriter(iface);
841
842 TRACE("%p\n", This);
843
844 switch (This->state)
845 {
846 case XmlWriterState_Initial:
847 return E_UNEXPECTED;
848 case XmlWriterState_Ready:
849 case XmlWriterState_DocClosed:
850 This->state = XmlWriterState_DocClosed;
851 return WR_E_INVALIDACTION;
852 default:
853 ;
854 }
855
856 /* empty element stack */
857 while (IXmlWriter_WriteEndElement(iface) == S_OK)
858 ;
859
860 This->state = XmlWriterState_DocClosed;
861 return S_OK;
862 }
863
864 static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
865 {
866 xmlwriter *This = impl_from_IXmlWriter(iface);
867 struct element *element;
868
869 TRACE("%p\n", This);
870
871 switch (This->state)
872 {
873 case XmlWriterState_Initial:
874 return E_UNEXPECTED;
875 case XmlWriterState_Ready:
876 case XmlWriterState_DocClosed:
877 This->state = XmlWriterState_DocClosed;
878 return WR_E_INVALIDACTION;
879 default:
880 ;
881 }
882
883 element = pop_element(This);
884 if (!element)
885 return WR_E_INVALIDACTION;
886
887 writer_dec_indent(This);
888
889 if (This->starttagopen) {
890 static WCHAR closetagW[] = {' ','/','>'};
891 write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
892 This->starttagopen = FALSE;
893 }
894 else {
895 /* write full end tag */
896 write_node_indent(This);
897 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
898 write_output_buffer(This->output, element->qname, element->len);
899 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
900 }
901
902 return S_OK;
903 }
904
905 static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
906 {
907 xmlwriter *This = impl_from_IXmlWriter(iface);
908
909 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
910
911 switch (This->state)
912 {
913 case XmlWriterState_Initial:
914 return E_UNEXPECTED;
915 case XmlWriterState_DocClosed:
916 return WR_E_INVALIDACTION;
917 default:
918 ;
919 }
920
921 return E_NOTIMPL;
922 }
923
924 static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
925 {
926 xmlwriter *This = impl_from_IXmlWriter(iface);
927 struct element *element;
928
929 TRACE("%p\n", This);
930
931 switch (This->state)
932 {
933 case XmlWriterState_Initial:
934 return E_UNEXPECTED;
935 case XmlWriterState_Ready:
936 case XmlWriterState_DocClosed:
937 This->state = XmlWriterState_DocClosed;
938 return WR_E_INVALIDACTION;
939 default:
940 ;
941 }
942
943 element = pop_element(This);
944 if (!element)
945 return WR_E_INVALIDACTION;
946
947 writer_close_starttag(This);
948 writer_dec_indent(This);
949
950 /* don't force full end tag to the next line */
951 if (This->state == XmlWriterState_ElemStarted)
952 This->state = XmlWriterState_Content;
953 else
954 write_node_indent(This);
955
956 /* write full end tag */
957 write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
958 write_output_buffer(This->output, element->qname, element->len);
959 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
960
961 return S_OK;
962 }
963
964 static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
965 {
966 xmlwriter *This = impl_from_IXmlWriter(iface);
967
968 FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));
969
970 switch (This->state)
971 {
972 case XmlWriterState_Initial:
973 return E_UNEXPECTED;
974 case XmlWriterState_Ready:
975 case XmlWriterState_DocClosed:
976 This->state = XmlWriterState_DocClosed;
977 return WR_E_INVALIDACTION;
978 default:
979 ;
980 }
981
982 return E_NOTIMPL;
983 }
984
985 static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
986 {
987 xmlwriter *This = impl_from_IXmlWriter(iface);
988
989 FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));
990
991 switch (This->state)
992 {
993 case XmlWriterState_Initial:
994 return E_UNEXPECTED;
995 case XmlWriterState_Ready:
996 case XmlWriterState_DocClosed:
997 This->state = XmlWriterState_DocClosed;
998 return WR_E_INVALIDACTION;
999 default:
1000 ;
1001 }
1002
1003 return E_NOTIMPL;
1004 }
1005
1006 static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *pReader,
1007 BOOL fWriteDefaultAttributes)
1008 {
1009 xmlwriter *This = impl_from_IXmlWriter(iface);
1010
1011 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1012
1013 return E_NOTIMPL;
1014 }
1015
1016 static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *pReader,
1017 BOOL fWriteDefaultAttributes)
1018 {
1019 xmlwriter *This = impl_from_IXmlWriter(iface);
1020
1021 FIXME("%p %p %d\n", This, pReader, fWriteDefaultAttributes);
1022
1023 return E_NOTIMPL;
1024 }
1025
1026 static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
1027 LPCWSTR text)
1028 {
1029 xmlwriter *This = impl_from_IXmlWriter(iface);
1030 static const WCHAR xmlW[] = {'x','m','l',0};
1031 static const WCHAR openpiW[] = {'<','?'};
1032
1033 TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1034
1035 switch (This->state)
1036 {
1037 case XmlWriterState_Initial:
1038 return E_UNEXPECTED;
1039 case XmlWriterState_DocStarted:
1040 if (!strcmpW(name, xmlW))
1041 return WR_E_INVALIDACTION;
1042 break;
1043 case XmlWriterState_ElemStarted:
1044 case XmlWriterState_DocClosed:
1045 return WR_E_INVALIDACTION;
1046 default:
1047 ;
1048 }
1049
1050 write_encoding_bom(This);
1051 write_node_indent(This);
1052 write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
1053 write_output_buffer(This->output, name, -1);
1054 write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
1055 write_output_buffer(This->output, text, -1);
1056 write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
1057
1058 if (!strcmpW(name, xmlW))
1059 This->state = XmlWriterState_PIDocStarted;
1060
1061 return S_OK;
1062 }
1063
1064 static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
1065 LPCWSTR pwszNamespaceUri)
1066 {
1067 xmlwriter *This = impl_from_IXmlWriter(iface);
1068
1069 FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));
1070
1071 switch (This->state)
1072 {
1073 case XmlWriterState_Initial:
1074 return E_UNEXPECTED;
1075 case XmlWriterState_DocClosed:
1076 return WR_E_INVALIDACTION;
1077 default:
1078 ;
1079 }
1080
1081 return E_NOTIMPL;
1082 }
1083
1084 static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1085 {
1086 xmlwriter *This = impl_from_IXmlWriter(iface);
1087
1088 TRACE("%p %s\n", This, debugstr_w(data));
1089
1090 if (!data)
1091 return S_OK;
1092
1093 switch (This->state)
1094 {
1095 case XmlWriterState_Initial:
1096 return E_UNEXPECTED;
1097 case XmlWriterState_Ready:
1098 write_xmldecl(This, XmlStandalone_Omit);
1099 /* fallthrough */
1100 case XmlWriterState_DocStarted:
1101 case XmlWriterState_PIDocStarted:
1102 break;
1103 default:
1104 This->state = XmlWriterState_DocClosed;
1105 return WR_E_INVALIDACTION;
1106 }
1107
1108 write_output_buffer(This->output, data, -1);
1109 return S_OK;
1110 }
1111
1112 static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface, const WCHAR *pwch, UINT cwch)
1113 {
1114 xmlwriter *This = impl_from_IXmlWriter(iface);
1115
1116 FIXME("%p %s %d\n", This, wine_dbgstr_w(pwch), cwch);
1117
1118 switch (This->state)
1119 {
1120 case XmlWriterState_Initial:
1121 return E_UNEXPECTED;
1122 case XmlWriterState_DocClosed:
1123 return WR_E_INVALIDACTION;
1124 default:
1125 ;
1126 }
1127
1128 return E_NOTIMPL;
1129 }
1130
1131 static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
1132 {
1133 xmlwriter *This = impl_from_IXmlWriter(iface);
1134
1135 TRACE("(%p)->(%d)\n", This, standalone);
1136
1137 switch (This->state)
1138 {
1139 case XmlWriterState_Initial:
1140 return E_UNEXPECTED;
1141 case XmlWriterState_PIDocStarted:
1142 This->state = XmlWriterState_DocStarted;
1143 return S_OK;
1144 case XmlWriterState_Ready:
1145 break;
1146 default:
1147 This->state = XmlWriterState_DocClosed;
1148 return WR_E_INVALIDACTION;
1149 }
1150
1151 return write_xmldecl(This, standalone);
1152 }
1153
1154 static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1155 {
1156 xmlwriter *This = impl_from_IXmlWriter(iface);
1157 struct element *element;
1158
1159 TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1160
1161 if (!local_name)
1162 return E_INVALIDARG;
1163
1164 switch (This->state)
1165 {
1166 case XmlWriterState_Initial:
1167 return E_UNEXPECTED;
1168 case XmlWriterState_DocClosed:
1169 return WR_E_INVALIDACTION;
1170 default:
1171 ;
1172 }
1173
1174 /* close pending element */
1175 if (This->starttagopen)
1176 write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
1177
1178 element = alloc_element(This, prefix, local_name);
1179 if (!element)
1180 return E_OUTOFMEMORY;
1181
1182 write_encoding_bom(This);
1183 This->state = XmlWriterState_ElemStarted;
1184 This->starttagopen = TRUE;
1185
1186 push_element(This, element);
1187
1188 write_node_indent(This);
1189 write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
1190 write_output_qname(This->output, prefix, local_name);
1191 writer_inc_indent(This);
1192
1193 return S_OK;
1194 }
1195
1196 static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, LPCWSTR pwszText)
1197 {
1198 xmlwriter *This = impl_from_IXmlWriter(iface);
1199
1200 FIXME("%p %s\n", This, wine_dbgstr_w(pwszText));
1201
1202 switch (This->state)
1203 {
1204 case XmlWriterState_Initial:
1205 return E_UNEXPECTED;
1206 case XmlWriterState_Ready:
1207 case XmlWriterState_DocClosed:
1208 This->state = XmlWriterState_DocClosed;
1209 return WR_E_INVALIDACTION;
1210 default:
1211 ;
1212 }
1213
1214 return E_NOTIMPL;
1215 }
1216
1217 static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
1218 {
1219 xmlwriter *This = impl_from_IXmlWriter(iface);
1220
1221 FIXME("%p %d %d\n", This, wchLow, wchHigh);
1222
1223 return E_NOTIMPL;
1224 }
1225
1226 static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR pwszWhitespace)
1227 {
1228 xmlwriter *This = impl_from_IXmlWriter(iface);
1229
1230 FIXME("%p %s\n", This, wine_dbgstr_w(pwszWhitespace));
1231
1232 return E_NOTIMPL;
1233 }
1234
1235 static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
1236 {
1237 xmlwriter *This = impl_from_IXmlWriter(iface);
1238
1239 TRACE("%p\n", This);
1240
1241 return writeroutput_flush_stream(This->output);
1242 }
1243
1244 static const struct IXmlWriterVtbl xmlwriter_vtbl =
1245 {
1246 xmlwriter_QueryInterface,
1247 xmlwriter_AddRef,
1248 xmlwriter_Release,
1249 xmlwriter_SetOutput,
1250 xmlwriter_GetProperty,
1251 xmlwriter_SetProperty,
1252 xmlwriter_WriteAttributes,
1253 xmlwriter_WriteAttributeString,
1254 xmlwriter_WriteCData,
1255 xmlwriter_WriteCharEntity,
1256 xmlwriter_WriteChars,
1257 xmlwriter_WriteComment,
1258 xmlwriter_WriteDocType,
1259 xmlwriter_WriteElementString,
1260 xmlwriter_WriteEndDocument,
1261 xmlwriter_WriteEndElement,
1262 xmlwriter_WriteEntityRef,
1263 xmlwriter_WriteFullEndElement,
1264 xmlwriter_WriteName,
1265 xmlwriter_WriteNmToken,
1266 xmlwriter_WriteNode,
1267 xmlwriter_WriteNodeShallow,
1268 xmlwriter_WriteProcessingInstruction,
1269 xmlwriter_WriteQualifiedName,
1270 xmlwriter_WriteRaw,
1271 xmlwriter_WriteRawChars,
1272 xmlwriter_WriteStartDocument,
1273 xmlwriter_WriteStartElement,
1274 xmlwriter_WriteString,
1275 xmlwriter_WriteSurrogateCharEntity,
1276 xmlwriter_WriteWhitespace,
1277 xmlwriter_Flush
1278 };
1279
1280 /** IXmlWriterOutput **/
1281 static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
1282 {
1283 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1284
1285 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1286
1287 if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
1288 IsEqualGUID(riid, &IID_IUnknown))
1289 {
1290 *ppvObject = iface;
1291 }
1292 else
1293 {
1294 WARN("interface %s not implemented\n", debugstr_guid(riid));
1295 *ppvObject = NULL;
1296 return E_NOINTERFACE;
1297 }
1298
1299 IUnknown_AddRef(iface);
1300
1301 return S_OK;
1302 }
1303
1304 static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
1305 {
1306 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1307 ULONG ref = InterlockedIncrement(&This->ref);
1308 TRACE("(%p)->(%d)\n", This, ref);
1309 return ref;
1310 }
1311
1312 static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
1313 {
1314 xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
1315 LONG ref = InterlockedDecrement(&This->ref);
1316
1317 TRACE("(%p)->(%d)\n", This, ref);
1318
1319 if (ref == 0)
1320 {
1321 IMalloc *imalloc = This->imalloc;
1322 if (This->output) IUnknown_Release(This->output);
1323 if (This->stream) ISequentialStream_Release(This->stream);
1324 free_output_buffer(This);
1325 writeroutput_free(This, This);
1326 if (imalloc) IMalloc_Release(imalloc);
1327 }
1328
1329 return ref;
1330 }
1331
1332 static const struct IUnknownVtbl xmlwriteroutputvtbl =
1333 {
1334 xmlwriteroutput_QueryInterface,
1335 xmlwriteroutput_AddRef,
1336 xmlwriteroutput_Release
1337 };
1338
1339 HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
1340 {
1341 xmlwriter *writer;
1342
1343 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
1344
1345 if (!IsEqualGUID(riid, &IID_IXmlWriter))
1346 {
1347 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid));
1348 return E_FAIL;
1349 }
1350
1351 if (imalloc)
1352 writer = IMalloc_Alloc(imalloc, sizeof(*writer));
1353 else
1354 writer = heap_alloc(sizeof(*writer));
1355 if(!writer) return E_OUTOFMEMORY;
1356
1357 writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
1358 writer->ref = 1;
1359 writer->imalloc = imalloc;
1360 if (imalloc) IMalloc_AddRef(imalloc);
1361 writer->output = NULL;
1362 writer->indent_level = 0;
1363 writer->indent = FALSE;
1364 writer->bom = TRUE;
1365 writer->omitxmldecl = FALSE;
1366 writer->conformance = XmlConformanceLevel_Document;
1367 writer->state = XmlWriterState_Initial;
1368 writer->bomwritten = FALSE;
1369 writer->starttagopen = FALSE;
1370 list_init(&writer->elements);
1371
1372 *obj = &writer->IXmlWriter_iface;
1373
1374 TRACE("returning iface %p\n", *obj);
1375
1376 return S_OK;
1377 }
1378
1379 static HRESULT create_writer(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
1380 IXmlWriterOutput **output)
1381 {
1382 xmlwriteroutput *writeroutput;
1383 HRESULT hr;
1384
1385 *output = NULL;
1386
1387 if (imalloc)
1388 writeroutput = IMalloc_Alloc(imalloc, sizeof(*writeroutput));
1389 else
1390 writeroutput = heap_alloc(sizeof(*writeroutput));
1391 if(!writeroutput) return E_OUTOFMEMORY;
1392
1393 writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
1394 writeroutput->ref = 1;
1395 writeroutput->imalloc = imalloc;
1396 if (imalloc) IMalloc_AddRef(imalloc);
1397 writeroutput->encoding = encoding;
1398 writeroutput->stream = NULL;
1399 hr = init_output_buffer(writeroutput);
1400 if (FAILED(hr)) {
1401 IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
1402 return hr;
1403 }
1404
1405 IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);
1406
1407 *output = &writeroutput->IXmlWriterOutput_iface;
1408
1409 TRACE("returning iface %p\n", *output);
1410
1411 return S_OK;
1412 }
1413
1414 HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream,
1415 IMalloc *imalloc,
1416 LPCWSTR encoding,
1417 IXmlWriterOutput **output)
1418 {
1419 static const WCHAR utf8W[] = {'U','T','F','-','8',0};
1420 xml_encoding xml_enc;
1421
1422 TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), output);
1423
1424 if (!stream || !output) return E_INVALIDARG;
1425
1426 xml_enc = parse_encoding_name(encoding ? encoding : utf8W, -1);
1427 return create_writer(stream, imalloc, xml_enc, output);
1428 }
1429
1430 HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream,
1431 IMalloc *imalloc,
1432 UINT codepage,
1433 IXmlWriterOutput **output)
1434 {
1435 xml_encoding xml_enc;
1436
1437 TRACE("%p %p %u %p\n", stream, imalloc, codepage, output);
1438
1439 if (!stream || !output) return E_INVALIDARG;
1440
1441 xml_enc = get_encoding_from_codepage(codepage);
1442 return create_writer(stream, imalloc, xml_enc, output);
1443 }