writer.c 61.5 KB
Newer Older
1 2 3 4
/*
 * IXmlWriter implementation
 *
 * Copyright 2011 Alistair Leslie-Hughes
5
 * Copyright 2014-2018 Nikolay Sivov for CodeWeavers
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#define COBJMACROS

23
#include <assert.h>
24
#include <stdarg.h>
25

26 27 28 29
#include "windef.h"
#include "winbase.h"
#include "objbase.h"
#include "xmllite.h"
30 31
#include "xmllite_private.h"
#include "initguid.h"
32 33

#include "wine/debug.h"
34
#include "wine/list.h"
35 36 37

WINE_DEFAULT_DEBUG_CHANNEL(xmllite);

38 39 40
/* not defined in public headers */
DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);

41
static const WCHAR xmlnsuriW[] = L"http://www.w3.org/2000/xmlns/";
42

43 44 45 46 47 48 49 50
struct output_buffer
{
    char *data;
    unsigned int allocated;
    unsigned int written;
    UINT codepage;
};

51 52
typedef enum
{
53 54 55 56 57 58 59 60
    XmlWriterState_Initial,         /* output is not set yet */
    XmlWriterState_Ready,           /* SetOutput() was called, ready to start */
    XmlWriterState_InvalidEncoding, /* SetOutput() was called, but output had invalid encoding */
    XmlWriterState_PIDocStarted,    /* document was started with manually added 'xml' PI */
    XmlWriterState_DocStarted,      /* document was started with WriteStartDocument() */
    XmlWriterState_ElemStarted,     /* writing element */
    XmlWriterState_Content,         /* content is accepted at this point */
    XmlWriterState_DocClosed        /* WriteEndDocument was called */
61 62
} XmlWriterState;

63 64 65 66 67
typedef struct
{
    IXmlWriterOutput IXmlWriterOutput_iface;
    LONG ref;
    IUnknown *output;
68
    ISequentialStream *stream;
69 70
    IMalloc *imalloc;
    xml_encoding encoding;
71
    WCHAR *encoding_name; /* exactly as specified on output creation */
72
    struct output_buffer buffer;
73
    DWORD written : 1;
74 75
} xmlwriteroutput;

76 77
static const struct IUnknownVtbl xmlwriteroutputvtbl;

78 79 80 81 82
struct element
{
    struct list entry;
    WCHAR *qname;
    unsigned int len; /* qname length in chars */
83
    struct list ns;
84 85
};

86 87 88 89 90 91 92 93 94 95
struct ns
{
    struct list entry;
    WCHAR *prefix;
    int prefix_len;
    WCHAR *uri;
    BOOL emitted;
    struct element *element;
};

96 97 98 99
typedef struct _xmlwriter
{
    IXmlWriter IXmlWriter_iface;
    LONG ref;
100
    IMalloc *imalloc;
101
    xmlwriteroutput *output;
102
    unsigned int indent_level;
103 104 105 106
    BOOL indent;
    BOOL bom;
    BOOL omitxmldecl;
    XmlConformanceLevel conformance;
107
    XmlWriterState state;
108
    struct list elements;
109 110
    DWORD bomwritten : 1;
    DWORD starttagopen : 1;
111
    DWORD textnode : 1;
112 113 114 115 116 117 118
} xmlwriter;

static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
{
    return CONTAINING_RECORD(iface, xmlwriter, IXmlWriter_iface);
}

119 120 121 122 123
static inline xmlwriteroutput *impl_from_IXmlWriterOutput(IXmlWriterOutput *iface)
{
    return CONTAINING_RECORD(iface, xmlwriteroutput, IXmlWriterOutput_iface);
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
static const char *debugstr_writer_prop(XmlWriterProperty prop)
{
    static const char * const prop_names[] =
    {
        "MultiLanguage",
        "Indent",
        "ByteOrderMark",
        "OmitXmlDeclaration",
        "ConformanceLevel"
    };

    if (prop > _XmlWriterProperty_Last)
        return wine_dbg_sprintf("unknown property=%d", prop);

    return prop_names[prop];
}

141 142 143
static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
    const WCHAR *encoding_name, xmlwriteroutput **out);

144
/* writer output memory allocation functions */
145 146 147 148 149 150 151 152 153 154
static inline void *writeroutput_alloc(xmlwriteroutput *output, size_t len)
{
    return m_alloc(output->imalloc, len);
}

static inline void writeroutput_free(xmlwriteroutput *output, void *mem)
{
    m_free(output->imalloc, mem);
}

155 156 157 158 159
static inline void *writeroutput_realloc(xmlwriteroutput *output, void *mem, size_t len)
{
    return m_realloc(output->imalloc, mem, len);
}

160
/* writer memory allocation functions */
161
static inline void *writer_alloc(const xmlwriter *writer, size_t len)
162 163 164 165
{
    return m_alloc(writer->imalloc, len);
}

166
static inline void writer_free(const xmlwriter *writer, void *mem)
167 168 169 170
{
    m_free(writer->imalloc, mem);
}

171 172 173 174 175
static BOOL is_empty_string(const WCHAR *str)
{
    return !str || !*str;
}

176 177 178 179 180 181 182 183
static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
{
    struct element *ret;
    int len;

    ret = writer_alloc(writer, sizeof(*ret));
    if (!ret) return ret;

184
    len = is_empty_string(prefix) ? 0 : lstrlenW(prefix) + 1 /* ':' */;
185
    len += lstrlenW(local);
186 187

    ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
188 189 190 191 192 193 194

    if (!ret->qname)
    {
        writer_free(writer, ret);
        return NULL;
    }

195
    ret->len = len;
196 197 198
    if (is_empty_string(prefix))
        ret->qname[0] = 0;
    else
199
    {
200
        lstrcpyW(ret->qname, prefix);
201
        lstrcatW(ret->qname, L":");
202
    }
203
    lstrcatW(ret->qname, local);
204
    list_init(&ret->ns);
205 206 207 208

    return ret;
}

209
static void writer_free_element(xmlwriter *writer, struct element *element)
210
{
211 212 213 214 215 216 217 218 219 220
    struct ns *ns, *ns2;

    LIST_FOR_EACH_ENTRY_SAFE(ns, ns2, &element->ns, struct ns, entry)
    {
        list_remove(&ns->entry);
        writer_free(writer, ns->prefix);
        writer_free(writer, ns->uri);
        writer_free(writer, ns);
    }

221 222 223 224
    writer_free(writer, element->qname);
    writer_free(writer, element);
}

225 226 227 228 229 230 231 232 233 234 235 236
static void writer_free_element_stack(xmlwriter *writer)
{
    struct element *element, *element2;

    LIST_FOR_EACH_ENTRY_SAFE(element, element2, &writer->elements, struct element, entry)
    {
        list_remove(&element->entry);
        writer_free_element(writer, element);
    }
}

static void writer_push_element(xmlwriter *writer, struct element *element)
237 238 239 240 241 242 243 244 245 246 247 248 249 250
{
    list_add_head(&writer->elements, &element->entry);
}

static struct element *pop_element(xmlwriter *writer)
{
    struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);

    if (element)
        list_remove(&element->entry);

    return element;
}

251 252 253 254 255 256 257 258
static WCHAR *writer_strndupW(const xmlwriter *writer, const WCHAR *str, int len)
{
    WCHAR *ret;

    if (!str)
        return NULL;

    if (len == -1)
259
        len = lstrlenW(str);
260

261 262 263 264 265 266
    ret = writer_alloc(writer, (len + 1) * sizeof(WCHAR));
    if (ret)
    {
        memcpy(ret, str, len * sizeof(WCHAR));
        ret[len] = 0;
    }
267

268 269 270 271 272 273 274 275
    return ret;
}

static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
{
    return writer_strndupW(writer, str, -1);
}

276
static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
277 278 279 280 281 282
{
    struct element *element;
    struct ns *ns;

    element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
    if (!element)
283
        return NULL;
284 285 286 287 288 289

    if ((ns = writer_alloc(writer, sizeof(*ns))))
    {
        ns->prefix = writer_strndupW(writer, prefix, prefix_len);
        ns->prefix_len = prefix_len;
        ns->uri = writer_strdupW(writer, uri);
290 291
        ns->emitted = FALSE;
        ns->element = element;
292 293
        list_add_tail(&element->ns, &ns->entry);
    }
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

    return ns;
}

static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
{
    struct element *element;
    struct ns *ns;

    if (is_empty_string(prefix) || is_empty_string(uri))
        return NULL;

    element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);

    LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
    {
310
        if (!wcscmp(uri, ns->uri) && !wcscmp(prefix, ns->prefix))
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            return ns;
    }

    return NULL;
}

static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
{
    struct element *element;
    struct ns *ns;

    if (is_empty_string(prefix) && is_empty_string(uri))
        return NULL;

    LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
    {
        LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
        {
            if (!uri)
            {
                if (!ns->prefix) continue;
332
                if (!wcscmp(ns->prefix, prefix))
333 334
                    return ns;
            }
335
            else if (!wcscmp(uri, ns->uri))
336 337 338
            {
                if (prefix && !*prefix)
                    return NULL;
339
                if (!prefix || !wcscmp(prefix, ns->prefix))
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
                    return ns;
            }
        }
    }

    return NULL;
}

static HRESULT is_valid_ncname(const WCHAR *str, int *out)
{
    int len = 0;

    *out = 0;

    if (!str || !*str)
        return S_OK;

    while (*str)
    {
        if (!is_ncnamechar(*str))
            return WC_E_NAMECHARACTER;
        len++;
        str++;
    }

    *out = len;
    return S_OK;
367 368
}

369 370 371 372 373 374 375 376 377 378 379 380
static HRESULT is_valid_name(const WCHAR *str, unsigned int *out)
{
    unsigned int len = 1;

    *out = 0;

    if (!str || !*str)
        return S_OK;

    if (!is_namestartchar(*str++))
        return WC_E_NAMECHARACTER;

381
    while (*str)
382 383 384 385
    {
        if (!is_namechar(*str))
            return WC_E_NAMECHARACTER;
        len++;
386
        str++;
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    }

    *out = len;
    return S_OK;
}

static HRESULT is_valid_pubid(const WCHAR *str, unsigned int *out)
{
    unsigned int len = 0;

    *out = 0;

    if (!str || !*str)
        return S_OK;

    while (*str)
    {
        if (!is_pubchar(*str++))
            return WC_E_PUBLICID;
        len++;
    }

    *out = len;

    return S_OK;
}

414 415 416 417
static HRESULT init_output_buffer(xmlwriteroutput *output)
{
    struct output_buffer *buffer = &output->buffer;
    const int initial_len = 0x2000;
418
    UINT cp = ~0u;
419 420
    HRESULT hr;

421 422
    if (FAILED(hr = get_code_page(output->encoding, &cp)))
        WARN("Failed to get code page for specified encoding.\n");
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

    buffer->data = writeroutput_alloc(output, initial_len);
    if (!buffer->data) return E_OUTOFMEMORY;

    memset(buffer->data, 0, 4);
    buffer->allocated = initial_len;
    buffer->written = 0;
    buffer->codepage = cp;

    return S_OK;
}

static void free_output_buffer(xmlwriteroutput *output)
{
    struct output_buffer *buffer = &output->buffer;
    writeroutput_free(output, buffer->data);
    buffer->data = NULL;
    buffer->allocated = 0;
    buffer->written = 0;
}

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
static HRESULT grow_output_buffer(xmlwriteroutput *output, int length)
{
    struct output_buffer *buffer = &output->buffer;
    /* grow if needed, plus 4 bytes to be sure null terminator will fit in */
    if (buffer->allocated < buffer->written + length + 4) {
        int grown_size = max(2*buffer->allocated, buffer->allocated + length);
        char *ptr = writeroutput_realloc(output, buffer->data, grown_size);
        if (!ptr) return E_OUTOFMEMORY;
        buffer->data = ptr;
        buffer->allocated = grown_size;
    }

    return S_OK;
}

static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, int len)
{
    struct output_buffer *buffer = &output->buffer;
    int length;
    HRESULT hr;
    char *ptr;

466 467
    if (buffer->codepage == 1200) {
        /* For UTF-16 encoding just copy. */
468
        length = len == -1 ? lstrlenW(data) : len;
469 470 471 472 473 474 475 476 477 478 479 480 481 482
        if (length) {
            length *= sizeof(WCHAR);

            hr = grow_output_buffer(output, length);
            if (FAILED(hr)) return hr;
            ptr = buffer->data + buffer->written;

            memcpy(ptr, data, length);
            buffer->written += length;
            ptr += length;
            /* null termination */
            *(WCHAR*)ptr = 0;
        }
    }
483 484 485 486 487 488 489 490
    else {
        length = WideCharToMultiByte(buffer->codepage, 0, data, len, NULL, 0, NULL, NULL);
        hr = grow_output_buffer(output, length);
        if (FAILED(hr)) return hr;
        ptr = buffer->data + buffer->written;
        length = WideCharToMultiByte(buffer->codepage, 0, data, len, ptr, length, NULL, NULL);
        buffer->written += len == -1 ? length-1 : length;
    }
491
    output->written = length != 0;
492 493 494 495

    return S_OK;
}

496 497 498 499 500
static HRESULT write_output_buffer_char(xmlwriteroutput *output, WCHAR ch)
{
    return write_output_buffer(output, &ch, 1);
}

501 502
static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
{
503
    write_output_buffer_char(output, '"');
504 505
    if (!is_empty_string(data))
        write_output_buffer(output, data, len);
506
    write_output_buffer_char(output, '"');
507 508 509
    return S_OK;
}

510
/* TODO: test if we need to validate char range */
511 512
static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix, int prefix_len,
        const WCHAR *local_name, int local_len)
513
{
514 515
    assert(prefix_len >= 0 && local_len >= 0);

516
    if (prefix_len)
517
        write_output_buffer(output, prefix, prefix_len);
518 519

    if (prefix_len && local_len)
520
        write_output_buffer_char(output, ':');
521

522
    write_output_buffer(output, local_name, local_len);
523 524 525 526

    return S_OK;
}

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
static void writeroutput_release_stream(xmlwriteroutput *writeroutput)
{
    if (writeroutput->stream) {
        ISequentialStream_Release(writeroutput->stream);
        writeroutput->stream = NULL;
    }
}

static inline HRESULT writeroutput_query_for_stream(xmlwriteroutput *writeroutput)
{
    HRESULT hr;

    writeroutput_release_stream(writeroutput);
    hr = IUnknown_QueryInterface(writeroutput->output, &IID_IStream, (void**)&writeroutput->stream);
    if (hr != S_OK)
        hr = IUnknown_QueryInterface(writeroutput->output, &IID_ISequentialStream, (void**)&writeroutput->stream);

    return hr;
}

547 548 549
static HRESULT writeroutput_flush_stream(xmlwriteroutput *output)
{
    struct output_buffer *buffer;
550
    ULONG written, offset = 0;
551 552 553 554 555 556 557
    HRESULT hr;

    if (!output || !output->stream)
        return S_OK;

    buffer = &output->buffer;

558
    /* It will loop forever until everything is written or an error occurred. */
559 560 561 562
    do {
        written = 0;
        hr = ISequentialStream_Write(output->stream, buffer->data + offset, buffer->written, &written);
        if (FAILED(hr)) {
563
            WARN("write to stream failed %#lx.\n", hr);
564 565 566
            buffer->written = 0;
            return hr;
        }
567

568 569 570
        offset += written;
        buffer->written -= written;
    } while (buffer->written > 0);
571 572 573 574

    return S_OK;
}

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
static HRESULT write_encoding_bom(xmlwriter *writer)
{
    if (!writer->bom || writer->bomwritten) return S_OK;

    if (writer->output->encoding == XmlEncoding_UTF16) {
        static const char utf16bom[] = {0xff, 0xfe};
        struct output_buffer *buffer = &writer->output->buffer;
        int len = sizeof(utf16bom);
        HRESULT hr;

        hr = grow_output_buffer(writer->output, len);
        if (FAILED(hr)) return hr;
        memcpy(buffer->data + buffer->written, utf16bom, len);
        buffer->written += len;
    }

    writer->bomwritten = TRUE;
    return S_OK;
}

595 596 597 598 599 600 601 602
static const WCHAR *get_output_encoding_name(xmlwriteroutput *output)
{
    if (output->encoding_name)
        return output->encoding_name;

    return get_encoding_name(output->encoding);
}

603 604 605 606 607 608 609
static HRESULT write_xmldecl(xmlwriter *writer, XmlStandalone standalone)
{
    write_encoding_bom(writer);
    writer->state = XmlWriterState_DocStarted;
    if (writer->omitxmldecl) return S_OK;

    /* version */
610
    write_output_buffer(writer->output, L"<?xml version=\"1.0\"", 19);
611 612

    /* encoding */
613
    write_output_buffer(writer->output, L" encoding=", 10);
614
    write_output_buffer_quoted(writer->output, get_output_encoding_name(writer->output), -1);
615 616 617

    /* standalone */
    if (standalone == XmlStandalone_Omit)
618 619 620 621
        write_output_buffer(writer->output, L"?>", 2);
    else
    {
        write_output_buffer(writer->output, L" standalone=\"", 13);
622
        if (standalone == XmlStandalone_Yes)
623
            write_output_buffer(writer->output, L"yes\"?>", 6);
624
        else
625
            write_output_buffer(writer->output, L"no\"?>", 5);
626 627 628 629 630
    }

    return S_OK;
}

631
static void writer_output_ns(xmlwriter *writer, struct element *element)
632
{
633 634 635 636
    struct ns *ns;

    LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
    {
637 638 639
        if (ns->emitted)
            continue;

640
        write_output_qname(writer->output, L" xmlns", 6, ns->prefix, ns->prefix_len);
641
        write_output_buffer_char(writer->output, '=');
642 643
        write_output_buffer_quoted(writer->output, ns->uri, -1);
    }
644
}
645

646 647 648 649 650 651 652
static HRESULT writer_close_starttag(xmlwriter *writer)
{
    HRESULT hr;

    if (!writer->starttagopen) return S_OK;

    writer_output_ns(writer, LIST_ENTRY(list_head(&writer->elements), struct element, entry));
653
    hr = write_output_buffer_char(writer->output, '>');
654
    writer->starttagopen = 0;
655 656 657
    return hr;
}

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
static void writer_inc_indent(xmlwriter *writer)
{
    writer->indent_level++;
}

static void writer_dec_indent(xmlwriter *writer)
{
    if (writer->indent_level)
        writer->indent_level--;
}

static void write_node_indent(xmlwriter *writer)
{
    unsigned int indent_level = writer->indent_level;

673 674 675
    if (!writer->indent || writer->textnode)
    {
        writer->textnode = 0;
676
        return;
677
    }
678

679 680
    /* Do state check to prevent newline inserted after BOM. It is assumed that
       state does not change between writing BOM and inserting indentation. */
681
    if (writer->output->written && writer->state != XmlWriterState_Ready)
682
        write_output_buffer(writer->output, L"\r\n", 2);
683
    while (indent_level--)
684
        write_output_buffer(writer->output, L"  ", 2);
685 686

    writer->textnode = 0;
687 688
}

689 690 691 692
static HRESULT WINAPI xmlwriter_QueryInterface(IXmlWriter *iface, REFIID riid, void **ppvObject)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

693
    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
694

695 696
    if (IsEqualGUID(riid, &IID_IXmlWriter) ||
        IsEqualGUID(riid, &IID_IUnknown))
697 698 699
    {
        *ppvObject = iface;
    }
700 701 702 703 704 705
    else
    {
        FIXME("interface %s is not supported\n", debugstr_guid(riid));
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
706 707 708 709 710 711 712 713

    IXmlWriter_AddRef(iface);

    return S_OK;
}

static ULONG WINAPI xmlwriter_AddRef(IXmlWriter *iface)
{
714 715 716
    xmlwriter *writer = impl_from_IXmlWriter(iface);
    ULONG ref = InterlockedIncrement(&writer->ref);
    TRACE("%p, refcount %lu.\n", iface, ref);
717
    return ref;
718 719 720 721
}

static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
{
722 723
    xmlwriter *writer = impl_from_IXmlWriter(iface);
    ULONG ref = InterlockedDecrement(&writer->ref);
724

725
    TRACE("%p, refcount %lu.\n", iface, ref);
726

727 728 729
    if (!ref)
    {
        IMalloc *imalloc = writer->imalloc;
730

731 732 733
        writeroutput_flush_stream(writer->output);
        if (writer->output)
            IUnknown_Release(&writer->output->IXmlWriterOutput_iface);
734

735
        writer_free_element_stack(writer);
736

737
        writer_free(writer, writer);
738 739
        if (imalloc) IMalloc_Release(imalloc);
    }
740 741 742 743 744

    return ref;
}

/*** IXmlWriter methods ***/
745
static HRESULT WINAPI xmlwriter_SetOutput(IXmlWriter *iface, IUnknown *output)
746 747
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
748 749
    IXmlWriterOutput *writeroutput;
    HRESULT hr;
750

751
    TRACE("(%p)->(%p)\n", This, output);
752

753 754 755 756
    if (This->output) {
        writeroutput_release_stream(This->output);
        IUnknown_Release(&This->output->IXmlWriterOutput_iface);
        This->output = NULL;
757
        This->bomwritten = 0;
758
        This->textnode = 0;
759
        This->indent_level = 0;
760
        writer_free_element_stack(This);
761 762 763
    }

    /* just reset current output */
764 765
    if (!output) {
        This->state = XmlWriterState_Initial;
766
        return S_OK;
767
    }
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782

    /* now try IXmlWriterOutput, ISequentialStream, IStream */
    hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&writeroutput);
    if (hr == S_OK) {
        if (writeroutput->lpVtbl == &xmlwriteroutputvtbl)
            This->output = impl_from_IXmlWriterOutput(writeroutput);
        else {
            ERR("got external IXmlWriterOutput implementation: %p, vtbl=%p\n",
                writeroutput, writeroutput->lpVtbl);
            IUnknown_Release(writeroutput);
            return E_FAIL;
        }
    }

    if (hr != S_OK || !writeroutput) {
783 784 785 786
        /* Create output for given stream. */
        hr = create_writer_output(output, This->imalloc, XmlEncoding_UTF8, NULL, &This->output);
        if (hr != S_OK)
            return hr;
787 788
    }

789 790 791 792
    if (This->output->encoding == XmlEncoding_Unknown)
        This->state = XmlWriterState_InvalidEncoding;
    else
        This->state = XmlWriterState_Ready;
793
    return writeroutput_query_for_stream(This->output);
794 795
}

796
static HRESULT WINAPI xmlwriter_GetProperty(IXmlWriter *iface, UINT property, LONG_PTR *value)
797 798 799
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

800
    TRACE("(%p)->(%s %p)\n", This, debugstr_writer_prop(property), value);
801

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
    if (!value) return E_INVALIDARG;

    switch (property)
    {
        case XmlWriterProperty_Indent:
            *value = This->indent;
            break;
        case XmlWriterProperty_ByteOrderMark:
            *value = This->bom;
            break;
        case XmlWriterProperty_OmitXmlDeclaration:
            *value = This->omitxmldecl;
            break;
        case XmlWriterProperty_ConformanceLevel:
            *value = This->conformance;
            break;
        default:
            FIXME("Unimplemented property (%u)\n", property);
            return E_NOTIMPL;
    }

    return S_OK;
824 825
}

826
static HRESULT WINAPI xmlwriter_SetProperty(IXmlWriter *iface, UINT property, LONG_PTR value)
827
{
828
    xmlwriter *writer = impl_from_IXmlWriter(iface);
829

830
    TRACE("%p, %s, %Id.\n", iface, debugstr_writer_prop(property), value);
831

832 833
    switch (property)
    {
834
        case XmlWriterProperty_Indent:
835
            writer->indent = !!value;
836
            break;
837
        case XmlWriterProperty_ByteOrderMark:
838
            writer->bom = !!value;
839
            break;
840
        case XmlWriterProperty_OmitXmlDeclaration:
841
            writer->omitxmldecl = !!value;
842 843 844 845 846 847 848
            break;
        default:
            FIXME("Unimplemented property (%u)\n", property);
            return E_NOTIMPL;
    }

    return S_OK;
849 850
}

851
static HRESULT writer_write_attribute(IXmlWriter *writer, IXmlReader *reader, BOOL write_default_attributes)
852
{
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    const WCHAR *prefix, *local, *uri, *value;
    HRESULT hr;

    if (IXmlReader_IsDefault(reader) && !write_default_attributes)
        return S_OK;

    if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
    if (FAILED(hr = IXmlReader_GetLocalName(reader, &local, NULL))) return hr;
    if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
    if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
    return IXmlWriter_WriteAttributeString(writer, prefix, local, uri, value);
}

static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
{
    XmlNodeType node_type;
    HRESULT hr = S_OK;
870

871
    TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
872

873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
    if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;

    switch (node_type)
    {
        case XmlNodeType_Element:
        case XmlNodeType_XmlDeclaration:
        case XmlNodeType_Attribute:
            if (node_type != XmlNodeType_Attribute)
            {
                if (FAILED(hr = IXmlReader_MoveToFirstAttribute(reader))) return hr;
                if (hr == S_FALSE) return S_OK;
            }
            if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) return hr;
            while (IXmlReader_MoveToNextAttribute(reader) == S_OK)
            {
                if (FAILED(hr = writer_write_attribute(iface, reader, write_default_attributes))) break;
            }
            if (node_type != XmlNodeType_Attribute && SUCCEEDED(hr))
                hr = IXmlReader_MoveToElement(reader);
            break;
        default:
            WARN("Unexpected node type %d.\n", node_type);
            return E_UNEXPECTED;
    }

    return hr;
899 900
}

901 902 903
static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
        const WCHAR *local, int local_len, const WCHAR *value)
{
904
    write_output_buffer_char(writer->output, ' ');
905
    write_output_qname(writer->output, prefix, prefix_len, local, local_len);
906
    write_output_buffer_char(writer->output, '=');
907 908 909
    write_output_buffer_quoted(writer->output, value, -1);
}

910 911
static BOOL is_valid_xml_space_value(const WCHAR *value)
{
912
    return value && (!wcscmp(value, L"preserve") || !wcscmp(value, L"default"));
913 914
}

915 916
static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
    LPCWSTR local, LPCWSTR uri, LPCWSTR value)
917
{
918
    xmlwriter *writer = impl_from_IXmlWriter(iface);
919
    BOOL is_xmlns_prefix, is_xmlns_local;
920 921 922
    int prefix_len, local_len;
    struct ns *ns;
    HRESULT hr;
923

924
    TRACE("%p, %s, %s, %s, %s.\n", iface, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
925

926
    switch (writer->state)
927 928 929
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
930
    case XmlWriterState_Ready:
931
    case XmlWriterState_DocClosed:
932
        writer->state = XmlWriterState_DocClosed;
933
        return WR_E_INVALIDACTION;
934 935
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
936 937 938 939
    default:
        ;
    }

940
    /* Prefix "xmlns" */
941
    is_xmlns_prefix = prefix && !wcscmp(prefix, L"xmlns");
942 943 944
    if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
        return WR_E_NSPREFIXDECLARED;

945
    if (is_empty_string(local))
946 947 948 949 950 951 952 953 954
        return E_INVALIDARG;

    /* Validate prefix and local name */
    if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
        return hr;

    if (FAILED(hr = is_valid_ncname(local, &local_len)))
        return hr;

955
    is_xmlns_local = !wcscmp(local, L"xmlns");
956

957 958
    /* Trivial case, no prefix. */
    if (prefix_len == 0 && is_empty_string(uri))
959
    {
960
        write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
961
        return S_OK;
962 963
    }

964
    /* Predefined "xml" prefix. */
965
    if (prefix_len && !wcscmp(prefix, L"xml"))
966 967
    {
        /* Valid "space" value is enforced. */
968
        if (!wcscmp(local, L"space") && !is_valid_xml_space_value(value))
969 970 971 972 973 974
            return WR_E_INVALIDXMLSPACE;

        /* Redefinition is not allowed. */
        if (!is_empty_string(uri))
            return WR_E_XMLPREFIXDECLARATION;

975
        write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
976 977 978 979

        return S_OK;
    }

980
    if (is_xmlns_prefix || (prefix_len == 0 && uri && !wcscmp(uri, xmlnsuriW)))
981 982 983 984 985
    {
        if (prefix_len && !is_empty_string(uri))
            return WR_E_XMLNSPREFIXDECLARATION;

        /* Look for exact match defined in current element, and write it out. */
986 987
        if (!(ns = writer_find_ns_current(writer, prefix, value)))
            ns = writer_push_ns(writer, local, local_len, value);
988 989
        ns->emitted = TRUE;

990
        write_output_attribute(writer, L"xmlns", 5, local, local_len, value);
991 992 993 994

        return S_OK;
    }

995
    /* Ignore prefix if URI wasn't specified. */
996
    if (is_xmlns_local && is_empty_string(uri))
997
    {
998
        write_output_attribute(writer, NULL, 0, L"xmlns", 5, value);
999 1000 1001
        return S_OK;
    }

1002
    if (!(ns = writer_find_ns(writer, prefix, uri)))
1003 1004 1005 1006 1007 1008 1009
    {
        if (is_empty_string(prefix) && !is_empty_string(uri))
        {
            FIXME("Prefix autogeneration is not implemented.\n");
            return E_NOTIMPL;
        }
        if (!is_empty_string(uri))
1010
            ns = writer_push_ns(writer, prefix, prefix_len, uri);
1011 1012 1013
    }

    if (ns)
1014
        write_output_attribute(writer, ns->prefix, ns->prefix_len, local, local_len, value);
1015
    else
1016
        write_output_attribute(writer, prefix, prefix_len, local, local_len, value);
1017 1018

    return S_OK;
1019 1020
}

1021 1022
static void write_cdata_section(xmlwriteroutput *output, const WCHAR *data, int len)
{
1023
    write_output_buffer(output, L"<![CDATA[", 9);
1024 1025
    if (data)
        write_output_buffer(output, data, len);
1026
    write_output_buffer(output, L"]]>", 3);
1027 1028 1029
}

static HRESULT WINAPI xmlwriter_WriteCData(IXmlWriter *iface, LPCWSTR data)
1030 1031
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1032
    int len;
1033

1034
    TRACE("%p %s\n", This, debugstr_w(data));
1035

1036 1037 1038 1039 1040 1041 1042
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1043
    case XmlWriterState_Ready:
1044
    case XmlWriterState_DocClosed:
1045
        This->state = XmlWriterState_DocClosed;
1046
        return WR_E_INVALIDACTION;
1047 1048
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1049 1050 1051 1052
    default:
        ;
    }

1053
    len = data ? lstrlenW(data) : 0;
1054

1055
    write_node_indent(This);
1056 1057
    if (!len)
        write_cdata_section(This->output, NULL, 0);
1058 1059 1060 1061 1062
    else
    {
        while (len)
        {
            const WCHAR *str = wcsstr(data, L"]]>");
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
            if (str) {
                str += 2;
                write_cdata_section(This->output, data, str - data);
                len -= str - data;
                data = str;
            }
            else {
                write_cdata_section(This->output, data, len);
                break;
            }
        }
    }

    return S_OK;
1077 1078
}

1079
static HRESULT WINAPI xmlwriter_WriteCharEntity(IXmlWriter *iface, WCHAR ch)
1080
{
1081
    xmlwriter *This = impl_from_IXmlWriter(iface);
1082
    WCHAR bufW[16];
1083

1084
    TRACE("%p %#x\n", This, ch);
1085 1086 1087 1088 1089

    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1090 1091
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1092 1093 1094
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1095 1096 1097 1098 1099 1100
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

1101
    swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%x;", ch);
1102 1103 1104
    write_output_buffer(This->output, bufW, -1);

    return S_OK;
1105 1106
}

1107
static HRESULT writer_get_next_write_count(const WCHAR *str, unsigned int length, unsigned int *count)
1108
{
1109
    if (!is_char(*str)) return WC_E_XMLCHARACTER;
1110

1111 1112 1113 1114
    if (IS_HIGH_SURROGATE(*str))
    {
        if (length < 2 || !IS_LOW_SURROGATE(*(str + 1)))
            return WR_E_INVALIDSURROGATEPAIR;
1115

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
        *count = 2;
    }
    else if (IS_LOW_SURROGATE(*str))
        return WR_E_INVALIDSURROGATEPAIR;
    else
        *count = 1;

    return S_OK;
}

static HRESULT write_escaped_char(xmlwriter *writer, const WCHAR *string, unsigned int count)
{
    HRESULT hr;

    switch (*string)
    {
       case '<':
           hr = write_output_buffer(writer->output, L"&lt;", 4);
           break;
       case '&':
           hr = write_output_buffer(writer->output, L"&amp;", 5);
           break;
       case '>':
           hr = write_output_buffer(writer->output, L"&gt;", 4);
           break;
       default:
           hr = write_output_buffer(writer->output, string, count);
    }

    return hr;
}

static HRESULT write_escaped_string(xmlwriter *writer, const WCHAR *string, unsigned int length)
{
    unsigned int count;
    HRESULT hr = S_OK;

    if (length == ~0u)
    {
        while (*string)
        {
            if (FAILED(hr = writer_get_next_write_count(string, ~0u, &count))) return hr;
            if (FAILED(hr = write_escaped_char(writer, string, count))) return hr;

            string += count;
        }
    }
    else
    {
        while (length)
        {
            if (FAILED(hr = writer_get_next_write_count(string, length, &count))) return hr;
            if (FAILED(hr = write_escaped_char(writer, string, count))) return hr;

            string += count;
            length -= count;
        }
    }

    return hr;
}

static HRESULT WINAPI xmlwriter_WriteChars(IXmlWriter *iface, const WCHAR *characters, UINT length)
{
    xmlwriter *writer = impl_from_IXmlWriter(iface);

    TRACE("%p, %s, %d.\n", iface, debugstr_wn(characters, length), length);

    if ((characters == NULL && length != 0))
        return E_INVALIDARG;

    if (length == 0)
        return S_OK;

    switch (writer->state)
1191 1192 1193
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1194 1195
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1196 1197 1198 1199
    case XmlWriterState_ElemStarted:
        writer_close_starttag(writer);
        break;
    case XmlWriterState_Ready:
1200
    case XmlWriterState_DocClosed:
1201
        writer->state = XmlWriterState_DocClosed;
1202 1203 1204 1205 1206
        return WR_E_INVALIDACTION;
    default:
        ;
    }

1207 1208
    writer->textnode = 1;
    return write_escaped_string(writer, characters, length);
1209 1210
}

1211
static HRESULT WINAPI xmlwriter_WriteComment(IXmlWriter *iface, LPCWSTR comment)
1212
{
1213 1214 1215 1216 1217 1218 1219 1220
    xmlwriter *This = impl_from_IXmlWriter(iface);

    TRACE("%p %s\n", This, debugstr_w(comment));

    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1221 1222
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1223 1224 1225 1226 1227 1228 1229 1230 1231
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

1232
    write_node_indent(This);
1233
    write_output_buffer(This->output, L"<!--", 4);
1234
    if (comment) {
1235
        int len = lstrlenW(comment), i;
1236 1237 1238 1239 1240 1241 1242

        /* Make sure there's no two hyphen sequences in a string, space is used as a separator to produce compliant
           comment string */
        if (len > 1) {
            for (i = 0; i < len; i++) {
                write_output_buffer(This->output, comment + i, 1);
                if (comment[i] == '-' && (i + 1 < len) && comment[i+1] == '-')
1243
                    write_output_buffer_char(This->output, ' ');
1244 1245 1246 1247 1248 1249
            }
        }
        else
            write_output_buffer(This->output, comment, len);

        if (len && comment[len-1] == '-')
1250
            write_output_buffer_char(This->output, ' ');
1251
    }
1252
    write_output_buffer(This->output, L"-->", 3);
1253 1254

    return S_OK;
1255 1256
}

1257 1258
static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR name, LPCWSTR pubid,
        LPCWSTR sysid, LPCWSTR subset)
1259 1260
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1261 1262
    unsigned int name_len, pubid_len;
    HRESULT hr;
1263

1264 1265
    TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(pubid), wine_dbgstr_w(sysid),
            wine_dbgstr_w(subset));
1266

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
    case XmlWriterState_Content:
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

    if (is_empty_string(name))
        return E_INVALIDARG;

    if (FAILED(hr = is_valid_name(name, &name_len)))
        return hr;

    if (FAILED(hr = is_valid_pubid(pubid, &pubid_len)))
        return hr;

1289
    write_output_buffer(This->output, L"<!DOCTYPE ", 10);
1290 1291 1292 1293
    write_output_buffer(This->output, name, name_len);

    if (pubid)
    {
1294
        write_output_buffer(This->output, L" PUBLIC ", 8);
1295
        write_output_buffer_quoted(This->output, pubid, pubid_len);
1296
        write_output_buffer_char(This->output, ' ');
1297 1298 1299 1300
        write_output_buffer_quoted(This->output, sysid, -1);
    }
    else if (sysid)
    {
1301
        write_output_buffer(This->output, L" SYSTEM ", 8);
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
        write_output_buffer_quoted(This->output, sysid, -1);
    }

    if (subset)
    {
        write_output_buffer_char(This->output, ' ');
        write_output_buffer_char(This->output, '[');
        write_output_buffer(This->output, subset, -1);
        write_output_buffer_char(This->output, ']');
    }
    write_output_buffer_char(This->output, '>');

    This->state = XmlWriterState_Content;

    return S_OK;
1317 1318
}

1319 1320
static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
                                     LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
1321 1322
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1323
    int prefix_len, local_len;
1324
    struct ns *ns;
1325
    HRESULT hr;
1326

1327 1328
    TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
                        wine_dbgstr_w(uri), wine_dbgstr_w(value));
1329

1330 1331 1332 1333
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1334 1335
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1336 1337 1338
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1339
    case XmlWriterState_DocClosed:
1340
        return WR_E_INVALIDACTION;
1341 1342
    default:
        ;
1343 1344
    }

1345 1346 1347
    if (!local_name)
        return E_INVALIDARG;

1348 1349 1350 1351 1352 1353 1354
    /* Validate prefix and local name */
    if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
        return hr;

    if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
        return hr;

1355 1356 1357 1358
    ns = writer_find_ns(This, prefix, uri);
    if (!ns && !is_empty_string(prefix) && is_empty_string(uri))
        return WR_E_NSPREFIXWITHEMPTYNSURI;

1359
    if (uri && !wcscmp(uri, xmlnsuriW))
1360 1361 1362 1363 1364 1365 1366 1367
    {
        if (!prefix)
            return WR_E_XMLNSPREFIXDECLARATION;

        if (!is_empty_string(prefix))
            return WR_E_XMLNSURIDECLARATION;
    }

1368
    write_encoding_bom(This);
1369
    write_node_indent(This);
1370

1371
    write_output_buffer_char(This->output, '<');
1372 1373 1374 1375 1376 1377 1378
    if (ns)
        write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
    else
        write_output_qname(This->output, prefix, prefix_len, local_name, local_len);

    if (!ns && (prefix_len || !is_empty_string(uri)))
    {
1379
        write_output_qname(This->output, L" xmlns", 6, prefix, prefix_len);
1380
        write_output_buffer_char(This->output, '=');
1381 1382
        write_output_buffer_quoted(This->output, uri, -1);
    }
1383 1384

    if (value)
1385
    {
1386
        write_output_buffer_char(This->output, '>');
1387
        write_output_buffer(This->output, value, -1);
1388
        write_output_buffer(This->output, L"</", 2);
1389
        write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1390
        write_output_buffer_char(This->output, '>');
1391 1392
    }
    else
1393
        write_output_buffer(This->output, L" />", 3);
1394 1395 1396 1397

    This->state = XmlWriterState_Content;

    return S_OK;
1398 1399 1400 1401 1402 1403
}

static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

1404
    TRACE("%p\n", This);
1405

1406 1407 1408
    switch (This->state)
    {
    case XmlWriterState_Initial:
1409
        return E_UNEXPECTED;
1410 1411
    case XmlWriterState_Ready:
    case XmlWriterState_DocClosed:
1412 1413
        This->state = XmlWriterState_DocClosed;
        return WR_E_INVALIDACTION;
1414 1415
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
    default:
        ;
    }

    /* empty element stack */
    while (IXmlWriter_WriteEndElement(iface) == S_OK)
        ;

    This->state = XmlWriterState_DocClosed;
    return S_OK;
1426 1427 1428 1429 1430
}

static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1431
    struct element *element;
1432

1433
    TRACE("%p\n", This);
1434

1435 1436 1437 1438
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1439 1440 1441 1442
    case XmlWriterState_Ready:
    case XmlWriterState_DocClosed:
        This->state = XmlWriterState_DocClosed;
        return WR_E_INVALIDACTION;
1443 1444
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1445 1446 1447 1448
    default:
        ;
    }

1449 1450 1451 1452
    element = pop_element(This);
    if (!element)
        return WR_E_INVALIDACTION;

1453 1454
    writer_dec_indent(This);

1455 1456
    if (This->starttagopen)
    {
1457
        writer_output_ns(This, element);
1458
        write_output_buffer(This->output, L" />", 3);
1459
        This->starttagopen = 0;
1460
    }
1461 1462 1463
    else
    {
        /* Write full end tag. */
1464
        write_node_indent(This);
1465
        write_output_buffer(This->output, L"</", 2);
1466
        write_output_buffer(This->output, element->qname, element->len);
1467
        write_output_buffer_char(This->output, '>');
1468
    }
1469
    writer_free_element(This, element);
1470 1471

    return S_OK;
1472 1473 1474 1475 1476 1477 1478 1479
}

static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

    FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));

1480 1481 1482 1483
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1484 1485
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1486 1487 1488 1489 1490 1491
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

1492 1493 1494 1495 1496 1497
    return E_NOTIMPL;
}

static HRESULT WINAPI xmlwriter_WriteFullEndElement(IXmlWriter *iface)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1498
    struct element *element;
1499

1500
    TRACE("%p\n", This);
1501

1502 1503 1504 1505 1506 1507 1508 1509
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_Ready:
    case XmlWriterState_DocClosed:
        This->state = XmlWriterState_DocClosed;
        return WR_E_INVALIDACTION;
1510 1511
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1512 1513 1514
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1515 1516 1517 1518
    default:
        ;
    }

1519 1520 1521 1522
    element = pop_element(This);
    if (!element)
        return WR_E_INVALIDACTION;

1523 1524
    writer_dec_indent(This);

1525 1526
    /* don't force full end tag to the next line */
    if (This->state == XmlWriterState_ElemStarted)
1527
    {
1528
        This->state = XmlWriterState_Content;
1529 1530
        This->textnode = 0;
    }
1531 1532 1533
    else
        write_node_indent(This);

1534
    /* write full end tag */
1535
    write_output_buffer(This->output, L"</", 2);
1536
    write_output_buffer(This->output, element->qname, element->len);
1537
    write_output_buffer_char(This->output, '>');
1538

1539 1540
    writer_free_element(This, element);

1541
    return S_OK;
1542 1543 1544 1545 1546 1547 1548 1549
}

static HRESULT WINAPI xmlwriter_WriteName(IXmlWriter *iface, LPCWSTR pwszName)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

    FIXME("%p %s\n", This, wine_dbgstr_w(pwszName));

1550 1551 1552 1553
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1554
    case XmlWriterState_Ready:
1555
    case XmlWriterState_DocClosed:
1556
        This->state = XmlWriterState_DocClosed;
1557
        return WR_E_INVALIDACTION;
1558 1559
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1560 1561 1562 1563
    default:
        ;
    }

1564 1565 1566 1567 1568 1569 1570 1571 1572
    return E_NOTIMPL;
}

static HRESULT WINAPI xmlwriter_WriteNmToken(IXmlWriter *iface, LPCWSTR pwszNmToken)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

    FIXME("%p %s\n", This, wine_dbgstr_w(pwszNmToken));

1573 1574 1575 1576
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1577
    case XmlWriterState_Ready:
1578
    case XmlWriterState_DocClosed:
1579
        This->state = XmlWriterState_DocClosed;
1580
        return WR_E_INVALIDACTION;
1581 1582
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1583 1584 1585 1586
    default:
        ;
    }

1587 1588 1589
    return E_NOTIMPL;
}

1590
static HRESULT writer_write_node(IXmlWriter *writer, IXmlReader *reader, BOOL shallow, BOOL write_default_attributes)
1591
{
1592 1593 1594 1595 1596
    XmlStandalone standalone = XmlStandalone_Omit;
    const WCHAR *name, *value, *prefix, *uri;
    unsigned int start_depth = 0, depth;
    XmlNodeType node_type;
    HRESULT hr;
1597

1598
    if (FAILED(hr = IXmlReader_GetNodeType(reader, &node_type))) return hr;
1599

1600 1601 1602
    switch (node_type)
    {
        case XmlNodeType_None:
1603
            if (shallow) return S_OK;
1604 1605
            while ((hr = IXmlReader_Read(reader, NULL)) == S_OK)
            {
1606
                if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620
            }
            break;
        case XmlNodeType_Element:
            if (FAILED(hr = IXmlReader_GetPrefix(reader, &prefix, NULL))) return hr;
            if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
            if (FAILED(hr = IXmlReader_GetNamespaceUri(reader, &uri, NULL))) return hr;
            if (FAILED(hr = IXmlWriter_WriteStartElement(writer, prefix, name, uri))) return hr;
            if (FAILED(hr = IXmlWriter_WriteAttributes(writer, reader, write_default_attributes))) return hr;
            if (IXmlReader_IsEmptyElement(reader))
            {
                hr = IXmlWriter_WriteEndElement(writer);
            }
            else
            {
1621
                if (shallow) return S_OK;
1622 1623 1624 1625
                if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;
                if (FAILED(hr = IXmlReader_GetDepth(reader, &start_depth))) return hr;
                while ((hr = IXmlReader_Read(reader, &node_type)) == S_OK)
                {
1626
                    if (FAILED(hr = writer_write_node(writer, reader, FALSE, write_default_attributes))) return hr;
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
                    if (FAILED(hr = IXmlReader_MoveToElement(reader))) return hr;

                    depth = 0;
                    if (FAILED(hr = IXmlReader_GetDepth(reader, &depth))) return hr;
                    if (node_type == XmlNodeType_EndElement && (start_depth == depth - 1)) break;
                }
            }
            break;
        case XmlNodeType_Attribute:
            break;
        case XmlNodeType_Text:
            if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
            hr = IXmlWriter_WriteRaw(writer, value);
            break;
        case XmlNodeType_CDATA:
            if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
            hr = IXmlWriter_WriteCData(writer, value);
            break;
        case XmlNodeType_ProcessingInstruction:
            if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
            if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
            hr = IXmlWriter_WriteProcessingInstruction(writer, name, value);
            break;
        case XmlNodeType_Comment:
            if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
            hr = IXmlWriter_WriteComment(writer, value);
            break;
        case XmlNodeType_Whitespace:
            if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
            hr = IXmlWriter_WriteWhitespace(writer, value);
            break;
        case XmlNodeType_EndElement:
            hr = IXmlWriter_WriteFullEndElement(writer);
            break;
        case XmlNodeType_XmlDeclaration:
1662
            while ((hr = IXmlReader_MoveToNextAttribute(reader)) == S_OK)
1663
            {
1664 1665 1666 1667 1668 1669
                if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) return hr;
                if (!wcscmp(name, L"standalone"))
                {
                    if (FAILED(hr = IXmlReader_GetValue(reader, &value, NULL))) return hr;
                    standalone = !wcscmp(value, L"yes") ? XmlStandalone_Yes : XmlStandalone_No;
                }
1670
            }
1671 1672
            if (SUCCEEDED(hr))
                hr = IXmlWriter_WriteStartDocument(writer, standalone);
1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
            break;
        default:
            WARN("Unknown node type %d.\n", node_type);
            return E_UNEXPECTED;
    }

    return hr;
}

static HRESULT WINAPI xmlwriter_WriteNode(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
{
    HRESULT hr;

    TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);

1688 1689 1690
    if (!reader)
        return E_INVALIDARG;

1691
    if (SUCCEEDED(hr = writer_write_node(iface, reader, FALSE, write_default_attributes)))
1692 1693 1694
        hr = IXmlReader_Read(reader, NULL);

    return hr;
1695 1696
}

1697
static HRESULT WINAPI xmlwriter_WriteNodeShallow(IXmlWriter *iface, IXmlReader *reader, BOOL write_default_attributes)
1698
{
1699
    TRACE("%p, %p, %d.\n", iface, reader, write_default_attributes);
1700

1701 1702 1703
    if (!reader)
        return E_INVALIDARG;

1704
    return writer_write_node(iface, reader, TRUE, write_default_attributes);
1705 1706
}

1707 1708
static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LPCWSTR name,
                                             LPCWSTR text)
1709 1710 1711
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

1712
    TRACE("(%p)->(%s %s)\n", This, wine_dbgstr_w(name), wine_dbgstr_w(text));
1713

1714 1715 1716
    switch (This->state)
    {
    case XmlWriterState_Initial:
1717
        return E_UNEXPECTED;
1718 1719
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1720
    case XmlWriterState_DocStarted:
1721
        if (!wcscmp(name, L"xml"))
1722 1723 1724
            return WR_E_INVALIDACTION;
        break;
    case XmlWriterState_ElemStarted:
1725 1726
        writer_close_starttag(This);
        break;
1727
    case XmlWriterState_DocClosed:
1728
        return WR_E_INVALIDACTION;
1729 1730 1731
    default:
        ;
    }
1732

1733
    write_encoding_bom(This);
1734
    write_node_indent(This);
1735
    write_output_buffer(This->output, L"<?", 2);
1736
    write_output_buffer(This->output, name, -1);
1737
    write_output_buffer_char(This->output, ' ');
1738
    write_output_buffer(This->output, text, -1);
1739
    write_output_buffer(This->output, L"?>", 2);
1740

1741
    if (!wcscmp(name, L"xml"))
1742 1743 1744
        This->state = XmlWriterState_PIDocStarted;

    return S_OK;
1745 1746 1747 1748 1749 1750 1751 1752 1753
}

static HRESULT WINAPI xmlwriter_WriteQualifiedName(IXmlWriter *iface, LPCWSTR pwszLocalName,
                                     LPCWSTR pwszNamespaceUri)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

    FIXME("%p %s %s\n", This, wine_dbgstr_w(pwszLocalName), wine_dbgstr_w(pwszNamespaceUri));

1754 1755 1756 1757
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1758 1759
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1760 1761 1762 1763 1764 1765
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

1766 1767 1768
    return E_NOTIMPL;
}

1769
static HRESULT WINAPI xmlwriter_WriteRaw(IXmlWriter *iface, LPCWSTR data)
1770 1771
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1772 1773
    unsigned int count;
    HRESULT hr = S_OK;
1774

1775
    TRACE("%p %s\n", This, debugstr_w(data));
1776

1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
    if (!data)
        return S_OK;

    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_Ready:
        write_xmldecl(This, XmlStandalone_Omit);
        /* fallthrough */
    case XmlWriterState_DocStarted:
    case XmlWriterState_PIDocStarted:
        break;
1790 1791
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1792 1793 1794
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1795
    default:
1796
        This->state = XmlWriterState_DocClosed;
1797 1798 1799
        return WR_E_INVALIDACTION;
    }

1800 1801 1802 1803 1804 1805 1806 1807 1808
    while (*data)
    {
        if (FAILED(hr = writer_get_next_write_count(data, ~0u, &count))) return hr;
        if (FAILED(hr = write_output_buffer(This->output, data, count))) return hr;

        data += count;
    }

    return hr;
1809 1810
}

1811
static HRESULT WINAPI xmlwriter_WriteRawChars(IXmlWriter *iface,  const WCHAR *characters, UINT length)
1812
{
1813 1814 1815
    xmlwriter *writer = impl_from_IXmlWriter(iface);
    HRESULT hr = S_OK;
    unsigned int count;
1816

1817
    TRACE("%p, %s, %d.\n", iface, debugstr_wn(characters, length), length);
1818

1819 1820 1821 1822 1823 1824 1825
    if ((characters == NULL && length != 0))
        return E_INVALIDARG;

    if (length == 0)
        return S_OK;

    switch (writer->state)
1826 1827 1828
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1829 1830
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1831 1832
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
1833 1834 1835 1836 1837
    case XmlWriterState_Ready:
        write_xmldecl(writer, XmlStandalone_Omit);
        break;
    case XmlWriterState_ElemStarted:
        writer_close_starttag(writer);
1838 1839 1840 1841
    default:
        ;
    }

1842 1843 1844 1845 1846 1847 1848 1849 1850 1851
    while (length)
    {
        if (FAILED(hr = writer_get_next_write_count(characters, length, &count))) return hr;
        if (FAILED(hr = write_output_buffer(writer->output, characters, count))) return hr;

        characters += count;
        length -= count;
    }

    return hr;
1852 1853 1854 1855 1856 1857
}

static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandalone standalone)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

1858
    TRACE("(%p)->(%d)\n", This, standalone);
1859

1860 1861 1862 1863 1864 1865 1866
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_PIDocStarted:
        This->state = XmlWriterState_DocStarted;
        return S_OK;
1867 1868
    case XmlWriterState_Ready:
        break;
1869 1870
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1871
    default:
1872
        This->state = XmlWriterState_DocClosed;
1873
        return WR_E_INVALIDACTION;
1874 1875
    }

1876
    return write_xmldecl(This, standalone);
1877 1878
}

1879
static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
1880 1881
{
    xmlwriter *This = impl_from_IXmlWriter(iface);
1882
    int prefix_len, local_len;
1883
    struct element *element;
1884
    struct ns *ns;
1885
    HRESULT hr;
1886

1887
    TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
1888

1889 1890 1891
    if (!local_name)
        return E_INVALIDARG;

1892 1893 1894
    switch (This->state)
    {
    case XmlWriterState_Initial:
1895
        return E_UNEXPECTED;
1896 1897
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1898 1899
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
1900 1901 1902
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1903 1904 1905
    default:
        ;
    }
1906

1907 1908 1909 1910 1911 1912 1913
    /* Validate prefix and local name */
    if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
        return hr;

    if (FAILED(hr = is_valid_ncname(local_name, &local_len)))
        return hr;

1914
    if (uri && !wcscmp(uri, xmlnsuriW))
1915 1916 1917 1918 1919 1920 1921 1922 1923
    {
        if (!prefix)
            return WR_E_XMLNSPREFIXDECLARATION;

        if (!is_empty_string(prefix))
            return WR_E_XMLNSURIDECLARATION;
    }

    ns = writer_find_ns(This, prefix, uri);
1924 1925 1926 1927

    element = alloc_element(This, prefix, local_name);
    if (!element)
        return E_OUTOFMEMORY;
1928 1929

    write_encoding_bom(This);
1930 1931
    write_node_indent(This);

1932
    This->state = XmlWriterState_ElemStarted;
1933
    This->starttagopen = 1;
1934

1935 1936
    writer_push_element(This, element);

1937
    if (!ns && !is_empty_string(uri))
1938
        writer_push_ns(This, prefix, prefix_len, uri);
1939

1940
    write_output_buffer_char(This->output, '<');
1941 1942 1943 1944
    if (ns)
        write_output_qname(This->output, ns->prefix, ns->prefix_len, local_name, local_len);
    else
        write_output_qname(This->output, prefix, prefix_len, local_name, local_len);
1945
    writer_inc_indent(This);
1946 1947

    return S_OK;
1948 1949
}

1950
static HRESULT WINAPI xmlwriter_WriteString(IXmlWriter *iface, const WCHAR *string)
1951 1952 1953
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

1954 1955 1956 1957
    TRACE("%p %s\n", This, debugstr_w(string));

    if (!string)
        return S_OK;
1958

1959 1960 1961 1962
    switch (This->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
1963 1964 1965
    case XmlWriterState_ElemStarted:
        writer_close_starttag(This);
        break;
1966
    case XmlWriterState_Ready:
1967
    case XmlWriterState_DocClosed:
1968
        This->state = XmlWriterState_DocClosed;
1969
        return WR_E_INVALIDACTION;
1970 1971
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
1972 1973 1974 1975
    default:
        ;
    }

1976
    This->textnode = 1;
1977
    return write_escaped_string(This, string, ~0u);
1978 1979 1980 1981
}

static HRESULT WINAPI xmlwriter_WriteSurrogateCharEntity(IXmlWriter *iface, WCHAR wchLow, WCHAR wchHigh)
{
1982 1983 1984
    xmlwriter *writer = impl_from_IXmlWriter(iface);
    int codepoint;
    WCHAR bufW[16];
1985

1986
    TRACE("%p, %d, %d.\n", iface, wchLow, wchHigh);
1987

1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
    if (!IS_SURROGATE_PAIR(wchHigh, wchLow))
        return WC_E_XMLCHARACTER;

    switch (writer->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
    case XmlWriterState_ElemStarted:
        writer_close_starttag(writer);
        break;
    case XmlWriterState_DocClosed:
        return WR_E_INVALIDACTION;
    default:
        ;
    }

    codepoint = ((wchHigh - 0xd800) * 0x400) + (wchLow - 0xdc00) + 0x10000;
    swprintf(bufW, ARRAY_SIZE(bufW), L"&#x%X;", codepoint);
    write_output_buffer(writer->output, bufW, -1);

    return S_OK;
2011 2012
}

2013
static HRESULT WINAPI xmlwriter_WriteWhitespace(IXmlWriter *iface, LPCWSTR text)
2014
{
2015 2016
    xmlwriter *writer = impl_from_IXmlWriter(iface);
    size_t length = 0;
2017

2018
    TRACE("%p, %s.\n", iface, wine_dbgstr_w(text));
2019

2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042
    switch (writer->state)
    {
    case XmlWriterState_Initial:
        return E_UNEXPECTED;
    case XmlWriterState_ElemStarted:
        writer_close_starttag(writer);
        break;
    case XmlWriterState_InvalidEncoding:
        return MX_E_ENCODING;
    case XmlWriterState_Ready:
        break;
    default:
        return WR_E_INVALIDACTION;
    }

    while (text[length])
    {
        if (!is_wchar_space(text[length])) return WR_E_NONWHITESPACE;
        length++;
    }

    write_output_buffer(writer->output, text, length);
    return S_OK;
2043 2044 2045 2046 2047 2048
}

static HRESULT WINAPI xmlwriter_Flush(IXmlWriter *iface)
{
    xmlwriter *This = impl_from_IXmlWriter(iface);

2049
    TRACE("%p\n", This);
2050

2051
    return writeroutput_flush_stream(This->output);
2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089
}

static const struct IXmlWriterVtbl xmlwriter_vtbl =
{
    xmlwriter_QueryInterface,
    xmlwriter_AddRef,
    xmlwriter_Release,
    xmlwriter_SetOutput,
    xmlwriter_GetProperty,
    xmlwriter_SetProperty,
    xmlwriter_WriteAttributes,
    xmlwriter_WriteAttributeString,
    xmlwriter_WriteCData,
    xmlwriter_WriteCharEntity,
    xmlwriter_WriteChars,
    xmlwriter_WriteComment,
    xmlwriter_WriteDocType,
    xmlwriter_WriteElementString,
    xmlwriter_WriteEndDocument,
    xmlwriter_WriteEndElement,
    xmlwriter_WriteEntityRef,
    xmlwriter_WriteFullEndElement,
    xmlwriter_WriteName,
    xmlwriter_WriteNmToken,
    xmlwriter_WriteNode,
    xmlwriter_WriteNodeShallow,
    xmlwriter_WriteProcessingInstruction,
    xmlwriter_WriteQualifiedName,
    xmlwriter_WriteRaw,
    xmlwriter_WriteRawChars,
    xmlwriter_WriteStartDocument,
    xmlwriter_WriteStartElement,
    xmlwriter_WriteString,
    xmlwriter_WriteSurrogateCharEntity,
    xmlwriter_WriteWhitespace,
    xmlwriter_Flush
};

2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115
/** IXmlWriterOutput **/
static HRESULT WINAPI xmlwriteroutput_QueryInterface(IXmlWriterOutput *iface, REFIID riid, void** ppvObject)
{
    xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);

    if (IsEqualGUID(riid, &IID_IXmlWriterOutput) ||
        IsEqualGUID(riid, &IID_IUnknown))
    {
        *ppvObject = iface;
    }
    else
    {
        WARN("interface %s not implemented\n", debugstr_guid(riid));
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef(iface);

    return S_OK;
}

static ULONG WINAPI xmlwriteroutput_AddRef(IXmlWriterOutput *iface)
{
2116 2117 2118
    xmlwriteroutput *output = impl_from_IXmlWriterOutput(iface);
    ULONG ref = InterlockedIncrement(&output->ref);
    TRACE("%p, refcount %ld.\n", iface, ref);
2119 2120 2121 2122 2123 2124 2125 2126
    return ref;
}

static ULONG WINAPI xmlwriteroutput_Release(IXmlWriterOutput *iface)
{
    xmlwriteroutput *This = impl_from_IXmlWriterOutput(iface);
    LONG ref = InterlockedDecrement(&This->ref);

2127
    TRACE("%p, refcount %ld.\n", iface, ref);
2128 2129 2130 2131 2132

    if (ref == 0)
    {
        IMalloc *imalloc = This->imalloc;
        if (This->output) IUnknown_Release(This->output);
2133
        if (This->stream) ISequentialStream_Release(This->stream);
2134
        free_output_buffer(This);
2135
        writeroutput_free(This, This->encoding_name);
2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149
        writeroutput_free(This, This);
        if (imalloc) IMalloc_Release(imalloc);
    }

    return ref;
}

static const struct IUnknownVtbl xmlwriteroutputvtbl =
{
    xmlwriteroutput_QueryInterface,
    xmlwriteroutput_AddRef,
    xmlwriteroutput_Release
};

2150
HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
2151 2152
{
    xmlwriter *writer;
2153
    HRESULT hr;
2154

2155
    TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid), obj, imalloc);
2156

2157
    if (!(writer = m_alloc(imalloc, sizeof(*writer))))
2158 2159
        return E_OUTOFMEMORY;
    memset(writer, 0, sizeof(*writer));
2160 2161 2162

    writer->IXmlWriter_iface.lpVtbl = &xmlwriter_vtbl;
    writer->ref = 1;
2163 2164
    writer->imalloc = imalloc;
    if (imalloc) IMalloc_AddRef(imalloc);
2165 2166
    writer->bom = TRUE;
    writer->conformance = XmlConformanceLevel_Document;
2167
    writer->state = XmlWriterState_Initial;
2168
    list_init(&writer->elements);
2169

2170 2171
    hr = IXmlWriter_QueryInterface(&writer->IXmlWriter_iface, riid, obj);
    IXmlWriter_Release(&writer->IXmlWriter_iface);
2172

2173
    TRACE("returning iface %p, hr %#lx.\n", *obj, hr);
2174

2175
    return hr;
2176
}
2177

2178
static HRESULT create_writer_output(IUnknown *stream, IMalloc *imalloc, xml_encoding encoding,
2179
    const WCHAR *encoding_name, xmlwriteroutput **out)
2180 2181
{
    xmlwriteroutput *writeroutput;
2182
    HRESULT hr;
2183

2184
    *out = NULL;
2185

2186
    if (!(writeroutput = m_alloc(imalloc, sizeof(*writeroutput))))
2187
        return E_OUTOFMEMORY;
2188
    memset(writeroutput, 0, sizeof(*writeroutput));
2189 2190 2191 2192

    writeroutput->IXmlWriterOutput_iface.lpVtbl = &xmlwriteroutputvtbl;
    writeroutput->ref = 1;
    writeroutput->imalloc = imalloc;
2193 2194
    if (imalloc)
        IMalloc_AddRef(imalloc);
2195
    writeroutput->encoding = encoding;
2196 2197 2198 2199 2200
    hr = init_output_buffer(writeroutput);
    if (FAILED(hr)) {
        IUnknown_Release(&writeroutput->IXmlWriterOutput_iface);
        return hr;
    }
2201

2202 2203
    if (encoding_name)
    {
2204
        unsigned int size = (lstrlenW(encoding_name) + 1) * sizeof(WCHAR);
2205 2206 2207 2208
        writeroutput->encoding_name = writeroutput_alloc(writeroutput, size);
        memcpy(writeroutput->encoding_name, encoding_name, size);
    }

2209 2210
    IUnknown_QueryInterface(stream, &IID_IUnknown, (void**)&writeroutput->output);

2211
    *out = writeroutput;
2212

2213
    TRACE("Created writer output %p\n", *out);
2214 2215 2216

    return S_OK;
}
2217

2218 2219
HRESULT WINAPI CreateXmlWriterOutputWithEncodingName(IUnknown *stream, IMalloc *imalloc, const WCHAR *encoding,
        IXmlWriterOutput **out)
2220
{
2221
    xmlwriteroutput *output;
2222
    xml_encoding xml_enc;
2223 2224 2225
    HRESULT hr;

    TRACE("%p %p %s %p\n", stream, imalloc, debugstr_w(encoding), out);
2226

2227 2228
    if (!stream || !out)
        return E_INVALIDARG;
2229

2230
    *out = NULL;
2231

2232
    xml_enc = encoding ? parse_encoding_name(encoding, -1) : XmlEncoding_UTF8;
2233 2234 2235 2236
    if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, encoding, &output)))
        *out = &output->IXmlWriterOutput_iface;

    return hr;
2237 2238
}

2239 2240
HRESULT WINAPI CreateXmlWriterOutputWithEncodingCodePage(IUnknown *stream, IMalloc *imalloc, UINT codepage,
        IXmlWriterOutput **out)
2241
{
2242
    xmlwriteroutput *output;
2243
    xml_encoding xml_enc;
2244
    HRESULT hr;
2245

2246
    TRACE("%p %p %u %p\n", stream, imalloc, codepage, out);
2247

2248 2249 2250 2251
    if (!stream || !out)
        return E_INVALIDARG;

    *out = NULL;
2252 2253

    xml_enc = get_encoding_from_codepage(codepage);
2254 2255 2256 2257
    if (SUCCEEDED(hr = create_writer_output(stream, imalloc, xml_enc, NULL, &output)))
        *out = &output->IXmlWriterOutput_iface;

    return hr;
2258
}