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