Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/libxml2/xmlsave.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * xmlsave.c: Implementation of the document serializer
3
 *
4
 * See Copyright for the status of this software.
5
 *
6
 * daniel@veillard.com
7
 */
8
9
#define IN_LIBXML
10
#include "libxml.h"
11
12
#include <limits.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <libxml/xmlmemory.h>
16
#include <libxml/parserInternals.h>
17
#include <libxml/tree.h>
18
#include <libxml/xmlsave.h>
19
20
0
#define MAX_INDENT 60
21
22
#include <libxml/HTMLtree.h>
23
24
#include "private/buf.h"
25
#include "private/enc.h"
26
#include "private/entities.h"
27
#include "private/error.h"
28
#include "private/io.h"
29
#include "private/save.h"
30
31
#ifdef LIBXML_OUTPUT_ENABLED
32
33
0
#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
34
35
struct _xmlSaveCtxt {
36
    void *_private;
37
    int type;
38
    int fd;
39
    const xmlChar *filename;
40
    const xmlChar *encoding;
41
    xmlCharEncodingHandlerPtr handler;
42
    xmlOutputBufferPtr buf;
43
    int options;
44
    int level;
45
    int format;
46
    char indent[MAX_INDENT + 1];  /* array for indenting output */
47
    int indent_nr;
48
    int indent_size;
49
    xmlCharEncodingOutputFunc escape; /* used for element content */
50
};
51
52
/************************************************************************
53
 *                  *
54
 *      Output error handlers       *
55
 *                  *
56
 ************************************************************************/
57
/**
58
 * xmlSaveErrMemory:
59
 * @extra:  extra information
60
 *
61
 * Handle an out of memory condition
62
 */
63
static void
64
xmlSaveErrMemory(xmlOutputBufferPtr out)
65
0
{
66
0
    if (out != NULL)
67
0
        out->error = XML_ERR_NO_MEMORY;
68
0
    xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_OUTPUT, NULL);
69
0
}
70
71
/**
72
 * xmlSaveErr:
73
 * @code:  the error number
74
 * @node:  the location of the error.
75
 * @extra:  extra information
76
 *
77
 * Handle an out of memory condition
78
 */
79
static void
80
xmlSaveErr(xmlOutputBufferPtr out, int code, xmlNodePtr node,
81
           const char *extra)
82
0
{
83
0
    const char *msg = NULL;
84
0
    int res;
85
86
    /* Don't overwrite catastrophic errors */
87
0
    if ((out != NULL) &&
88
0
        (out->error != XML_ERR_OK) &&
89
0
        (xmlIsCatastrophicError(XML_ERR_FATAL, out->error)))
90
0
        return;
91
92
0
    if (code == XML_ERR_NO_MEMORY) {
93
0
        xmlSaveErrMemory(out);
94
0
        return;
95
0
    }
96
97
0
    if (out != NULL)
98
0
        out->error = code;
99
100
0
    if (code == XML_ERR_UNSUPPORTED_ENCODING) {
101
0
        msg = "Unsupported encoding: %s";
102
0
    } else {
103
0
        msg = xmlErrString(code);
104
0
        extra = NULL;
105
0
    }
106
107
0
    res = xmlRaiseError(NULL, NULL, NULL, NULL, node,
108
0
                        XML_FROM_OUTPUT, code, XML_ERR_ERROR, NULL, 0,
109
0
                        extra, NULL, NULL, 0, 0,
110
0
                        msg, extra);
111
0
    if (res < 0)
112
0
        xmlSaveErrMemory(out);
113
0
}
114
115
/************************************************************************
116
 *                  *
117
 *      Special escaping routines     *
118
 *                  *
119
 ************************************************************************/
120
121
/*
122
 * Tables generated with tools/genEscape.py
123
 */
124
125
static const char xmlEscapeContent[] = {
126
      8, '&', '#', 'x', 'F', 'F', 'F', 'D', ';',   4, '&', '#',
127
    '9', ';',   5, '&', '#', '1', '0', ';',   5, '&', '#', '1',
128
    '3', ';',   6, '&', 'q', 'u', 'o', 't', ';',   5, '&', 'a',
129
    'm', 'p', ';',   4, '&', 'l', 't', ';',   4, '&', 'g', 't',
130
    ';',
131
};
132
133
static const signed char xmlEscapeTab[128] = {
134
     0,  0,  0,  0,  0,  0,  0,  0,  0, -1, -1,  0,  0, 20,  0,  0,
135
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
136
    -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1,
137
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 44, -1,
138
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
139
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
140
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
141
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
142
};
143
144
static const signed char xmlEscapeTabAttr[128] = {
145
     0,  0,  0,  0,  0,  0,  0,  0,  0,  9, 14,  0,  0, 20,  0,  0,
146
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
147
    -1, -1, 26, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1,
148
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 44, -1,
149
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
150
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
151
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
152
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
153
};
154
155
static void
156
xmlSerializeText(xmlOutputBufferPtr buf, const xmlChar *string,
157
54.6k
                 unsigned flags) {
158
54.6k
    const char *cur;
159
54.6k
    const signed char *tab;
160
161
54.6k
    if (string == NULL)
162
0
        return;
163
164
54.6k
    if (flags & XML_ESCAPE_ATTR)
165
54.6k
        tab = xmlEscapeTabAttr;
166
0
    else
167
0
        tab = xmlEscapeTab;
168
169
54.6k
    cur = (const char *) string;
170
171
54.6k
    while (*cur != 0) {
172
36.3k
        const char *base;
173
36.3k
        int c;
174
36.3k
        int offset;
175
176
36.3k
        base = cur;
177
36.3k
        offset = -1;
178
179
1.04M
        while (1) {
180
1.04M
            c = (unsigned char) *cur;
181
182
1.04M
            if (c < 0x80) {
183
1.04M
                offset = tab[c];
184
1.04M
                if (offset >= 0)
185
36.3k
                    break;
186
1.04M
            } else if (flags & XML_ESCAPE_NON_ASCII) {
187
0
                break;
188
0
            }
189
190
1.01M
            cur += 1;
191
1.01M
        }
192
193
36.3k
        if (cur > base)
194
36.3k
            xmlOutputBufferWrite(buf, cur - base, base);
195
196
36.3k
        if (offset >= 0) {
197
36.3k
            if (c == 0)
198
36.3k
                break;
199
200
0
            xmlOutputBufferWrite(buf, xmlEscapeContent[offset],
201
0
                                 &xmlEscapeContent[offset+1]);
202
0
            cur += 1;
203
0
        } else {
204
0
            char tempBuf[12];
205
0
            int tempSize;
206
0
            int val = 0, len = 4;
207
208
0
            val = xmlGetUTF8Char((const xmlChar *) cur, &len);
209
0
            if (val < 0) {
210
0
                val = 0xFFFD;
211
0
                cur += 1;
212
0
            } else {
213
0
                if (!IS_CHAR(val))
214
0
                    val = 0xFFFD;
215
0
                cur += len;
216
0
            }
217
218
0
            tempSize = xmlSerializeHexCharRef(tempBuf, val);
219
0
            xmlOutputBufferWrite(buf, tempSize, tempBuf);
220
0
        }
221
36.3k
    }
222
54.6k
}
223
224
/************************************************************************
225
 *                  *
226
 *      Allocation and deallocation     *
227
 *                  *
228
 ************************************************************************/
229
230
/**
231
 * xmlSaveSetIndentString:
232
 * @ctxt:  save context
233
 * @indent:  indent string
234
 *
235
 * Sets the indent string.
236
 *
237
 * Available since 2.14.0.
238
 *
239
 * Returns 0 on success, -1 if the string is NULL, empty or too long.
240
 */
241
int
242
0
xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt, const char *indent) {
243
0
    size_t len;
244
0
    int i;
245
246
0
    if ((ctxt == NULL) || (indent == NULL))
247
0
        return(-1);
248
249
0
    len = strlen(indent);
250
0
    if ((len <= 0) || (len > MAX_INDENT))
251
0
        return(-1);
252
253
0
    ctxt->indent_size = len;
254
0
    ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
255
0
    for (i = 0; i < ctxt->indent_nr; i++)
256
0
        memcpy(&ctxt->indent[i * ctxt->indent_size], indent, len);
257
258
0
    return(0);
259
0
}
260
261
/**
262
 * xmlSaveCtxtInit:
263
 * @ctxt: the saving context
264
 *
265
 * Initialize a saving context
266
 */
267
static void
268
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt, int options)
269
0
{
270
0
    if (ctxt == NULL) return;
271
272
0
    xmlSaveSetIndentString(ctxt, xmlTreeIndentString);
273
274
0
    if (options & XML_SAVE_FORMAT)
275
0
        ctxt->format = 1;
276
0
    else if (options & XML_SAVE_WSNONSIG)
277
0
        ctxt->format = 2;
278
279
0
    if (((options & XML_SAVE_EMPTY) == 0) &&
280
0
        (xmlSaveNoEmptyTags))
281
0
  options |= XML_SAVE_NO_EMPTY;
282
283
0
    ctxt->options = options;
284
0
}
285
286
/**
287
 * xmlFreeSaveCtxt:
288
 *
289
 * Free a saving context, destroying the output in any remaining buffer
290
 */
291
static void
292
xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
293
0
{
294
0
    if (ctxt == NULL) return;
295
0
    if (ctxt->encoding != NULL)
296
0
        xmlFree((char *) ctxt->encoding);
297
0
    if (ctxt->buf != NULL)
298
0
        xmlOutputBufferClose(ctxt->buf);
299
0
    xmlFree(ctxt);
300
0
}
301
302
/**
303
 * xmlNewSaveCtxt:
304
 *
305
 * Create a new saving context
306
 *
307
 * Returns the new structure or NULL in case of error
308
 */
309
static xmlSaveCtxtPtr
310
xmlNewSaveCtxt(const char *encoding, int options)
311
0
{
312
0
    xmlSaveCtxtPtr ret;
313
314
0
    ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
315
0
    if (ret == NULL) {
316
0
  xmlSaveErrMemory(NULL);
317
0
  return ( NULL );
318
0
    }
319
0
    memset(ret, 0, sizeof(xmlSaveCtxt));
320
321
0
    if (encoding != NULL) {
322
0
        xmlParserErrors res;
323
324
0
        res = xmlOpenCharEncodingHandler(encoding, /* output */ 1,
325
0
                                         &ret->handler);
326
0
  if (res != XML_ERR_OK) {
327
0
      xmlSaveErr(NULL, res, NULL, encoding);
328
0
            xmlFreeSaveCtxt(ret);
329
0
      return(NULL);
330
0
  }
331
0
        ret->encoding = xmlStrdup((const xmlChar *)encoding);
332
0
    }
333
334
0
    xmlSaveCtxtInit(ret, options);
335
336
0
    return(ret);
337
0
}
338
339
/************************************************************************
340
 *                  *
341
 *    Dumping XML tree content to a simple buffer   *
342
 *                  *
343
 ************************************************************************/
344
345
static void
346
0
xmlSaveWriteText(xmlSaveCtxt *ctxt, const xmlChar *text, unsigned flags) {
347
0
    if (ctxt->encoding == NULL)
348
0
        flags |= XML_ESCAPE_NON_ASCII;
349
350
0
    xmlSerializeText(ctxt->buf, text, flags);
351
0
}
352
353
/**
354
 * xmlSaveWriteAttrContent:
355
 * @ctxt:  save context
356
 * @attr:  the attribute pointer
357
 *
358
 * Serialize the attribute in the buffer
359
 */
360
static void
361
xmlSaveWriteAttrContent(xmlSaveCtxt *ctxt, xmlAttrPtr attr)
362
0
{
363
0
    xmlNodePtr children;
364
0
    xmlOutputBufferPtr buf = ctxt->buf;
365
366
0
    children = attr->children;
367
0
    while (children != NULL) {
368
0
        switch (children->type) {
369
0
            case XML_TEXT_NODE:
370
0
          xmlSaveWriteText(ctxt, children->content, XML_ESCAPE_ATTR);
371
0
    break;
372
0
            case XML_ENTITY_REF_NODE:
373
0
                xmlOutputBufferWrite(buf, 1, "&");
374
0
                xmlOutputBufferWriteString(buf, (const char *) children->name);
375
0
                xmlOutputBufferWrite(buf, 1, ";");
376
0
                break;
377
0
            default:
378
                /* should not happen unless we have a badly built tree */
379
0
                break;
380
0
        }
381
0
        children = children->next;
382
0
    }
383
0
}
384
385
/**
386
 * xmlBufDumpNotationDecl:
387
 * @buf:  the XML buffer output
388
 * @nota:  A notation declaration
389
 *
390
 * This will dump the content the notation declaration as an XML DTD definition
391
 */
392
static void
393
0
xmlBufDumpNotationDecl(xmlOutputBufferPtr buf, xmlNotationPtr nota) {
394
0
    xmlOutputBufferWrite(buf, 11, "<!NOTATION ");
395
0
    xmlOutputBufferWriteString(buf, (const char *) nota->name);
396
397
0
    if (nota->PublicID != NULL) {
398
0
  xmlOutputBufferWrite(buf, 8, " PUBLIC ");
399
0
  xmlOutputBufferWriteQuotedString(buf, nota->PublicID);
400
0
  if (nota->SystemID != NULL) {
401
0
      xmlOutputBufferWrite(buf, 1, " ");
402
0
      xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
403
0
  }
404
0
    } else {
405
0
  xmlOutputBufferWrite(buf, 8, " SYSTEM ");
406
0
  xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
407
0
    }
408
409
0
    xmlOutputBufferWrite(buf, 3, " >\n");
410
0
}
411
412
/**
413
 * xmlBufDumpNotationDeclScan:
414
 * @nota:  A notation declaration
415
 * @buf:  the XML buffer output
416
 *
417
 * This is called with the hash scan function, and just reverses args
418
 */
419
static void
420
xmlBufDumpNotationDeclScan(void *nota, void *buf,
421
0
                           const xmlChar *name ATTRIBUTE_UNUSED) {
422
0
    xmlBufDumpNotationDecl((xmlOutputBufferPtr) buf, (xmlNotationPtr) nota);
423
0
}
424
425
/**
426
 * xmlBufDumpNotationTable:
427
 * @buf:  an xmlBufPtr output
428
 * @table:  A notation table
429
 *
430
 * This will dump the content of the notation table as an XML DTD definition
431
 */
432
static void
433
0
xmlBufDumpNotationTable(xmlOutputBufferPtr buf, xmlNotationTablePtr table) {
434
0
    xmlHashScan(table, xmlBufDumpNotationDeclScan, buf);
435
0
}
436
437
/**
438
 * xmlBufDumpElementOccur:
439
 * @buf:  output buffer
440
 * @cur:  element table
441
 *
442
 * Dump the occurrence operator of an element.
443
 */
444
static void
445
0
xmlBufDumpElementOccur(xmlOutputBufferPtr buf, xmlElementContentPtr cur) {
446
0
    switch (cur->ocur) {
447
0
        case XML_ELEMENT_CONTENT_ONCE:
448
0
            break;
449
0
        case XML_ELEMENT_CONTENT_OPT:
450
0
            xmlOutputBufferWrite(buf, 1, "?");
451
0
            break;
452
0
        case XML_ELEMENT_CONTENT_MULT:
453
0
            xmlOutputBufferWrite(buf, 1, "*");
454
0
            break;
455
0
        case XML_ELEMENT_CONTENT_PLUS:
456
0
            xmlOutputBufferWrite(buf, 1, "+");
457
0
            break;
458
0
    }
459
0
}
460
461
/**
462
 * xmlBufDumpElementContent:
463
 * @buf:  output buffer
464
 * @content:  element table
465
 *
466
 * This will dump the content of the element table as an XML DTD definition
467
 */
468
static void
469
xmlBufDumpElementContent(xmlOutputBufferPtr buf,
470
0
                         xmlElementContentPtr content) {
471
0
    xmlElementContentPtr cur;
472
473
0
    if (content == NULL) return;
474
475
0
    xmlOutputBufferWrite(buf, 1, "(");
476
0
    cur = content;
477
478
0
    do {
479
0
        if (cur == NULL) return;
480
481
0
        switch (cur->type) {
482
0
            case XML_ELEMENT_CONTENT_PCDATA:
483
0
                xmlOutputBufferWrite(buf, 7, "#PCDATA");
484
0
                break;
485
0
            case XML_ELEMENT_CONTENT_ELEMENT:
486
0
                if (cur->prefix != NULL) {
487
0
                    xmlOutputBufferWriteString(buf,
488
0
                            (const char *) cur->prefix);
489
0
                    xmlOutputBufferWrite(buf, 1, ":");
490
0
                }
491
0
                xmlOutputBufferWriteString(buf, (const char *) cur->name);
492
0
                break;
493
0
            case XML_ELEMENT_CONTENT_SEQ:
494
0
            case XML_ELEMENT_CONTENT_OR:
495
0
                if ((cur != content) &&
496
0
                    (cur->parent != NULL) &&
497
0
                    ((cur->type != cur->parent->type) ||
498
0
                     (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
499
0
                    xmlOutputBufferWrite(buf, 1, "(");
500
0
                cur = cur->c1;
501
0
                continue;
502
0
        }
503
504
0
        while (cur != content) {
505
0
            xmlElementContentPtr parent = cur->parent;
506
507
0
            if (parent == NULL) return;
508
509
0
            if (((cur->type == XML_ELEMENT_CONTENT_OR) ||
510
0
                 (cur->type == XML_ELEMENT_CONTENT_SEQ)) &&
511
0
                ((cur->type != parent->type) ||
512
0
                 (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
513
0
                xmlOutputBufferWrite(buf, 1, ")");
514
0
            xmlBufDumpElementOccur(buf, cur);
515
516
0
            if (cur == parent->c1) {
517
0
                if (parent->type == XML_ELEMENT_CONTENT_SEQ)
518
0
                    xmlOutputBufferWrite(buf, 3, " , ");
519
0
                else if (parent->type == XML_ELEMENT_CONTENT_OR)
520
0
                    xmlOutputBufferWrite(buf, 3, " | ");
521
522
0
                cur = parent->c2;
523
0
                break;
524
0
            }
525
526
0
            cur = parent;
527
0
        }
528
0
    } while (cur != content);
529
530
0
    xmlOutputBufferWrite(buf, 1, ")");
531
0
    xmlBufDumpElementOccur(buf, content);
532
0
}
533
534
/**
535
 * xmlBufDumpElementDecl:
536
 * @buf:  an xmlBufPtr output
537
 * @elem:  An element table
538
 *
539
 * This will dump the content of the element declaration as an XML
540
 * DTD definition
541
 */
542
static void
543
0
xmlBufDumpElementDecl(xmlOutputBufferPtr buf, xmlElementPtr elem) {
544
0
    xmlOutputBufferWrite(buf, 10, "<!ELEMENT ");
545
0
    if (elem->prefix != NULL) {
546
0
        xmlOutputBufferWriteString(buf, (const char *) elem->prefix);
547
0
        xmlOutputBufferWrite(buf, 1, ":");
548
0
    }
549
0
    xmlOutputBufferWriteString(buf, (const char *) elem->name);
550
0
    xmlOutputBufferWrite(buf, 1, " ");
551
552
0
    switch (elem->etype) {
553
0
  case XML_ELEMENT_TYPE_EMPTY:
554
0
      xmlOutputBufferWrite(buf, 5, "EMPTY");
555
0
      break;
556
0
  case XML_ELEMENT_TYPE_ANY:
557
0
      xmlOutputBufferWrite(buf, 3, "ANY");
558
0
      break;
559
0
  case XML_ELEMENT_TYPE_MIXED:
560
0
  case XML_ELEMENT_TYPE_ELEMENT:
561
0
      xmlBufDumpElementContent(buf, elem->content);
562
0
      break;
563
0
        default:
564
            /* assert(0); */
565
0
            break;
566
0
    }
567
568
0
    xmlOutputBufferWrite(buf, 2, ">\n");
569
0
}
570
571
/**
572
 * xmlBufDumpEnumeration:
573
 * @buf:  output buffer
574
 * @enum:  An enumeration
575
 *
576
 * This will dump the content of the enumeration
577
 */
578
static void
579
0
xmlBufDumpEnumeration(xmlOutputBufferPtr buf, xmlEnumerationPtr cur) {
580
0
    while (cur != NULL) {
581
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
582
0
        if (cur->next != NULL)
583
0
            xmlOutputBufferWrite(buf, 3, " | ");
584
585
0
        cur = cur->next;
586
0
    }
587
588
0
    xmlOutputBufferWrite(buf, 1, ")");
589
0
}
590
/**
591
 * xmlBufDumpAttributeDecl:
592
 * @buf:  output buffer
593
 * @attr:  An attribute declaration
594
 *
595
 * This will dump the content of the attribute declaration as an XML
596
 * DTD definition
597
 */
598
static void
599
0
xmlSaveWriteAttributeDecl(xmlSaveCtxtPtr ctxt, xmlAttributePtr attr) {
600
0
    xmlOutputBufferPtr buf = ctxt->buf;
601
602
0
    xmlOutputBufferWrite(buf, 10, "<!ATTLIST ");
603
0
    xmlOutputBufferWriteString(buf, (const char *) attr->elem);
604
0
    xmlOutputBufferWrite(buf, 1, " ");
605
0
    if (attr->prefix != NULL) {
606
0
  xmlOutputBufferWriteString(buf, (const char *) attr->prefix);
607
0
  xmlOutputBufferWrite(buf, 1, ":");
608
0
    }
609
0
    xmlOutputBufferWriteString(buf, (const char *) attr->name);
610
611
0
    switch (attr->atype) {
612
0
  case XML_ATTRIBUTE_CDATA:
613
0
      xmlOutputBufferWrite(buf, 6, " CDATA");
614
0
      break;
615
0
  case XML_ATTRIBUTE_ID:
616
0
      xmlOutputBufferWrite(buf, 3, " ID");
617
0
      break;
618
0
  case XML_ATTRIBUTE_IDREF:
619
0
      xmlOutputBufferWrite(buf, 6, " IDREF");
620
0
      break;
621
0
  case XML_ATTRIBUTE_IDREFS:
622
0
      xmlOutputBufferWrite(buf, 7, " IDREFS");
623
0
      break;
624
0
  case XML_ATTRIBUTE_ENTITY:
625
0
      xmlOutputBufferWrite(buf, 7, " ENTITY");
626
0
      break;
627
0
  case XML_ATTRIBUTE_ENTITIES:
628
0
      xmlOutputBufferWrite(buf, 9, " ENTITIES");
629
0
      break;
630
0
  case XML_ATTRIBUTE_NMTOKEN:
631
0
      xmlOutputBufferWrite(buf, 8, " NMTOKEN");
632
0
      break;
633
0
  case XML_ATTRIBUTE_NMTOKENS:
634
0
      xmlOutputBufferWrite(buf, 9, " NMTOKENS");
635
0
      break;
636
0
  case XML_ATTRIBUTE_ENUMERATION:
637
0
      xmlOutputBufferWrite(buf, 2, " (");
638
0
      xmlBufDumpEnumeration(buf, attr->tree);
639
0
      break;
640
0
  case XML_ATTRIBUTE_NOTATION:
641
0
      xmlOutputBufferWrite(buf, 11, " NOTATION (");
642
0
      xmlBufDumpEnumeration(buf, attr->tree);
643
0
      break;
644
0
  default:
645
            /* assert(0); */
646
0
            break;
647
0
    }
648
649
0
    switch (attr->def) {
650
0
  case XML_ATTRIBUTE_NONE:
651
0
      break;
652
0
  case XML_ATTRIBUTE_REQUIRED:
653
0
      xmlOutputBufferWrite(buf, 10, " #REQUIRED");
654
0
      break;
655
0
  case XML_ATTRIBUTE_IMPLIED:
656
0
      xmlOutputBufferWrite(buf, 9, " #IMPLIED");
657
0
      break;
658
0
  case XML_ATTRIBUTE_FIXED:
659
0
      xmlOutputBufferWrite(buf, 7, " #FIXED");
660
0
      break;
661
0
  default:
662
            /* assert(0); */
663
0
            break;
664
0
    }
665
666
0
    if (attr->defaultValue != NULL) {
667
0
        xmlOutputBufferWrite(buf, 2, " \"");
668
0
        xmlSaveWriteText(ctxt, attr->defaultValue, XML_ESCAPE_ATTR);
669
0
        xmlOutputBufferWrite(buf, 1, "\"");
670
0
    }
671
672
0
    xmlOutputBufferWrite(buf, 2, ">\n");
673
0
}
674
675
/**
676
 * xmlBufDumpEntityContent:
677
 * @buf:  output buffer
678
 * @content:  entity content.
679
 *
680
 * This will dump the quoted string value, taking care of the special
681
 * treatment required by %
682
 */
683
static void
684
0
xmlBufDumpEntityContent(xmlOutputBufferPtr buf, const xmlChar *content) {
685
0
    if (xmlStrchr(content, '%')) {
686
0
        const char * base, *cur;
687
688
0
  xmlOutputBufferWrite(buf, 1, "\"");
689
0
  base = cur = (const char *) content;
690
0
  while (*cur != 0) {
691
0
      if (*cur == '"') {
692
0
    if (base != cur)
693
0
        xmlOutputBufferWrite(buf, cur - base, base);
694
0
    xmlOutputBufferWrite(buf, 6, "&quot;");
695
0
    cur++;
696
0
    base = cur;
697
0
      } else if (*cur == '%') {
698
0
    if (base != cur)
699
0
        xmlOutputBufferWrite(buf, cur - base, base);
700
0
    xmlOutputBufferWrite(buf, 6, "&#x25;");
701
0
    cur++;
702
0
    base = cur;
703
0
      } else {
704
0
    cur++;
705
0
      }
706
0
  }
707
0
  if (base != cur)
708
0
      xmlOutputBufferWrite(buf, cur - base, base);
709
0
  xmlOutputBufferWrite(buf, 1, "\"");
710
0
    } else {
711
0
        xmlOutputBufferWriteQuotedString(buf, content);
712
0
    }
713
0
}
714
715
/**
716
 * xmlBufDumpEntityDecl:
717
 * @buf:  an xmlBufPtr output
718
 * @ent:  An entity table
719
 *
720
 * This will dump the content of the entity table as an XML DTD definition
721
 */
722
static void
723
0
xmlBufDumpEntityDecl(xmlOutputBufferPtr buf, xmlEntityPtr ent) {
724
0
    if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) ||
725
0
        (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY))
726
0
        xmlOutputBufferWrite(buf, 11, "<!ENTITY % ");
727
0
    else
728
0
        xmlOutputBufferWrite(buf, 9, "<!ENTITY ");
729
0
    xmlOutputBufferWriteString(buf, (const char *) ent->name);
730
0
    xmlOutputBufferWrite(buf, 1, " ");
731
732
0
    if ((ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) ||
733
0
        (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) ||
734
0
        (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
735
0
        if (ent->ExternalID != NULL) {
736
0
             xmlOutputBufferWrite(buf, 7, "PUBLIC ");
737
0
             xmlOutputBufferWriteQuotedString(buf, ent->ExternalID);
738
0
             xmlOutputBufferWrite(buf, 1, " ");
739
0
        } else {
740
0
             xmlOutputBufferWrite(buf, 7, "SYSTEM ");
741
0
        }
742
0
        xmlOutputBufferWriteQuotedString(buf, ent->SystemID);
743
0
    }
744
745
0
    if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
746
0
        if (ent->content != NULL) { /* Should be true ! */
747
0
            xmlOutputBufferWrite(buf, 7, " NDATA ");
748
0
            if (ent->orig != NULL)
749
0
                xmlOutputBufferWriteString(buf, (const char *) ent->orig);
750
0
            else
751
0
                xmlOutputBufferWriteString(buf, (const char *) ent->content);
752
0
        }
753
0
    }
754
755
0
    if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
756
0
        (ent->etype == XML_INTERNAL_PARAMETER_ENTITY)) {
757
0
        if (ent->orig != NULL)
758
0
            xmlOutputBufferWriteQuotedString(buf, ent->orig);
759
0
        else
760
0
            xmlBufDumpEntityContent(buf, ent->content);
761
0
    }
762
763
0
    xmlOutputBufferWrite(buf, 2, ">\n");
764
0
}
765
766
/************************************************************************
767
 *                  *
768
 *    Dumping XML tree content to an I/O output buffer  *
769
 *                  *
770
 ************************************************************************/
771
772
0
static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
773
0
    xmlOutputBufferPtr buf = ctxt->buf;
774
775
0
    if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
776
0
        xmlCharEncodingHandler *handler;
777
0
        xmlParserErrors res;
778
779
0
  res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
780
0
        if (res != XML_ERR_OK) {
781
0
            xmlSaveErr(buf, res, NULL, encoding);
782
0
            return(-1);
783
0
        }
784
785
0
        if (handler != NULL) {
786
0
            buf->conv = xmlBufCreate(4000 /* MINLEN */);
787
0
            if (buf->conv == NULL) {
788
0
                xmlCharEncCloseFunc(handler);
789
0
                xmlSaveErrMemory(buf);
790
0
                return(-1);
791
0
            }
792
0
            buf->encoder = handler;
793
0
        }
794
795
0
        ctxt->encoding = (const xmlChar *) encoding;
796
797
  /*
798
   * initialize the state, e.g. if outputting a BOM
799
   */
800
0
        xmlCharEncOutput(buf, 1);
801
0
    }
802
0
    return(0);
803
0
}
804
805
0
static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
806
0
    xmlOutputBufferPtr buf = ctxt->buf;
807
808
0
    xmlOutputBufferFlush(buf);
809
0
    xmlCharEncCloseFunc(buf->encoder);
810
0
    xmlBufFree(buf->conv);
811
0
    buf->encoder = NULL;
812
0
    buf->conv = NULL;
813
814
0
    ctxt->encoding = NULL;
815
816
0
    return(0);
817
0
}
818
819
#ifdef LIBXML_HTML_ENABLED
820
static void
821
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
822
#endif
823
static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
824
static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
825
826
static void
827
xmlSaveWriteIndent(xmlSaveCtxtPtr ctxt, int extra)
828
0
{
829
0
    int level;
830
831
0
    if ((ctxt->options & XML_SAVE_NO_INDENT) ||
832
0
        (((ctxt->options & XML_SAVE_INDENT) == 0) &&
833
0
         (xmlIndentTreeOutput == 0)))
834
0
        return;
835
836
0
    level = ctxt->level + extra;
837
0
    if (level > ctxt->indent_nr)
838
0
        level = ctxt->indent_nr;
839
0
    xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size * level, ctxt->indent);
840
0
}
841
842
/**
843
 * xmlOutputBufferWriteWSNonSig:
844
 * @ctxt:  The save context
845
 * @extra: Number of extra indents to apply to ctxt->level
846
 *
847
 * Write out formatting for non-significant whitespace output.
848
 */
849
static void
850
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
851
0
{
852
0
    int i;
853
0
    if ((ctxt == NULL) || (ctxt->buf == NULL))
854
0
        return;
855
0
    xmlOutputBufferWrite(ctxt->buf, 1, "\n");
856
0
    for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
857
0
        xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
858
0
                ((ctxt->level + extra - i) > ctxt->indent_nr ?
859
0
                 ctxt->indent_nr : (ctxt->level + extra - i)),
860
0
                ctxt->indent);
861
0
    }
862
0
}
863
864
/**
865
 * xmlNsDumpOutput:
866
 * @buf:  the XML buffer output
867
 * @cur:  a namespace
868
 * @ctxt: the output save context. Optional.
869
 *
870
 * Dump a local Namespace definition.
871
 * Should be called in the context of attributes dumps.
872
 * If @ctxt is supplied, @buf should be its buffer.
873
 */
874
static void
875
0
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
876
0
    unsigned escapeFlags = XML_ESCAPE_ATTR;
877
878
0
    if ((cur == NULL) || (buf == NULL)) return;
879
880
0
    if ((ctxt == NULL) || (ctxt->encoding == NULL))
881
0
        escapeFlags |= XML_ESCAPE_NON_ASCII;
882
883
0
    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
884
0
  if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
885
0
      return;
886
887
0
  if (ctxt != NULL && ctxt->format == 2)
888
0
      xmlOutputBufferWriteWSNonSig(ctxt, 2);
889
0
  else
890
0
      xmlOutputBufferWrite(buf, 1, " ");
891
892
        /* Within the context of an element attributes */
893
0
  if (cur->prefix != NULL) {
894
0
      xmlOutputBufferWrite(buf, 6, "xmlns:");
895
0
      xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
896
0
  } else
897
0
      xmlOutputBufferWrite(buf, 5, "xmlns");
898
0
        xmlOutputBufferWrite(buf, 2, "=\"");
899
0
        xmlSerializeText(buf, cur->href, escapeFlags);
900
0
        xmlOutputBufferWrite(buf, 1, "\"");
901
0
    }
902
0
}
903
904
/**
905
 * xmlNsListDumpOutputCtxt
906
 * @ctxt: the save context
907
 * @cur:  the first namespace
908
 *
909
 * Dump a list of local namespace definitions to a save context.
910
 * Should be called in the context of attribute dumps.
911
 */
912
static void
913
0
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
914
0
    while (cur != NULL) {
915
0
        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
916
0
  cur = cur->next;
917
0
    }
918
0
}
919
920
/**
921
 * xmlNsListDumpOutput:
922
 * @buf:  the XML buffer output
923
 * @cur:  the first namespace
924
 *
925
 * Dump a list of local Namespace definitions.
926
 * Should be called in the context of attributes dumps.
927
 */
928
void
929
0
xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
930
0
    while (cur != NULL) {
931
0
        xmlNsDumpOutput(buf, cur, NULL);
932
0
  cur = cur->next;
933
0
    }
934
0
}
935
936
/**
937
 * xmlDtdDumpOutput:
938
 * @buf:  the XML buffer output
939
 * @dtd:  the pointer to the DTD
940
 *
941
 * Dump the XML document DTD, if any.
942
 */
943
static void
944
0
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
945
0
    xmlOutputBufferPtr buf;
946
0
    xmlNodePtr cur;
947
0
    int format, level;
948
949
0
    if (dtd == NULL) return;
950
0
    if ((ctxt == NULL) || (ctxt->buf == NULL))
951
0
        return;
952
0
    buf = ctxt->buf;
953
0
    xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
954
0
    xmlOutputBufferWriteString(buf, (const char *)dtd->name);
955
0
    if (dtd->ExternalID != NULL) {
956
0
  xmlOutputBufferWrite(buf, 8, " PUBLIC ");
957
0
  xmlOutputBufferWriteQuotedString(buf, dtd->ExternalID);
958
0
  xmlOutputBufferWrite(buf, 1, " ");
959
0
  xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
960
0
    }  else if (dtd->SystemID != NULL) {
961
0
  xmlOutputBufferWrite(buf, 8, " SYSTEM ");
962
0
  xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
963
0
    }
964
0
    if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
965
0
        (dtd->attributes == NULL) && (dtd->notations == NULL) &&
966
0
  (dtd->pentities == NULL)) {
967
0
  xmlOutputBufferWrite(buf, 1, ">");
968
0
  return;
969
0
    }
970
0
    xmlOutputBufferWrite(buf, 3, " [\n");
971
    /*
972
     * Dump the notations first they are not in the DTD children list
973
     * Do this only on a standalone DTD or on the internal subset though.
974
     */
975
0
    if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
976
0
        (dtd->doc->intSubset == dtd))) {
977
0
        xmlBufDumpNotationTable(buf, (xmlNotationTablePtr) dtd->notations);
978
0
    }
979
0
    format = ctxt->format;
980
0
    level = ctxt->level;
981
0
    ctxt->format = 0;
982
0
    ctxt->level = -1;
983
0
    for (cur = dtd->children; cur != NULL; cur = cur->next) {
984
0
        xmlNodeDumpOutputInternal(ctxt, cur);
985
0
    }
986
0
    ctxt->format = format;
987
0
    ctxt->level = level;
988
0
    xmlOutputBufferWrite(buf, 2, "]>");
989
0
}
990
991
/**
992
 * xmlAttrDumpOutput:
993
 * @buf:  the XML buffer output
994
 * @cur:  the attribute pointer
995
 *
996
 * Dump an XML attribute
997
 */
998
static void
999
0
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1000
0
    xmlOutputBufferPtr buf;
1001
1002
0
    if (cur == NULL) return;
1003
0
    buf = ctxt->buf;
1004
0
    if (buf == NULL) return;
1005
0
    if (ctxt->format == 2)
1006
0
        xmlOutputBufferWriteWSNonSig(ctxt, 2);
1007
0
    else
1008
0
        xmlOutputBufferWrite(buf, 1, " ");
1009
0
    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1010
0
        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1011
0
  xmlOutputBufferWrite(buf, 1, ":");
1012
0
    }
1013
0
    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1014
0
    xmlOutputBufferWrite(buf, 2, "=\"");
1015
0
#ifdef LIBXML_HTML_ENABLED
1016
0
    if ((ctxt->options & XML_SAVE_XHTML) &&
1017
0
        (cur->ns == NULL) &&
1018
0
        ((cur->children == NULL) ||
1019
0
         (cur->children->content == NULL) ||
1020
0
         (cur->children->content[0] == 0)) &&
1021
0
        (htmlIsBooleanAttr(cur->name))) {
1022
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
1023
0
    } else
1024
0
#endif
1025
0
    {
1026
0
        xmlSaveWriteAttrContent(ctxt, cur);
1027
0
    }
1028
0
    xmlOutputBufferWrite(buf, 1, "\"");
1029
0
}
1030
1031
#ifdef LIBXML_HTML_ENABLED
1032
/**
1033
 * htmlNodeDumpOutputInternal:
1034
 * @cur:  the current node
1035
 *
1036
 * Dump an HTML node, recursive behaviour, children are printed too.
1037
 */
1038
static int
1039
0
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1040
0
    const xmlChar *oldctxtenc = ctxt->encoding;
1041
0
    const xmlChar *encoding = ctxt->encoding;
1042
0
    xmlOutputBufferPtr buf = ctxt->buf;
1043
0
    int switched_encoding = 0;
1044
0
    xmlDocPtr doc;
1045
1046
0
    xmlInitParser();
1047
1048
0
    doc = cur->doc;
1049
0
    if ((encoding == NULL) && (doc != NULL))
1050
0
  encoding = doc->encoding;
1051
1052
0
    if ((encoding != NULL) && (doc != NULL))
1053
0
  htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
1054
0
    if ((encoding == NULL) && (doc != NULL))
1055
0
  encoding = htmlGetMetaEncoding(doc);
1056
0
    if (encoding == NULL)
1057
0
  encoding = BAD_CAST "HTML";
1058
0
    if ((encoding != NULL) && (oldctxtenc == NULL) &&
1059
0
  (buf->encoder == NULL) && (buf->conv == NULL)) {
1060
0
  if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0)
1061
0
      return(-1);
1062
0
  switched_encoding = 1;
1063
0
    }
1064
0
    if (ctxt->options & XML_SAVE_FORMAT)
1065
0
  htmlNodeDumpFormatOutput(buf, doc, cur,
1066
0
               (const char *)encoding, 1);
1067
0
    else
1068
0
  htmlNodeDumpFormatOutput(buf, doc, cur,
1069
0
               (const char *)encoding, 0);
1070
    /*
1071
     * Restore the state of the saving context at the end of the document
1072
     */
1073
0
    if ((switched_encoding) && (oldctxtenc == NULL)) {
1074
0
  xmlSaveClearEncoding(ctxt);
1075
0
    }
1076
0
    return(0);
1077
0
}
1078
#endif
1079
1080
/**
1081
 * xmlNodeDumpOutputInternal:
1082
 * @cur:  the current node
1083
 *
1084
 * Dump an XML node, recursive behaviour, children are printed too.
1085
 */
1086
static void
1087
0
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1088
0
    int format = ctxt->format;
1089
0
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1090
0
    xmlAttrPtr attr;
1091
0
    xmlChar *start, *end;
1092
0
    xmlOutputBufferPtr buf;
1093
1094
0
    if (cur == NULL) return;
1095
0
    buf = ctxt->buf;
1096
1097
0
    root = cur;
1098
0
    parent = cur->parent;
1099
0
    while (1) {
1100
0
        switch (cur->type) {
1101
0
        case XML_DOCUMENT_NODE:
1102
0
        case XML_HTML_DOCUMENT_NODE:
1103
0
      xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1104
0
      break;
1105
1106
0
        case XML_DTD_NODE:
1107
0
            xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1108
0
            break;
1109
1110
0
        case XML_DOCUMENT_FRAG_NODE:
1111
            /* Always validate cur->parent when descending. */
1112
0
            if ((cur->parent == parent) && (cur->children != NULL)) {
1113
0
                parent = cur;
1114
0
                cur = cur->children;
1115
0
                continue;
1116
0
            }
1117
0
      break;
1118
1119
0
        case XML_ELEMENT_DECL:
1120
0
            xmlBufDumpElementDecl(buf, (xmlElementPtr) cur);
1121
0
            break;
1122
1123
0
        case XML_ATTRIBUTE_DECL:
1124
0
            xmlSaveWriteAttributeDecl(ctxt, (xmlAttributePtr) cur);
1125
0
            break;
1126
1127
0
        case XML_ENTITY_DECL:
1128
0
            xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur);
1129
0
            break;
1130
1131
0
        case XML_ELEMENT_NODE:
1132
0
      if ((cur != root) && (ctxt->format == 1))
1133
0
                xmlSaveWriteIndent(ctxt, 0);
1134
1135
            /*
1136
             * Some users like lxml are known to pass nodes with a corrupted
1137
             * tree structure. Fall back to a recursive call to handle this
1138
             * case.
1139
             */
1140
0
            if ((cur->parent != parent) && (cur->children != NULL)) {
1141
0
                xmlNodeDumpOutputInternal(ctxt, cur);
1142
0
                break;
1143
0
            }
1144
1145
0
            xmlOutputBufferWrite(buf, 1, "<");
1146
0
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1147
0
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1148
0
                xmlOutputBufferWrite(buf, 1, ":");
1149
0
            }
1150
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1151
0
            if (cur->nsDef)
1152
0
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1153
0
            for (attr = cur->properties; attr != NULL; attr = attr->next)
1154
0
                xmlAttrDumpOutput(ctxt, attr);
1155
1156
0
            if (cur->children == NULL) {
1157
0
                if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
1158
0
                    if (ctxt->format == 2)
1159
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1160
0
                    xmlOutputBufferWrite(buf, 2, "/>");
1161
0
                } else {
1162
0
                    if (ctxt->format == 2)
1163
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 1);
1164
0
                    xmlOutputBufferWrite(buf, 3, "></");
1165
0
                    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1166
0
                        xmlOutputBufferWriteString(buf,
1167
0
                                (const char *)cur->ns->prefix);
1168
0
                        xmlOutputBufferWrite(buf, 1, ":");
1169
0
                    }
1170
0
                    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1171
0
                    if (ctxt->format == 2)
1172
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1173
0
                    xmlOutputBufferWrite(buf, 1, ">");
1174
0
                }
1175
0
            } else {
1176
0
                if (ctxt->format == 1) {
1177
0
                    tmp = cur->children;
1178
0
                    while (tmp != NULL) {
1179
0
                        if ((tmp->type == XML_TEXT_NODE) ||
1180
0
                            (tmp->type == XML_CDATA_SECTION_NODE) ||
1181
0
                            (tmp->type == XML_ENTITY_REF_NODE)) {
1182
0
                            ctxt->format = 0;
1183
0
                            unformattedNode = cur;
1184
0
                            break;
1185
0
                        }
1186
0
                        tmp = tmp->next;
1187
0
                    }
1188
0
                }
1189
0
                if (ctxt->format == 2)
1190
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 1);
1191
0
                xmlOutputBufferWrite(buf, 1, ">");
1192
0
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1193
0
                if (ctxt->level >= 0) ctxt->level++;
1194
0
                parent = cur;
1195
0
                cur = cur->children;
1196
0
                continue;
1197
0
            }
1198
1199
0
            break;
1200
1201
0
        case XML_TEXT_NODE:
1202
0
      if (cur->content == NULL)
1203
0
                break;
1204
0
      if (cur->name != xmlStringTextNoenc) {
1205
0
                if (ctxt->escape)
1206
0
                    xmlOutputBufferWriteEscape(buf, cur->content,
1207
0
                                               ctxt->escape);
1208
#ifdef TEST_OUTPUT_BUFFER_WRITE_ESCAPE
1209
                else if (ctxt->encoding)
1210
                    xmlOutputBufferWriteEscape(buf, cur->content, NULL);
1211
#endif
1212
0
                else
1213
0
                    xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1214
0
      } else {
1215
    /*
1216
     * Disable escaping, needed for XSLT
1217
     */
1218
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1219
0
      }
1220
0
      break;
1221
1222
0
        case XML_PI_NODE:
1223
0
      if ((cur != root) && (ctxt->format == 1))
1224
0
                xmlSaveWriteIndent(ctxt, 0);
1225
1226
0
            if (cur->content != NULL) {
1227
0
                xmlOutputBufferWrite(buf, 2, "<?");
1228
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1229
0
                if (cur->content != NULL) {
1230
0
                    if (ctxt->format == 2)
1231
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1232
0
                    else
1233
0
                        xmlOutputBufferWrite(buf, 1, " ");
1234
0
                    xmlOutputBufferWriteString(buf,
1235
0
                            (const char *)cur->content);
1236
0
                }
1237
0
                xmlOutputBufferWrite(buf, 2, "?>");
1238
0
            } else {
1239
0
                xmlOutputBufferWrite(buf, 2, "<?");
1240
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1241
0
                if (ctxt->format == 2)
1242
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1243
0
                xmlOutputBufferWrite(buf, 2, "?>");
1244
0
            }
1245
0
            break;
1246
1247
0
        case XML_COMMENT_NODE:
1248
0
      if ((cur != root) && (ctxt->format == 1))
1249
0
                xmlSaveWriteIndent(ctxt, 0);
1250
1251
0
            if (cur->content != NULL) {
1252
0
                xmlOutputBufferWrite(buf, 4, "<!--");
1253
0
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1254
0
                xmlOutputBufferWrite(buf, 3, "-->");
1255
0
            }
1256
0
            break;
1257
1258
0
        case XML_ENTITY_REF_NODE:
1259
0
            xmlOutputBufferWrite(buf, 1, "&");
1260
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1261
0
            xmlOutputBufferWrite(buf, 1, ";");
1262
0
            break;
1263
1264
0
        case XML_CDATA_SECTION_NODE:
1265
0
            if (cur->content == NULL || *cur->content == '\0') {
1266
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1267
0
            } else {
1268
0
                start = end = cur->content;
1269
0
                while (*end != '\0') {
1270
0
                    if ((*end == ']') && (*(end + 1) == ']') &&
1271
0
                        (*(end + 2) == '>')) {
1272
0
                        end = end + 2;
1273
0
                        xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1274
0
                        xmlOutputBufferWrite(buf, end - start,
1275
0
                                (const char *)start);
1276
0
                        xmlOutputBufferWrite(buf, 3, "]]>");
1277
0
                        start = end;
1278
0
                    }
1279
0
                    end++;
1280
0
                }
1281
0
                if (start != end) {
1282
0
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1283
0
                    xmlOutputBufferWriteString(buf, (const char *)start);
1284
0
                    xmlOutputBufferWrite(buf, 3, "]]>");
1285
0
                }
1286
0
            }
1287
0
            break;
1288
1289
0
        case XML_ATTRIBUTE_NODE:
1290
0
            xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1291
0
            break;
1292
1293
0
        case XML_NAMESPACE_DECL:
1294
0
            xmlNsDumpOutput(buf, (xmlNsPtr) cur, ctxt);
1295
0
            break;
1296
1297
0
        default:
1298
0
            break;
1299
0
        }
1300
1301
0
        while (1) {
1302
0
            if (cur == root)
1303
0
                return;
1304
0
            if ((ctxt->format == 1) &&
1305
0
                (cur->type != XML_XINCLUDE_START) &&
1306
0
                (cur->type != XML_XINCLUDE_END))
1307
0
                xmlOutputBufferWrite(buf, 1, "\n");
1308
0
            if (cur->next != NULL) {
1309
0
                cur = cur->next;
1310
0
                break;
1311
0
            }
1312
1313
0
            cur = parent;
1314
            /* cur->parent was validated when descending. */
1315
0
            parent = cur->parent;
1316
1317
0
            if (cur->type == XML_ELEMENT_NODE) {
1318
0
                if (ctxt->level > 0) ctxt->level--;
1319
0
                if (ctxt->format == 1)
1320
0
                    xmlSaveWriteIndent(ctxt, 0);
1321
1322
0
                xmlOutputBufferWrite(buf, 2, "</");
1323
0
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1324
0
                    xmlOutputBufferWriteString(buf,
1325
0
                            (const char *)cur->ns->prefix);
1326
0
                    xmlOutputBufferWrite(buf, 1, ":");
1327
0
                }
1328
1329
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1330
0
                if (ctxt->format == 2)
1331
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1332
0
                xmlOutputBufferWrite(buf, 1, ">");
1333
1334
0
                if (cur == unformattedNode) {
1335
0
                    ctxt->format = format;
1336
0
                    unformattedNode = NULL;
1337
0
                }
1338
0
            }
1339
0
        }
1340
0
    }
1341
0
}
1342
1343
/**
1344
 * xmlDocContentDumpOutput:
1345
 * @cur:  the document
1346
 *
1347
 * Dump an XML document.
1348
 */
1349
static int
1350
0
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1351
0
#ifdef LIBXML_HTML_ENABLED
1352
0
    xmlDtdPtr dtd;
1353
0
    int is_xhtml = 0;
1354
0
#endif
1355
0
    const xmlChar *oldctxtenc = ctxt->encoding;
1356
0
    const xmlChar *encoding = ctxt->encoding;
1357
0
    xmlOutputBufferPtr buf = ctxt->buf;
1358
0
    int switched_encoding = 0;
1359
1360
0
    xmlInitParser();
1361
1362
0
    if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1363
0
        (cur->type != XML_DOCUMENT_NODE))
1364
0
   return(-1);
1365
1366
0
    if (ctxt->encoding == NULL)
1367
0
  encoding = cur->encoding;
1368
1369
0
    if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1370
0
         ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1371
0
         ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1372
0
        (ctxt->options & XML_SAVE_AS_HTML)) {
1373
0
#ifdef LIBXML_HTML_ENABLED
1374
0
        if (encoding != NULL)
1375
0
      htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1376
0
        if (encoding == NULL)
1377
0
      encoding = htmlGetMetaEncoding(cur);
1378
0
        if (encoding == NULL)
1379
0
      encoding = BAD_CAST "HTML";
1380
0
  if ((encoding != NULL) && (oldctxtenc == NULL) &&
1381
0
      (buf->encoder == NULL) && (buf->conv == NULL)) {
1382
0
      if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1383
0
    return(-1);
1384
0
      }
1385
0
            switched_encoding = 1;
1386
0
  }
1387
0
        if (ctxt->options & XML_SAVE_FORMAT)
1388
0
      htmlDocContentDumpFormatOutput(buf, cur,
1389
0
                                     (const char *)encoding, 1);
1390
0
  else
1391
0
      htmlDocContentDumpFormatOutput(buf, cur,
1392
0
                                     (const char *)encoding, 0);
1393
#else
1394
        return(-1);
1395
#endif
1396
0
    } else if ((cur->type == XML_DOCUMENT_NODE) ||
1397
0
               (ctxt->options & XML_SAVE_AS_XML) ||
1398
0
               (ctxt->options & XML_SAVE_XHTML)) {
1399
0
  if ((encoding != NULL) && (oldctxtenc == NULL) &&
1400
0
      (buf->encoder == NULL) && (buf->conv == NULL) &&
1401
0
      ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1402
            /*
1403
             * we need to switch to this encoding but just for this
1404
             * document since we output the XMLDecl the conversion
1405
             * must be done to not generate not well formed documents.
1406
             */
1407
0
            if (xmlSaveSwitchEncoding(ctxt, (const char *) encoding) < 0)
1408
0
                return(-1);
1409
0
            switched_encoding = 1;
1410
0
  }
1411
1412
1413
  /*
1414
   * Save the XML declaration
1415
   */
1416
0
  if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1417
0
      xmlOutputBufferWrite(buf, 14, "<?xml version=");
1418
0
      if (cur->version != NULL)
1419
0
    xmlOutputBufferWriteQuotedString(buf, cur->version);
1420
0
      else
1421
0
    xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1422
0
      if (encoding != NULL) {
1423
0
    xmlOutputBufferWrite(buf, 10, " encoding=");
1424
0
    xmlOutputBufferWriteQuotedString(buf, (xmlChar *) encoding);
1425
0
      }
1426
0
      switch (cur->standalone) {
1427
0
    case 0:
1428
0
        xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1429
0
        break;
1430
0
    case 1:
1431
0
        xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1432
0
        break;
1433
0
      }
1434
0
      xmlOutputBufferWrite(buf, 3, "?>\n");
1435
0
  }
1436
1437
0
#ifdef LIBXML_HTML_ENABLED
1438
0
        if (ctxt->options & XML_SAVE_XHTML)
1439
0
            is_xhtml = 1;
1440
0
  if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1441
0
      dtd = xmlGetIntSubset(cur);
1442
0
      if (dtd != NULL) {
1443
0
    is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1444
0
    if (is_xhtml < 0) is_xhtml = 0;
1445
0
      }
1446
0
  }
1447
0
#endif
1448
0
  if (cur->children != NULL) {
1449
0
      xmlNodePtr child = cur->children;
1450
1451
0
      while (child != NULL) {
1452
0
    ctxt->level = 0;
1453
0
#ifdef LIBXML_HTML_ENABLED
1454
0
    if (is_xhtml)
1455
0
        xhtmlNodeDumpOutput(ctxt, child);
1456
0
    else
1457
0
#endif
1458
0
        xmlNodeDumpOutputInternal(ctxt, child);
1459
0
                if ((child->type != XML_XINCLUDE_START) &&
1460
0
                    (child->type != XML_XINCLUDE_END))
1461
0
                    xmlOutputBufferWrite(buf, 1, "\n");
1462
0
    child = child->next;
1463
0
      }
1464
0
  }
1465
0
    }
1466
1467
    /*
1468
     * Restore the state of the saving context at the end of the document
1469
     */
1470
0
    if ((switched_encoding) && (oldctxtenc == NULL)) {
1471
0
  xmlSaveClearEncoding(ctxt);
1472
0
    }
1473
0
    return(0);
1474
0
}
1475
1476
#ifdef LIBXML_HTML_ENABLED
1477
/************************************************************************
1478
 *                  *
1479
 *    Functions specific to XHTML serialization   *
1480
 *                  *
1481
 ************************************************************************/
1482
1483
/**
1484
 * xhtmlIsEmpty:
1485
 * @node:  the node
1486
 *
1487
 * Check if a node is an empty xhtml node
1488
 *
1489
 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1490
 */
1491
static int
1492
0
xhtmlIsEmpty(xmlNodePtr node) {
1493
0
    if (node == NULL)
1494
0
  return(-1);
1495
0
    if (node->type != XML_ELEMENT_NODE)
1496
0
  return(0);
1497
0
    if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1498
0
  return(0);
1499
0
    if (node->children != NULL)
1500
0
  return(0);
1501
0
    switch (node->name ? node->name[0] : 0) {
1502
0
  case 'a':
1503
0
      if (xmlStrEqual(node->name, BAD_CAST "area"))
1504
0
    return(1);
1505
0
      return(0);
1506
0
  case 'b':
1507
0
      if (xmlStrEqual(node->name, BAD_CAST "br"))
1508
0
    return(1);
1509
0
      if (xmlStrEqual(node->name, BAD_CAST "base"))
1510
0
    return(1);
1511
0
      if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1512
0
    return(1);
1513
0
      return(0);
1514
0
  case 'c':
1515
0
      if (xmlStrEqual(node->name, BAD_CAST "col"))
1516
0
    return(1);
1517
0
      return(0);
1518
0
  case 'f':
1519
0
      if (xmlStrEqual(node->name, BAD_CAST "frame"))
1520
0
    return(1);
1521
0
      return(0);
1522
0
  case 'h':
1523
0
      if (xmlStrEqual(node->name, BAD_CAST "hr"))
1524
0
    return(1);
1525
0
      return(0);
1526
0
  case 'i':
1527
0
      if (xmlStrEqual(node->name, BAD_CAST "img"))
1528
0
    return(1);
1529
0
      if (xmlStrEqual(node->name, BAD_CAST "input"))
1530
0
    return(1);
1531
0
      if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1532
0
    return(1);
1533
0
      return(0);
1534
0
  case 'l':
1535
0
      if (xmlStrEqual(node->name, BAD_CAST "link"))
1536
0
    return(1);
1537
0
      return(0);
1538
0
  case 'm':
1539
0
      if (xmlStrEqual(node->name, BAD_CAST "meta"))
1540
0
    return(1);
1541
0
      return(0);
1542
0
  case 'p':
1543
0
      if (xmlStrEqual(node->name, BAD_CAST "param"))
1544
0
    return(1);
1545
0
      return(0);
1546
0
    }
1547
0
    return(0);
1548
0
}
1549
1550
/**
1551
 * xhtmlAttrListDumpOutput:
1552
 * @cur:  the first attribute pointer
1553
 *
1554
 * Dump a list of XML attributes
1555
 */
1556
static void
1557
0
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1558
0
    xmlAttrPtr xml_lang = NULL;
1559
0
    xmlAttrPtr lang = NULL;
1560
0
    xmlAttrPtr name = NULL;
1561
0
    xmlAttrPtr id = NULL;
1562
0
    xmlNodePtr parent;
1563
0
    xmlOutputBufferPtr buf;
1564
1565
0
    if (cur == NULL) return;
1566
0
    buf = ctxt->buf;
1567
0
    parent = cur->parent;
1568
0
    while (cur != NULL) {
1569
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1570
0
      id = cur;
1571
0
  else
1572
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1573
0
      name = cur;
1574
0
  else
1575
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1576
0
      lang = cur;
1577
0
  else
1578
0
  if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1579
0
      (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1580
0
      xml_lang = cur;
1581
0
        xmlAttrDumpOutput(ctxt, cur);
1582
0
  cur = cur->next;
1583
0
    }
1584
    /*
1585
     * C.8
1586
     */
1587
0
    if ((name != NULL) && (id == NULL)) {
1588
0
  if ((parent != NULL) && (parent->name != NULL) &&
1589
0
      ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1590
0
       (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1591
0
       (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1592
0
       (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1593
0
       (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1594
0
       (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1595
0
       (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1596
0
       (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1597
0
       (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1598
0
      xmlOutputBufferWrite(buf, 5, " id=\"");
1599
0
            xmlSaveWriteAttrContent(ctxt, name);
1600
0
      xmlOutputBufferWrite(buf, 1, "\"");
1601
0
  }
1602
0
    }
1603
    /*
1604
     * C.7.
1605
     */
1606
0
    if ((lang != NULL) && (xml_lang == NULL)) {
1607
0
  xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1608
0
        xmlSaveWriteAttrContent(ctxt, lang);
1609
0
  xmlOutputBufferWrite(buf, 1, "\"");
1610
0
    } else
1611
0
    if ((xml_lang != NULL) && (lang == NULL)) {
1612
0
  xmlOutputBufferWrite(buf, 7, " lang=\"");
1613
0
        xmlSaveWriteAttrContent(ctxt, xml_lang);
1614
0
  xmlOutputBufferWrite(buf, 1, "\"");
1615
0
    }
1616
0
}
1617
1618
/**
1619
 * xhtmlNodeDumpOutput:
1620
 * @buf:  the XML buffer output
1621
 * @doc:  the XHTML document
1622
 * @cur:  the current node
1623
 * @level: the imbrication level for indenting
1624
 * @format: is formatting allowed
1625
 * @encoding:  an optional encoding string
1626
 *
1627
 * Dump an XHTML node, recursive behaviour, children are printed too.
1628
 */
1629
static void
1630
0
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1631
0
    int format = ctxt->format, addmeta, oldoptions;
1632
0
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1633
0
    xmlChar *start, *end;
1634
0
    xmlOutputBufferPtr buf = ctxt->buf;
1635
1636
0
    if (cur == NULL) return;
1637
1638
0
    oldoptions = ctxt->options;
1639
0
    ctxt->options |= XML_SAVE_XHTML;
1640
1641
0
    root = cur;
1642
0
    parent = cur->parent;
1643
0
    while (1) {
1644
0
        switch (cur->type) {
1645
0
        case XML_DOCUMENT_NODE:
1646
0
        case XML_HTML_DOCUMENT_NODE:
1647
0
            xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1648
0
      break;
1649
1650
0
        case XML_NAMESPACE_DECL:
1651
0
      xmlNsDumpOutput(buf, (xmlNsPtr) cur, ctxt);
1652
0
      break;
1653
1654
0
        case XML_DTD_NODE:
1655
0
            xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1656
0
      break;
1657
1658
0
        case XML_DOCUMENT_FRAG_NODE:
1659
            /* Always validate cur->parent when descending. */
1660
0
            if ((cur->parent == parent) && (cur->children != NULL)) {
1661
0
                parent = cur;
1662
0
                cur = cur->children;
1663
0
                continue;
1664
0
            }
1665
0
            break;
1666
1667
0
        case XML_ELEMENT_DECL:
1668
0
            xmlBufDumpElementDecl(buf, (xmlElementPtr) cur);
1669
0
      break;
1670
1671
0
        case XML_ATTRIBUTE_DECL:
1672
0
            xmlSaveWriteAttributeDecl(ctxt, (xmlAttributePtr) cur);
1673
0
      break;
1674
1675
0
        case XML_ENTITY_DECL:
1676
0
            xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur);
1677
0
      break;
1678
1679
0
        case XML_ELEMENT_NODE:
1680
0
            addmeta = 0;
1681
1682
0
      if ((cur != root) && (ctxt->format == 1))
1683
0
                xmlSaveWriteIndent(ctxt, 0);
1684
1685
            /*
1686
             * Some users like lxml are known to pass nodes with a corrupted
1687
             * tree structure. Fall back to a recursive call to handle this
1688
             * case.
1689
             */
1690
0
            if ((cur->parent != parent) && (cur->children != NULL)) {
1691
0
                xhtmlNodeDumpOutput(ctxt, cur);
1692
0
                break;
1693
0
            }
1694
1695
0
            xmlOutputBufferWrite(buf, 1, "<");
1696
0
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1697
0
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1698
0
                xmlOutputBufferWrite(buf, 1, ":");
1699
0
            }
1700
1701
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1702
0
            if (cur->nsDef)
1703
0
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1704
0
            if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1705
0
                (cur->ns == NULL) && (cur->nsDef == NULL))) {
1706
                /*
1707
                 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1708
                 */
1709
0
                xmlOutputBufferWriteString(buf,
1710
0
                        " xmlns=\"http://www.w3.org/1999/xhtml\"");
1711
0
            }
1712
0
            if (cur->properties != NULL)
1713
0
                xhtmlAttrListDumpOutput(ctxt, cur->properties);
1714
1715
0
            if ((parent != NULL) &&
1716
0
                (parent->parent == (xmlNodePtr) cur->doc) &&
1717
0
                xmlStrEqual(cur->name, BAD_CAST"head") &&
1718
0
                xmlStrEqual(parent->name, BAD_CAST"html")) {
1719
1720
0
                tmp = cur->children;
1721
0
                while (tmp != NULL) {
1722
0
                    if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1723
0
                        int res;
1724
0
                        xmlChar *httpequiv;
1725
1726
0
                        res = xmlNodeGetAttrValue(tmp, BAD_CAST "http-equiv",
1727
0
                                                  NULL, &httpequiv);
1728
0
                        if (res < 0) {
1729
0
                            xmlSaveErrMemory(buf);
1730
0
                        } else if (res == 0) {
1731
0
                            if (xmlStrcasecmp(httpequiv,
1732
0
                                        BAD_CAST"Content-Type") == 0) {
1733
0
                                xmlFree(httpequiv);
1734
0
                                break;
1735
0
                            }
1736
0
                            xmlFree(httpequiv);
1737
0
                        }
1738
0
                    }
1739
0
                    tmp = tmp->next;
1740
0
                }
1741
0
                if (tmp == NULL)
1742
0
                    addmeta = 1;
1743
0
            }
1744
1745
0
            if (cur->children == NULL) {
1746
0
                if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1747
0
                    ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1748
                    /*
1749
                     * C.2. Empty Elements
1750
                     */
1751
0
                    xmlOutputBufferWrite(buf, 3, " />");
1752
0
                } else {
1753
0
                    if (addmeta == 1) {
1754
0
                        xmlOutputBufferWrite(buf, 1, ">");
1755
0
                        if (ctxt->format == 1) {
1756
0
                            xmlOutputBufferWrite(buf, 1, "\n");
1757
0
                            xmlSaveWriteIndent(ctxt, 1);
1758
0
                        }
1759
0
                        xmlOutputBufferWriteString(buf,
1760
0
                                "<meta http-equiv=\"Content-Type\" "
1761
0
                                "content=\"text/html; charset=");
1762
0
                        if (ctxt->encoding) {
1763
0
                            xmlOutputBufferWriteString(buf,
1764
0
                                    (const char *)ctxt->encoding);
1765
0
                        } else {
1766
0
                            xmlOutputBufferWrite(buf, 5, "UTF-8");
1767
0
                        }
1768
0
                        xmlOutputBufferWrite(buf, 4, "\" />");
1769
0
                        if (ctxt->format == 1)
1770
0
                            xmlOutputBufferWrite(buf, 1, "\n");
1771
0
                    } else {
1772
0
                        xmlOutputBufferWrite(buf, 1, ">");
1773
0
                    }
1774
                    /*
1775
                     * C.3. Element Minimization and Empty Element Content
1776
                     */
1777
0
                    xmlOutputBufferWrite(buf, 2, "</");
1778
0
                    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1779
0
                        xmlOutputBufferWriteString(buf,
1780
0
                                (const char *)cur->ns->prefix);
1781
0
                        xmlOutputBufferWrite(buf, 1, ":");
1782
0
                    }
1783
0
                    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1784
0
                    xmlOutputBufferWrite(buf, 1, ">");
1785
0
                }
1786
0
            } else {
1787
0
                xmlOutputBufferWrite(buf, 1, ">");
1788
0
                if (addmeta == 1) {
1789
0
                    if (ctxt->format == 1) {
1790
0
                        xmlOutputBufferWrite(buf, 1, "\n");
1791
0
                        xmlSaveWriteIndent(ctxt, 1);
1792
0
                    }
1793
0
                    xmlOutputBufferWriteString(buf,
1794
0
                            "<meta http-equiv=\"Content-Type\" "
1795
0
                            "content=\"text/html; charset=");
1796
0
                    if (ctxt->encoding) {
1797
0
                        xmlOutputBufferWriteString(buf,
1798
0
                                (const char *)ctxt->encoding);
1799
0
                    } else {
1800
0
                        xmlOutputBufferWrite(buf, 5, "UTF-8");
1801
0
                    }
1802
0
                    xmlOutputBufferWrite(buf, 4, "\" />");
1803
0
                }
1804
1805
0
                if (ctxt->format == 1) {
1806
0
                    tmp = cur->children;
1807
0
                    while (tmp != NULL) {
1808
0
                        if ((tmp->type == XML_TEXT_NODE) ||
1809
0
                            (tmp->type == XML_ENTITY_REF_NODE)) {
1810
0
                            unformattedNode = cur;
1811
0
                            ctxt->format = 0;
1812
0
                            break;
1813
0
                        }
1814
0
                        tmp = tmp->next;
1815
0
                    }
1816
0
                }
1817
1818
0
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1819
0
                if (ctxt->level >= 0) ctxt->level++;
1820
0
                parent = cur;
1821
0
                cur = cur->children;
1822
0
                continue;
1823
0
            }
1824
1825
0
            break;
1826
1827
0
        case XML_TEXT_NODE:
1828
0
      if (cur->content == NULL)
1829
0
                break;
1830
0
      if ((cur->name == xmlStringText) ||
1831
0
    (cur->name != xmlStringTextNoenc)) {
1832
0
                if (ctxt->escape)
1833
0
                    xmlOutputBufferWriteEscape(buf, cur->content,
1834
0
                                               ctxt->escape);
1835
0
                else
1836
0
                    xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1837
0
      } else {
1838
    /*
1839
     * Disable escaping, needed for XSLT
1840
     */
1841
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1842
0
      }
1843
0
      break;
1844
1845
0
        case XML_PI_NODE:
1846
0
            if (cur->content != NULL) {
1847
0
                xmlOutputBufferWrite(buf, 2, "<?");
1848
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1849
0
                if (cur->content != NULL) {
1850
0
                    xmlOutputBufferWrite(buf, 1, " ");
1851
0
                    xmlOutputBufferWriteString(buf,
1852
0
                            (const char *)cur->content);
1853
0
                }
1854
0
                xmlOutputBufferWrite(buf, 2, "?>");
1855
0
            } else {
1856
0
                xmlOutputBufferWrite(buf, 2, "<?");
1857
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1858
0
                xmlOutputBufferWrite(buf, 2, "?>");
1859
0
            }
1860
0
            break;
1861
1862
0
        case XML_COMMENT_NODE:
1863
0
            if (cur->content != NULL) {
1864
0
                xmlOutputBufferWrite(buf, 4, "<!--");
1865
0
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1866
0
                xmlOutputBufferWrite(buf, 3, "-->");
1867
0
            }
1868
0
            break;
1869
1870
0
        case XML_ENTITY_REF_NODE:
1871
0
            xmlOutputBufferWrite(buf, 1, "&");
1872
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1873
0
            xmlOutputBufferWrite(buf, 1, ";");
1874
0
            break;
1875
1876
0
        case XML_CDATA_SECTION_NODE:
1877
0
            if (cur->content == NULL || *cur->content == '\0') {
1878
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1879
0
            } else {
1880
0
                start = end = cur->content;
1881
0
                while (*end != '\0') {
1882
0
                    if (*end == ']' && *(end + 1) == ']' &&
1883
0
                        *(end + 2) == '>') {
1884
0
                        end = end + 2;
1885
0
                        xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1886
0
                        xmlOutputBufferWrite(buf, end - start,
1887
0
                                (const char *)start);
1888
0
                        xmlOutputBufferWrite(buf, 3, "]]>");
1889
0
                        start = end;
1890
0
                    }
1891
0
                    end++;
1892
0
                }
1893
0
                if (start != end) {
1894
0
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1895
0
                    xmlOutputBufferWriteString(buf, (const char *)start);
1896
0
                    xmlOutputBufferWrite(buf, 3, "]]>");
1897
0
                }
1898
0
            }
1899
0
            break;
1900
1901
0
        case XML_ATTRIBUTE_NODE:
1902
0
            xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1903
0
      break;
1904
1905
0
        default:
1906
0
            break;
1907
0
        }
1908
1909
0
        while (1) {
1910
0
            if (cur == root)
1911
0
                return;
1912
0
            if (ctxt->format == 1)
1913
0
                xmlOutputBufferWrite(buf, 1, "\n");
1914
0
            if (cur->next != NULL) {
1915
0
                cur = cur->next;
1916
0
                break;
1917
0
            }
1918
1919
0
            cur = parent;
1920
            /* cur->parent was validated when descending. */
1921
0
            parent = cur->parent;
1922
1923
0
            if (cur->type == XML_ELEMENT_NODE) {
1924
0
                if (ctxt->level > 0) ctxt->level--;
1925
0
                if (ctxt->format == 1)
1926
0
                    xmlSaveWriteIndent(ctxt, 0);
1927
1928
0
                xmlOutputBufferWrite(buf, 2, "</");
1929
0
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1930
0
                    xmlOutputBufferWriteString(buf,
1931
0
                            (const char *)cur->ns->prefix);
1932
0
                    xmlOutputBufferWrite(buf, 1, ":");
1933
0
                }
1934
1935
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1936
0
                xmlOutputBufferWrite(buf, 1, ">");
1937
1938
0
                if (cur == unformattedNode) {
1939
0
                    ctxt->format = format;
1940
0
                    unformattedNode = NULL;
1941
0
                }
1942
0
            }
1943
0
        }
1944
0
    }
1945
1946
0
    ctxt->options = oldoptions;
1947
0
}
1948
#endif
1949
1950
/************************************************************************
1951
 *                  *
1952
 *      Public entry points       *
1953
 *                  *
1954
 ************************************************************************/
1955
1956
/**
1957
 * xmlSaveToFd:
1958
 * @fd:  a file descriptor number
1959
 * @encoding:  the encoding name to use or NULL
1960
 * @options:  a set of xmlSaveOptions
1961
 *
1962
 * Create a document saving context serializing to a file descriptor
1963
 * with the encoding and the options given.
1964
 *
1965
 * Returns a new serialization context or NULL in case of error.
1966
 */
1967
xmlSaveCtxtPtr
1968
xmlSaveToFd(int fd, const char *encoding, int options)
1969
0
{
1970
0
    xmlSaveCtxtPtr ret;
1971
1972
0
    ret = xmlNewSaveCtxt(encoding, options);
1973
0
    if (ret == NULL) return(NULL);
1974
0
    ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1975
0
    if (ret->buf == NULL) {
1976
0
        xmlCharEncCloseFunc(ret->handler);
1977
0
  xmlFreeSaveCtxt(ret);
1978
0
  return(NULL);
1979
0
    }
1980
0
    return(ret);
1981
0
}
1982
1983
/**
1984
 * xmlSaveToFilename:
1985
 * @filename:  a file name or an URL
1986
 * @encoding:  the encoding name to use or NULL
1987
 * @options:  a set of xmlSaveOptions
1988
 *
1989
 * Create a document saving context serializing to a filename or possibly
1990
 * to an URL (but this is less reliable) with the encoding and the options
1991
 * given.
1992
 *
1993
 * Returns a new serialization context or NULL in case of error.
1994
 */
1995
xmlSaveCtxtPtr
1996
xmlSaveToFilename(const char *filename, const char *encoding, int options)
1997
0
{
1998
0
    xmlSaveCtxtPtr ret;
1999
0
    int compression = 0; /* TODO handle compression option */
2000
2001
0
    ret = xmlNewSaveCtxt(encoding, options);
2002
0
    if (ret == NULL) return(NULL);
2003
0
    ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
2004
0
                                             compression);
2005
0
    if (ret->buf == NULL) {
2006
0
        xmlCharEncCloseFunc(ret->handler);
2007
0
  xmlFreeSaveCtxt(ret);
2008
0
  return(NULL);
2009
0
    }
2010
0
    return(ret);
2011
0
}
2012
2013
/**
2014
 * xmlSaveToBuffer:
2015
 * @buffer:  a buffer
2016
 * @encoding:  the encoding name to use or NULL
2017
 * @options:  a set of xmlSaveOptions
2018
 *
2019
 * Create a document saving context serializing to a buffer
2020
 * with the encoding and the options given
2021
 *
2022
 * Returns a new serialization context or NULL in case of error.
2023
 */
2024
2025
xmlSaveCtxtPtr
2026
xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
2027
0
{
2028
0
    xmlSaveCtxtPtr ret;
2029
2030
0
    ret = xmlNewSaveCtxt(encoding, options);
2031
0
    if (ret == NULL) return(NULL);
2032
0
    ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
2033
0
    if (ret->buf == NULL) {
2034
0
        xmlCharEncCloseFunc(ret->handler);
2035
0
  xmlFreeSaveCtxt(ret);
2036
0
  return(NULL);
2037
0
    }
2038
0
    return(ret);
2039
0
}
2040
2041
/**
2042
 * xmlSaveToIO:
2043
 * @iowrite:  an I/O write function
2044
 * @ioclose:  an I/O close function
2045
 * @ioctx:  an I/O handler
2046
 * @encoding:  the encoding name to use or NULL
2047
 * @options:  a set of xmlSaveOptions
2048
 *
2049
 * Create a document saving context serializing to a file descriptor
2050
 * with the encoding and the options given
2051
 *
2052
 * Returns a new serialization context or NULL in case of error.
2053
 */
2054
xmlSaveCtxtPtr
2055
xmlSaveToIO(xmlOutputWriteCallback iowrite,
2056
            xmlOutputCloseCallback ioclose,
2057
            void *ioctx, const char *encoding, int options)
2058
0
{
2059
0
    xmlSaveCtxtPtr ret;
2060
2061
0
    ret = xmlNewSaveCtxt(encoding, options);
2062
0
    if (ret == NULL) return(NULL);
2063
0
    ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
2064
0
    if (ret->buf == NULL) {
2065
0
        xmlCharEncCloseFunc(ret->handler);
2066
0
  xmlFreeSaveCtxt(ret);
2067
0
  return(NULL);
2068
0
    }
2069
0
    return(ret);
2070
0
}
2071
2072
/**
2073
 * xmlSaveDoc:
2074
 * @ctxt:  a document saving context
2075
 * @doc:  a document
2076
 *
2077
 * Save a full document to a saving context
2078
 * TODO: The function is not fully implemented yet as it does not return the
2079
 * byte count but 0 instead
2080
 *
2081
 * Returns the number of byte written or -1 in case of error
2082
 */
2083
long
2084
xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
2085
0
{
2086
0
    long ret = 0;
2087
2088
0
    if ((ctxt == NULL) || (doc == NULL)) return(-1);
2089
0
    if (xmlDocContentDumpOutput(ctxt, doc) < 0)
2090
0
        return(-1);
2091
0
    return(ret);
2092
0
}
2093
2094
/**
2095
 * xmlSaveTree:
2096
 * @ctxt:  a document saving context
2097
 * @cur:  the top node of the subtree to save
2098
 *
2099
 * Save a subtree starting at the node parameter to a saving context
2100
 * TODO: The function is not fully implemented yet as it does not return the
2101
 * byte count but 0 instead
2102
 *
2103
 * Returns the number of byte written or -1 in case of error
2104
 */
2105
long
2106
xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
2107
0
{
2108
0
    long ret = 0;
2109
2110
0
    if ((ctxt == NULL) || (cur == NULL)) return(-1);
2111
0
#ifdef LIBXML_HTML_ENABLED
2112
0
    if (ctxt->options & XML_SAVE_XHTML) {
2113
0
        xhtmlNodeDumpOutput(ctxt, cur);
2114
0
        return(ret);
2115
0
    }
2116
0
    if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
2117
0
         (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
2118
0
         ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
2119
0
        (ctxt->options & XML_SAVE_AS_HTML)) {
2120
0
  htmlNodeDumpOutputInternal(ctxt, cur);
2121
0
  return(ret);
2122
0
    }
2123
0
#endif
2124
0
    xmlNodeDumpOutputInternal(ctxt, cur);
2125
0
    return(ret);
2126
0
}
2127
2128
/**
2129
 * xmlSaveNotationDecl:
2130
 * @ctxt:  save context
2131
 * @cur:  notation
2132
 *
2133
 * Serialize a notation declaration.
2134
 *
2135
 * Return 0 on succes, -1 on error.
2136
 */
2137
int
2138
0
xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt, xmlNotationPtr cur) {
2139
0
    if (ctxt == NULL)
2140
0
        return(-1);
2141
0
    xmlBufDumpNotationDecl(ctxt->buf, cur);
2142
0
    return(0);
2143
0
}
2144
2145
/**
2146
 * xmlSaveNotationTable:
2147
 * @ctxt:  save context
2148
 * @cur:  notation table
2149
 *
2150
 * Serialize notation declarations of a document.
2151
 *
2152
 * Return 0 on succes, -1 on error.
2153
 */
2154
int
2155
0
xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur) {
2156
0
    if (ctxt == NULL)
2157
0
        return(-1);
2158
0
    xmlBufDumpNotationTable(ctxt->buf, cur);
2159
0
    return(0);
2160
0
}
2161
2162
/**
2163
 * xmlSaveFlush:
2164
 * @ctxt:  a document saving context
2165
 *
2166
 * Flush a document saving context, i.e. make sure that all bytes have
2167
 * been output.
2168
 *
2169
 * Returns the number of byte written or -1 in case of error.
2170
 */
2171
int
2172
xmlSaveFlush(xmlSaveCtxtPtr ctxt)
2173
0
{
2174
0
    if (ctxt == NULL) return(-1);
2175
0
    if (ctxt->buf == NULL) return(-1);
2176
0
    return(xmlOutputBufferFlush(ctxt->buf));
2177
0
}
2178
2179
/**
2180
 * xmlSaveClose:
2181
 * @ctxt:  a document saving context
2182
 *
2183
 * Close a document saving context, i.e. make sure that all bytes have
2184
 * been output and free the associated data.
2185
 *
2186
 * Returns the number of byte written or -1 in case of error.
2187
 */
2188
int
2189
xmlSaveClose(xmlSaveCtxtPtr ctxt)
2190
0
{
2191
0
    int ret;
2192
2193
0
    if (ctxt == NULL) return(-1);
2194
0
    ret = xmlSaveFlush(ctxt);
2195
0
    xmlFreeSaveCtxt(ctxt);
2196
0
    return(ret);
2197
0
}
2198
2199
/**
2200
 * xmlSaveFinish:
2201
 * @ctxt:  a document saving context
2202
 *
2203
 * Close a document saving context, i.e. make sure that all bytes have
2204
 * been output and free the associated data.
2205
 *
2206
 * Available since 2.13.0.
2207
 *
2208
 * Returns an xmlParserErrors code.
2209
 */
2210
xmlParserErrors
2211
xmlSaveFinish(xmlSaveCtxtPtr ctxt)
2212
0
{
2213
0
    int ret;
2214
2215
0
    if (ctxt == NULL)
2216
0
        return(XML_ERR_INTERNAL_ERROR);
2217
2218
0
    ret = xmlOutputBufferClose(ctxt->buf);
2219
0
    ctxt->buf = NULL;
2220
0
    if (ret < 0)
2221
0
        ret = -ret;
2222
0
    else
2223
0
        ret = XML_ERR_OK;
2224
2225
0
    xmlFreeSaveCtxt(ctxt);
2226
0
    return(ret);
2227
0
}
2228
2229
/**
2230
 * xmlSaveSetEscape:
2231
 * @ctxt:  a document saving context
2232
 * @escape:  the escaping function
2233
 *
2234
 * DEPRECATED: Don't use.
2235
 *
2236
 * Set a custom escaping function to be used for text in element content
2237
 *
2238
 * Returns 0 if successful or -1 in case of error.
2239
 */
2240
int
2241
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2242
0
{
2243
0
    if (ctxt == NULL) return(-1);
2244
0
    ctxt->escape = escape;
2245
0
    return(0);
2246
0
}
2247
2248
/**
2249
 * xmlSaveSetAttrEscape:
2250
 * @ctxt:  a document saving context
2251
 * @escape:  the escaping function
2252
 *
2253
 * DEPRECATED: Don't use.
2254
 *
2255
 * Has no effect.
2256
 *
2257
 * Returns 0 if successful or -1 in case of error.
2258
 */
2259
int
2260
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,
2261
                     xmlCharEncodingOutputFunc escape ATTRIBUTE_UNUSED)
2262
0
{
2263
0
    if (ctxt == NULL) return(-1);
2264
0
    return(0);
2265
0
}
2266
2267
/************************************************************************
2268
 *                  *
2269
 *    Public entry points based on buffers      *
2270
 *                  *
2271
 ************************************************************************/
2272
2273
/**
2274
 * xmlBufAttrSerializeTxtContent:
2275
 * @buf:  output buffer
2276
 * @doc:  the document
2277
 * @string: the text content
2278
 *
2279
 * Serialize text attribute values to an xmlBufPtr
2280
 */
2281
void
2282
xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc,
2283
                              const xmlChar *string)
2284
54.6k
{
2285
54.6k
    int flags = XML_ESCAPE_ATTR;
2286
2287
54.6k
    if ((doc == NULL) || (doc->encoding == NULL))
2288
54.6k
        flags |= XML_ESCAPE_NON_ASCII;
2289
54.6k
    xmlSerializeText(buf, string, flags);
2290
54.6k
}
2291
2292
/**
2293
 * xmlAttrSerializeTxtContent:
2294
 * @buf:  the XML buffer output
2295
 * @doc:  the document
2296
 * @attr: the attribute node
2297
 * @string: the text content
2298
 *
2299
 * Serialize text attribute values to an xml simple buffer
2300
 */
2301
void
2302
xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2303
                           xmlAttrPtr attr ATTRIBUTE_UNUSED,
2304
                           const xmlChar *string)
2305
0
{
2306
0
    xmlOutputBufferPtr out;
2307
2308
0
    if ((buf == NULL) || (string == NULL))
2309
0
        return;
2310
0
    out = xmlOutputBufferCreateBuffer(buf, NULL);
2311
0
    xmlBufAttrSerializeTxtContent(out, doc, string);
2312
0
    xmlOutputBufferFlush(out);
2313
0
    if ((out == NULL) || (out->error))
2314
0
        xmlFree(xmlBufferDetach(buf));
2315
0
    xmlOutputBufferClose(out);
2316
0
}
2317
2318
/**
2319
 * xmlNodeDump:
2320
 * @buf:  the XML buffer output
2321
 * @doc:  the document
2322
 * @cur:  the current node
2323
 * @level: the imbrication level for indenting
2324
 * @format: is formatting allowed
2325
 *
2326
 * Dump an XML node, recursive behaviour,children are printed too.
2327
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2328
 * or xmlKeepBlanksDefault(0) was called.
2329
 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2330
 * deprecated, use xmlNodeDumpOutput() instead.
2331
 *
2332
 * Returns the number of bytes written to the buffer or -1 in case of error
2333
 */
2334
int
2335
xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2336
            int format)
2337
0
{
2338
0
    xmlBufPtr buffer;
2339
0
    size_t ret1;
2340
0
    int ret2;
2341
2342
0
    if ((buf == NULL) || (cur == NULL))
2343
0
        return(-1);
2344
0
    if (level < 0)
2345
0
        level = 0;
2346
0
    else if (level > 100)
2347
0
        level = 100;
2348
0
    buffer = xmlBufFromBuffer(buf);
2349
0
    if (buffer == NULL)
2350
0
        return(-1);
2351
0
    ret1 = xmlBufNodeDump(buffer, doc, cur, level, format);
2352
0
    ret2 = xmlBufBackToBuffer(buffer, buf);
2353
0
    if ((ret1 == (size_t) -1) || (ret2 < 0))
2354
0
        return(-1);
2355
0
    return(ret1 > INT_MAX ? INT_MAX : ret1);
2356
0
}
2357
2358
/**
2359
 * xmlBufNodeDump:
2360
 * @buf:  the XML buffer output
2361
 * @doc:  the document
2362
 * @cur:  the current node
2363
 * @level: the imbrication level for indenting
2364
 * @format: is formatting allowed
2365
 *
2366
 * Dump an XML node, recursive behaviour,children are printed too.
2367
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2368
 * or xmlKeepBlanksDefault(0) was called
2369
 *
2370
 * Returns the number of bytes written to the buffer, in case of error 0
2371
 *     is returned or @buf stores the error
2372
 */
2373
2374
size_t
2375
xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2376
            int format)
2377
0
{
2378
0
    size_t use;
2379
0
    size_t ret;
2380
0
    xmlOutputBufferPtr outbuf;
2381
2382
0
    xmlInitParser();
2383
2384
0
    if (cur == NULL) {
2385
0
        return ((size_t) -1);
2386
0
    }
2387
0
    if (buf == NULL) {
2388
0
        return ((size_t) -1);
2389
0
    }
2390
0
    outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2391
0
    if (outbuf == NULL) {
2392
0
        xmlSaveErrMemory(NULL);
2393
0
        return ((size_t) -1);
2394
0
    }
2395
0
    memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2396
0
    outbuf->buffer = buf;
2397
0
    outbuf->encoder = NULL;
2398
0
    outbuf->writecallback = NULL;
2399
0
    outbuf->closecallback = NULL;
2400
0
    outbuf->context = NULL;
2401
0
    outbuf->written = 0;
2402
2403
0
    use = xmlBufUse(buf);
2404
0
    xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2405
0
    if (outbuf->error)
2406
0
        ret = (size_t) -1;
2407
0
    else
2408
0
        ret = xmlBufUse(buf) - use;
2409
0
    xmlFree(outbuf);
2410
0
    return (ret);
2411
0
}
2412
2413
/**
2414
 * xmlElemDump:
2415
 * @f:  the FILE * for the output
2416
 * @doc:  the document
2417
 * @cur:  the current node
2418
 *
2419
 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2420
 */
2421
void
2422
xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2423
0
{
2424
0
    xmlOutputBufferPtr outbuf;
2425
2426
0
    xmlInitParser();
2427
2428
0
    if (cur == NULL) {
2429
0
        return;
2430
0
    }
2431
2432
0
    outbuf = xmlOutputBufferCreateFile(f, NULL);
2433
0
    if (outbuf == NULL)
2434
0
        return;
2435
0
#ifdef LIBXML_HTML_ENABLED
2436
0
    if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE))
2437
0
        htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2438
0
    else
2439
0
#endif /* LIBXML_HTML_ENABLED */
2440
0
        xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2441
0
    xmlOutputBufferClose(outbuf);
2442
0
}
2443
2444
/************************************************************************
2445
 *                  *
2446
 *    Saving functions front-ends       *
2447
 *                  *
2448
 ************************************************************************/
2449
2450
/**
2451
 * xmlNodeDumpOutput:
2452
 * @buf:  the XML buffer output
2453
 * @doc:  the document
2454
 * @cur:  the current node
2455
 * @level: the imbrication level for indenting
2456
 * @format: is formatting allowed
2457
 * @encoding:  an optional encoding string
2458
 *
2459
 * Dump an XML node, recursive behaviour, children are printed too.
2460
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2461
 * or xmlKeepBlanksDefault(0) was called
2462
 */
2463
void
2464
xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2465
                  int level, int format, const char *encoding)
2466
0
{
2467
0
    xmlSaveCtxt ctxt;
2468
0
    int options;
2469
0
#ifdef LIBXML_HTML_ENABLED
2470
0
    xmlDtdPtr dtd;
2471
0
    int is_xhtml = 0;
2472
0
#endif
2473
2474
0
    (void) doc;
2475
2476
0
    xmlInitParser();
2477
2478
0
    if ((buf == NULL) || (cur == NULL)) return;
2479
2480
0
    if (level < 0)
2481
0
        level = 0;
2482
0
    else if (level > 100)
2483
0
        level = 100;
2484
2485
0
    if (encoding == NULL)
2486
0
        encoding = "UTF-8";
2487
2488
0
    memset(&ctxt, 0, sizeof(ctxt));
2489
0
    ctxt.buf = buf;
2490
0
    ctxt.level = level;
2491
0
    ctxt.encoding = (const xmlChar *) encoding;
2492
2493
0
    options = XML_SAVE_AS_XML;
2494
0
    if (format)
2495
0
        options |= XML_SAVE_FORMAT;
2496
0
    xmlSaveCtxtInit(&ctxt, options);
2497
2498
0
#ifdef LIBXML_HTML_ENABLED
2499
0
    dtd = xmlGetIntSubset(doc);
2500
0
    if (dtd != NULL) {
2501
0
  is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2502
0
  if (is_xhtml < 0)
2503
0
      is_xhtml = 0;
2504
0
    }
2505
2506
0
    if (is_xhtml)
2507
0
        xhtmlNodeDumpOutput(&ctxt, cur);
2508
0
    else
2509
0
#endif
2510
0
        xmlNodeDumpOutputInternal(&ctxt, cur);
2511
0
}
2512
2513
/**
2514
 * xmlDocDumpFormatMemoryEnc:
2515
 * @out_doc:  Document to generate XML text from
2516
 * @doc_txt_ptr:  Memory pointer for allocated XML text
2517
 * @doc_txt_len:  Length of the generated XML text
2518
 * @txt_encoding:  Character encoding to use when generating XML text
2519
 * @format:  should formatting spaces been added
2520
 *
2521
 * Dump the current DOM tree into memory using the character encoding specified
2522
 * by the caller.  Note it is up to the caller of this function to free the
2523
 * allocated memory with xmlFree().
2524
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2525
 * or xmlKeepBlanksDefault(0) was called
2526
 */
2527
2528
void
2529
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2530
    int * doc_txt_len, const char * txt_encoding,
2531
0
    int format) {
2532
0
    xmlSaveCtxt ctxt;
2533
0
    int options;
2534
0
    int                         dummy = 0;
2535
0
    xmlOutputBufferPtr          out_buff = NULL;
2536
0
    xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
2537
2538
0
    if (doc_txt_len == NULL) {
2539
0
        doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
2540
0
    }
2541
2542
0
    if (doc_txt_ptr == NULL) {
2543
0
        *doc_txt_len = 0;
2544
0
        return;
2545
0
    }
2546
2547
0
    *doc_txt_ptr = NULL;
2548
0
    *doc_txt_len = 0;
2549
2550
0
    if (out_doc == NULL) {
2551
        /*  No document, no output  */
2552
0
        return;
2553
0
    }
2554
2555
    /*
2556
     *  Validate the encoding value, if provided.
2557
     *  This logic is copied from xmlSaveFileEnc.
2558
     */
2559
2560
0
    if (txt_encoding == NULL)
2561
0
  txt_encoding = (const char *) out_doc->encoding;
2562
0
    if (txt_encoding != NULL) {
2563
0
        xmlParserErrors res;
2564
2565
0
  res = xmlOpenCharEncodingHandler(txt_encoding, /* output */ 1,
2566
0
                                         &conv_hdlr);
2567
0
  if (res != XML_ERR_OK) {
2568
0
            xmlSaveErr(NULL, res, NULL, txt_encoding);
2569
0
      return;
2570
0
  }
2571
0
    }
2572
2573
0
    out_buff = xmlAllocOutputBuffer(conv_hdlr);
2574
0
    if (out_buff == NULL ) {
2575
0
        xmlSaveErrMemory(NULL);
2576
0
        return;
2577
0
    }
2578
2579
0
    memset(&ctxt, 0, sizeof(ctxt));
2580
0
    ctxt.buf = out_buff;
2581
0
    ctxt.level = 0;
2582
0
    ctxt.encoding = (const xmlChar *) txt_encoding;
2583
2584
0
    options = XML_SAVE_AS_XML;
2585
0
    if (format)
2586
0
        options |= XML_SAVE_FORMAT;
2587
0
    xmlSaveCtxtInit(&ctxt, options);
2588
2589
0
    xmlDocContentDumpOutput(&ctxt, out_doc);
2590
0
    xmlOutputBufferFlush(out_buff);
2591
2592
0
    if (!out_buff->error) {
2593
0
        if (out_buff->conv != NULL) {
2594
0
            *doc_txt_len = xmlBufUse(out_buff->conv);
2595
0
            *doc_txt_ptr = xmlBufDetach(out_buff->conv);
2596
0
        } else {
2597
0
            *doc_txt_len = xmlBufUse(out_buff->buffer);
2598
0
            *doc_txt_ptr = xmlBufDetach(out_buff->buffer);
2599
0
        }
2600
0
    }
2601
2602
0
    xmlOutputBufferClose(out_buff);
2603
0
}
2604
2605
/**
2606
 * xmlDocDumpMemory:
2607
 * @cur:  the document
2608
 * @mem:  OUT: the memory pointer
2609
 * @size:  OUT: the memory length
2610
 *
2611
 * Dump an XML document in memory and return the #xmlChar * and it's size
2612
 * in bytes. It's up to the caller to free the memory with xmlFree().
2613
 * The resulting byte array is zero terminated, though the last 0 is not
2614
 * included in the returned size.
2615
 */
2616
void
2617
0
xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2618
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2619
0
}
2620
2621
/**
2622
 * xmlDocDumpFormatMemory:
2623
 * @cur:  the document
2624
 * @mem:  OUT: the memory pointer
2625
 * @size:  OUT: the memory length
2626
 * @format:  should formatting spaces been added
2627
 *
2628
 *
2629
 * Dump an XML document in memory and return the #xmlChar * and it's size.
2630
 * It's up to the caller to free the memory with xmlFree().
2631
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2632
 * or xmlKeepBlanksDefault(0) was called
2633
 */
2634
void
2635
0
xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2636
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2637
0
}
2638
2639
/**
2640
 * xmlDocDumpMemoryEnc:
2641
 * @out_doc:  Document to generate XML text from
2642
 * @doc_txt_ptr:  Memory pointer for allocated XML text
2643
 * @doc_txt_len:  Length of the generated XML text
2644
 * @txt_encoding:  Character encoding to use when generating XML text
2645
 *
2646
 * Dump the current DOM tree into memory using the character encoding specified
2647
 * by the caller.  Note it is up to the caller of this function to free the
2648
 * allocated memory with xmlFree().
2649
 */
2650
2651
void
2652
xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2653
0
              int * doc_txt_len, const char * txt_encoding) {
2654
0
    xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2655
0
                        txt_encoding, 0);
2656
0
}
2657
2658
/**
2659
 * xmlDocFormatDump:
2660
 * @f:  the FILE*
2661
 * @cur:  the document
2662
 * @format: should formatting spaces been added
2663
 *
2664
 * Dump an XML document to an open FILE.
2665
 *
2666
 * returns: the number of bytes written or -1 in case of failure.
2667
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2668
 * or xmlKeepBlanksDefault(0) was called
2669
 */
2670
int
2671
0
xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2672
0
    xmlSaveCtxt ctxt;
2673
0
    xmlOutputBufferPtr buf;
2674
0
    const char * encoding;
2675
0
    xmlCharEncodingHandlerPtr handler = NULL;
2676
0
    int ret;
2677
0
    int options;
2678
2679
0
    if (cur == NULL) {
2680
0
  return(-1);
2681
0
    }
2682
0
    encoding = (const char *) cur->encoding;
2683
2684
0
    if (encoding != NULL) {
2685
0
        xmlParserErrors res;
2686
2687
0
  res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2688
0
  if (res != XML_ERR_OK) {
2689
0
      xmlFree((char *) cur->encoding);
2690
0
      encoding = NULL;
2691
0
  }
2692
0
    }
2693
0
    buf = xmlOutputBufferCreateFile(f, handler);
2694
0
    if (buf == NULL) return(-1);
2695
0
    memset(&ctxt, 0, sizeof(ctxt));
2696
0
    ctxt.buf = buf;
2697
0
    ctxt.level = 0;
2698
0
    ctxt.encoding = (const xmlChar *) encoding;
2699
2700
0
    options = XML_SAVE_AS_XML;
2701
0
    if (format)
2702
0
        options |= XML_SAVE_FORMAT;
2703
0
    xmlSaveCtxtInit(&ctxt, options);
2704
2705
0
    xmlDocContentDumpOutput(&ctxt, cur);
2706
2707
0
    ret = xmlOutputBufferClose(buf);
2708
0
    return(ret);
2709
0
}
2710
2711
/**
2712
 * xmlDocDump:
2713
 * @f:  the FILE*
2714
 * @cur:  the document
2715
 *
2716
 * Dump an XML document to an open FILE.
2717
 *
2718
 * returns: the number of bytes written or -1 in case of failure.
2719
 */
2720
int
2721
0
xmlDocDump(FILE *f, xmlDocPtr cur) {
2722
0
    return(xmlDocFormatDump (f, cur, 0));
2723
0
}
2724
2725
/**
2726
 * xmlSaveFileTo:
2727
 * @buf:  an output I/O buffer
2728
 * @cur:  the document
2729
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2730
 *
2731
 * Dump an XML document to an I/O buffer.
2732
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2733
 * after this call.
2734
 *
2735
 * returns: the number of bytes written or -1 in case of failure.
2736
 */
2737
int
2738
0
xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2739
0
    xmlSaveCtxt ctxt;
2740
0
    int ret;
2741
2742
0
    if (buf == NULL) return(-1);
2743
0
    if (cur == NULL) {
2744
0
        xmlOutputBufferClose(buf);
2745
0
  return(-1);
2746
0
    }
2747
0
    memset(&ctxt, 0, sizeof(ctxt));
2748
0
    ctxt.buf = buf;
2749
0
    ctxt.level = 0;
2750
0
    ctxt.encoding = (const xmlChar *) encoding;
2751
2752
0
    xmlSaveCtxtInit(&ctxt, XML_SAVE_AS_XML);
2753
2754
0
    xmlDocContentDumpOutput(&ctxt, cur);
2755
0
    ret = xmlOutputBufferClose(buf);
2756
0
    return(ret);
2757
0
}
2758
2759
/**
2760
 * xmlSaveFormatFileTo:
2761
 * @buf:  an output I/O buffer
2762
 * @cur:  the document
2763
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2764
 * @format: should formatting spaces been added
2765
 *
2766
 * Dump an XML document to an I/O buffer.
2767
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2768
 * after this call.
2769
 *
2770
 * returns: the number of bytes written or -1 in case of failure.
2771
 */
2772
int
2773
xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2774
                    const char *encoding, int format)
2775
0
{
2776
0
    xmlSaveCtxt ctxt;
2777
0
    int ret;
2778
0
    int options;
2779
2780
0
    if (buf == NULL) return(-1);
2781
0
    if ((cur == NULL) ||
2782
0
        ((cur->type != XML_DOCUMENT_NODE) &&
2783
0
   (cur->type != XML_HTML_DOCUMENT_NODE))) {
2784
0
        xmlOutputBufferClose(buf);
2785
0
  return(-1);
2786
0
    }
2787
0
    memset(&ctxt, 0, sizeof(ctxt));
2788
0
    ctxt.buf = buf;
2789
0
    ctxt.level = 0;
2790
0
    ctxt.encoding = (const xmlChar *) encoding;
2791
2792
0
    options = XML_SAVE_AS_XML;
2793
0
    if (format)
2794
0
        options |= XML_SAVE_FORMAT;
2795
0
    xmlSaveCtxtInit(&ctxt, options);
2796
2797
0
    xmlDocContentDumpOutput(&ctxt, cur);
2798
0
    ret = xmlOutputBufferClose(buf);
2799
0
    return (ret);
2800
0
}
2801
2802
/**
2803
 * xmlSaveFormatFileEnc:
2804
 * @filename:  the filename or URL to output
2805
 * @cur:  the document being saved
2806
 * @encoding:  the name of the encoding to use or NULL.
2807
 * @format:  should formatting spaces be added.
2808
 *
2809
 * Dump an XML document to a file or an URL.
2810
 *
2811
 * Returns the number of bytes written or -1 in case of error.
2812
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2813
 * or xmlKeepBlanksDefault(0) was called
2814
 */
2815
int
2816
xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2817
0
      const char * encoding, int format ) {
2818
0
    xmlSaveCtxt ctxt;
2819
0
    xmlOutputBufferPtr buf;
2820
0
    xmlCharEncodingHandlerPtr handler = NULL;
2821
0
    int ret;
2822
0
    int options;
2823
2824
0
    if (cur == NULL)
2825
0
  return(-1);
2826
2827
0
    if (encoding == NULL)
2828
0
  encoding = (const char *) cur->encoding;
2829
2830
0
    if (encoding != NULL) {
2831
0
        xmlParserErrors res;
2832
2833
0
        res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2834
0
        if (res != XML_ERR_OK)
2835
0
            return(-1);
2836
0
    }
2837
2838
#ifdef LIBXML_ZLIB_ENABLED
2839
    if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2840
#endif
2841
    /*
2842
     * save the content to a temp buffer.
2843
     */
2844
0
    buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2845
0
    if (buf == NULL) return(-1);
2846
0
    memset(&ctxt, 0, sizeof(ctxt));
2847
0
    ctxt.buf = buf;
2848
0
    ctxt.level = 0;
2849
0
    ctxt.encoding = (const xmlChar *) encoding;
2850
2851
0
    options = XML_SAVE_AS_XML;
2852
0
    if (format)
2853
0
        options |= XML_SAVE_FORMAT;
2854
0
    xmlSaveCtxtInit(&ctxt, options);
2855
2856
0
    xmlDocContentDumpOutput(&ctxt, cur);
2857
2858
0
    ret = xmlOutputBufferClose(buf);
2859
0
    return(ret);
2860
0
}
2861
2862
2863
/**
2864
 * xmlSaveFileEnc:
2865
 * @filename:  the filename (or URL)
2866
 * @cur:  the document
2867
 * @encoding:  the name of an encoding (or NULL)
2868
 *
2869
 * Dump an XML document, converting it to the given encoding
2870
 *
2871
 * returns: the number of bytes written or -1 in case of failure.
2872
 */
2873
int
2874
0
xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2875
0
    return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2876
0
}
2877
2878
/**
2879
 * xmlSaveFormatFile:
2880
 * @filename:  the filename (or URL)
2881
 * @cur:  the document
2882
 * @format:  should formatting spaces been added
2883
 *
2884
 * Dump an XML document to a file. Will use compression if
2885
 * compiled in and enabled. If @filename is "-" the stdout file is
2886
 * used. If @format is set then the document will be indented on output.
2887
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2888
 * or xmlKeepBlanksDefault(0) was called
2889
 *
2890
 * returns: the number of bytes written or -1 in case of failure.
2891
 */
2892
int
2893
0
xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2894
0
    return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2895
0
}
2896
2897
/**
2898
 * xmlSaveFile:
2899
 * @filename:  the filename (or URL)
2900
 * @cur:  the document
2901
 *
2902
 * Dump an XML document to a file. Will use compression if
2903
 * compiled in and enabled. If @filename is "-" the stdout file is
2904
 * used.
2905
 * returns: the number of bytes written or -1 in case of failure.
2906
 */
2907
int
2908
0
xmlSaveFile(const char *filename, xmlDocPtr cur) {
2909
0
    return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2910
0
}
2911
2912
#endif /* LIBXML_OUTPUT_ENABLED */
2913