Coverage Report

Created: 2025-08-03 06:44

/src/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
181k
#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
718k
                 unsigned flags) {
158
718k
    const char *cur;
159
718k
    const signed char *tab;
160
161
718k
    if (string == NULL)
162
0
        return;
163
164
718k
    if (flags & XML_ESCAPE_ATTR)
165
563k
        tab = xmlEscapeTabAttr;
166
155k
    else
167
155k
        tab = xmlEscapeTab;
168
169
718k
    cur = (const char *) string;
170
171
40.2M
    while (*cur != 0) {
172
40.2M
        const char *base;
173
40.2M
        int c;
174
40.2M
        int offset;
175
176
40.2M
        base = cur;
177
40.2M
        offset = -1;
178
179
5.74G
        while (1) {
180
5.74G
            c = (unsigned char) *cur;
181
182
5.74G
            if (c < 0x80) {
183
4.19G
                offset = tab[c];
184
4.19G
                if (offset >= 0)
185
40.2M
                    break;
186
4.19G
            } else if (flags & XML_ESCAPE_NON_ASCII) {
187
1.61k
                break;
188
1.61k
            }
189
190
5.70G
            cur += 1;
191
5.70G
        }
192
193
40.2M
        if (cur > base)
194
10.5M
            xmlOutputBufferWrite(buf, cur - base, base);
195
196
40.2M
        if (offset >= 0) {
197
40.2M
            if (c == 0)
198
713k
                break;
199
200
39.5M
            xmlOutputBufferWrite(buf, xmlEscapeContent[offset],
201
39.5M
                                 &xmlEscapeContent[offset+1]);
202
39.5M
            cur += 1;
203
39.5M
        } else {
204
1.61k
            char tempBuf[12];
205
1.61k
            int tempSize;
206
1.61k
            int val = 0, len = 4;
207
208
1.61k
            val = xmlGetUTF8Char((const xmlChar *) cur, &len);
209
1.61k
            if (val < 0) {
210
0
                val = 0xFFFD;
211
0
                cur += 1;
212
1.61k
            } else {
213
1.61k
                if (!IS_CHAR(val))
214
0
                    val = 0xFFFD;
215
1.61k
                cur += len;
216
1.61k
            }
217
218
1.61k
            tempSize = xmlSerializeHexCharRef(tempBuf, val);
219
1.61k
            xmlOutputBufferWrite(buf, tempSize, tempBuf);
220
1.61k
        }
221
40.2M
    }
222
718k
}
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
90.5k
xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt, const char *indent) {
243
90.5k
    size_t len;
244
90.5k
    int i;
245
246
90.5k
    if ((ctxt == NULL) || (indent == NULL))
247
0
        return(-1);
248
249
90.5k
    len = strlen(indent);
250
90.5k
    if ((len <= 0) || (len > MAX_INDENT))
251
0
        return(-1);
252
253
90.5k
    ctxt->indent_size = len;
254
90.5k
    ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
255
2.80M
    for (i = 0; i < ctxt->indent_nr; i++)
256
2.71M
        memcpy(&ctxt->indent[i * ctxt->indent_size], indent, len);
257
258
90.5k
    return(0);
259
90.5k
}
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
90.5k
{
270
90.5k
    if (ctxt == NULL) return;
271
272
90.5k
    xmlSaveSetIndentString(ctxt, xmlTreeIndentString);
273
274
90.5k
    if (options & XML_SAVE_FORMAT)
275
1.35k
        ctxt->format = 1;
276
89.1k
    else if (options & XML_SAVE_WSNONSIG)
277
0
        ctxt->format = 2;
278
279
90.5k
    if (((options & XML_SAVE_EMPTY) == 0) &&
280
90.5k
        (xmlSaveNoEmptyTags))
281
0
  options |= XML_SAVE_NO_EMPTY;
282
283
90.5k
    ctxt->options = options;
284
90.5k
}
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
222k
xmlSaveWriteText(xmlSaveCtxt *ctxt, const xmlChar *text, unsigned flags) {
347
222k
    if (ctxt->encoding == NULL)
348
0
        flags |= XML_ESCAPE_NON_ASCII;
349
350
222k
    xmlSerializeText(ctxt->buf, text, flags);
351
222k
}
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
66.9k
{
363
66.9k
    xmlNodePtr children;
364
66.9k
    xmlOutputBufferPtr buf = ctxt->buf;
365
366
66.9k
    children = attr->children;
367
133k
    while (children != NULL) {
368
66.9k
        switch (children->type) {
369
66.9k
            case XML_TEXT_NODE:
370
66.9k
          xmlSaveWriteText(ctxt, children->content, XML_ESCAPE_ATTR);
371
66.9k
    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
66.9k
        }
381
66.9k
        children = children->next;
382
66.9k
    }
383
66.9k
}
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
5.86k
{
829
5.86k
    int level;
830
831
5.86k
    if ((ctxt->options & XML_SAVE_NO_INDENT) ||
832
5.86k
        (((ctxt->options & XML_SAVE_INDENT) == 0) &&
833
5.86k
         (xmlIndentTreeOutput == 0)))
834
0
        return;
835
836
5.86k
    level = ctxt->level + extra;
837
5.86k
    if (level > ctxt->indent_nr)
838
162
        level = ctxt->indent_nr;
839
5.86k
    xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size * level, ctxt->indent);
840
5.86k
}
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
496k
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
876
496k
    unsigned escapeFlags = XML_ESCAPE_ATTR;
877
878
496k
    if ((cur == NULL) || (buf == NULL)) return;
879
880
496k
    if ((ctxt == NULL) || (ctxt->encoding == NULL))
881
112k
        escapeFlags |= XML_ESCAPE_NON_ASCII;
882
883
496k
    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
884
496k
  if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
885
0
      return;
886
887
496k
  if (ctxt != NULL && ctxt->format == 2)
888
0
      xmlOutputBufferWriteWSNonSig(ctxt, 2);
889
496k
  else
890
496k
      xmlOutputBufferWrite(buf, 1, " ");
891
892
        /* Within the context of an element attributes */
893
496k
  if (cur->prefix != NULL) {
894
493k
      xmlOutputBufferWrite(buf, 6, "xmlns:");
895
493k
      xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
896
493k
  } else
897
2.79k
      xmlOutputBufferWrite(buf, 5, "xmlns");
898
496k
        xmlOutputBufferWrite(buf, 2, "=\"");
899
496k
        xmlSerializeText(buf, cur->href, escapeFlags);
900
496k
        xmlOutputBufferWrite(buf, 1, "\"");
901
496k
    }
902
496k
}
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
44.1k
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
914
428k
    while (cur != NULL) {
915
384k
        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
916
384k
  cur = cur->next;
917
384k
    }
918
44.1k
}
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
12.8k
xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
930
125k
    while (cur != NULL) {
931
112k
        xmlNsDumpOutput(buf, cur, NULL);
932
112k
  cur = cur->next;
933
112k
    }
934
12.8k
}
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
66.9k
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1000
66.9k
    xmlOutputBufferPtr buf;
1001
1002
66.9k
    if (cur == NULL) return;
1003
66.9k
    buf = ctxt->buf;
1004
66.9k
    if (buf == NULL) return;
1005
66.9k
    if (ctxt->format == 2)
1006
0
        xmlOutputBufferWriteWSNonSig(ctxt, 2);
1007
66.9k
    else
1008
66.9k
        xmlOutputBufferWrite(buf, 1, " ");
1009
66.9k
    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1010
1.28k
        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1011
1.28k
  xmlOutputBufferWrite(buf, 1, ":");
1012
1.28k
    }
1013
66.9k
    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1014
66.9k
    xmlOutputBufferWrite(buf, 2, "=\"");
1015
66.9k
#ifdef LIBXML_HTML_ENABLED
1016
66.9k
    if ((ctxt->options & XML_SAVE_XHTML) &&
1017
66.9k
        (cur->ns == NULL) &&
1018
66.9k
        ((cur->children == NULL) ||
1019
0
         (cur->children->content == NULL) ||
1020
0
         (cur->children->content[0] == 0)) &&
1021
66.9k
        (htmlIsBooleanAttr(cur->name))) {
1022
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
1023
0
    } else
1024
66.9k
#endif
1025
66.9k
    {
1026
66.9k
        xmlSaveWriteAttrContent(ctxt, cur);
1027
66.9k
    }
1028
66.9k
    xmlOutputBufferWrite(buf, 1, "\"");
1029
66.9k
}
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
90.5k
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1088
90.5k
    int format = ctxt->format;
1089
90.5k
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1090
90.5k
    xmlAttrPtr attr;
1091
90.5k
    xmlChar *start, *end;
1092
90.5k
    xmlOutputBufferPtr buf;
1093
1094
90.5k
    if (cur == NULL) return;
1095
90.5k
    buf = ctxt->buf;
1096
1097
90.5k
    root = cur;
1098
90.5k
    parent = cur->parent;
1099
284k
    while (1) {
1100
284k
        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
124k
        case XML_ELEMENT_NODE:
1132
124k
      if ((cur != root) && (ctxt->format == 1))
1133
3.99k
                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
124k
            if ((cur->parent != parent) && (cur->children != NULL)) {
1141
0
                xmlNodeDumpOutputInternal(ctxt, cur);
1142
0
                break;
1143
0
            }
1144
1145
124k
            xmlOutputBufferWrite(buf, 1, "<");
1146
124k
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1147
36.5k
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1148
36.5k
                xmlOutputBufferWrite(buf, 1, ":");
1149
36.5k
            }
1150
124k
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1151
124k
            if (cur->nsDef)
1152
44.1k
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1153
191k
            for (attr = cur->properties; attr != NULL; attr = attr->next)
1154
66.9k
                xmlAttrDumpOutput(ctxt, attr);
1155
1156
124k
            if (cur->children == NULL) {
1157
61.0k
                if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
1158
61.0k
                    if (ctxt->format == 2)
1159
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1160
61.0k
                    xmlOutputBufferWrite(buf, 2, "/>");
1161
61.0k
                } 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
63.7k
            } else {
1176
63.7k
                if (ctxt->format == 1) {
1177
4.19k
                    tmp = cur->children;
1178
9.19k
                    while (tmp != NULL) {
1179
7.32k
                        if ((tmp->type == XML_TEXT_NODE) ||
1180
7.32k
                            (tmp->type == XML_CDATA_SECTION_NODE) ||
1181
7.32k
                            (tmp->type == XML_ENTITY_REF_NODE)) {
1182
2.33k
                            ctxt->format = 0;
1183
2.33k
                            unformattedNode = cur;
1184
2.33k
                            break;
1185
2.33k
                        }
1186
4.99k
                        tmp = tmp->next;
1187
4.99k
                    }
1188
4.19k
                }
1189
63.7k
                if (ctxt->format == 2)
1190
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 1);
1191
63.7k
                xmlOutputBufferWrite(buf, 1, ">");
1192
63.7k
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1193
63.7k
                if (ctxt->level >= 0) ctxt->level++;
1194
63.7k
                parent = cur;
1195
63.7k
                cur = cur->children;
1196
63.7k
                continue;
1197
63.7k
            }
1198
1199
61.0k
            break;
1200
1201
155k
        case XML_TEXT_NODE:
1202
155k
      if (cur->content == NULL)
1203
0
                break;
1204
155k
      if (cur->name != xmlStringTextNoenc) {
1205
155k
                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
155k
                else
1213
155k
                    xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1214
155k
      } else {
1215
    /*
1216
     * Disable escaping, needed for XSLT
1217
     */
1218
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1219
0
      }
1220
155k
      break;
1221
1222
1.48k
        case XML_PI_NODE:
1223
1.48k
      if ((cur != root) && (ctxt->format == 1))
1224
0
                xmlSaveWriteIndent(ctxt, 0);
1225
1226
1.48k
            if (cur->content != NULL) {
1227
188
                xmlOutputBufferWrite(buf, 2, "<?");
1228
188
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1229
188
                if (cur->content != NULL) {
1230
188
                    if (ctxt->format == 2)
1231
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1232
188
                    else
1233
188
                        xmlOutputBufferWrite(buf, 1, " ");
1234
188
                    xmlOutputBufferWriteString(buf,
1235
188
                            (const char *)cur->content);
1236
188
                }
1237
188
                xmlOutputBufferWrite(buf, 2, "?>");
1238
1.29k
            } else {
1239
1.29k
                xmlOutputBufferWrite(buf, 2, "<?");
1240
1.29k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1241
1.29k
                if (ctxt->format == 2)
1242
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1243
1.29k
                xmlOutputBufferWrite(buf, 2, "?>");
1244
1.29k
            }
1245
1.48k
            break;
1246
1247
837
        case XML_COMMENT_NODE:
1248
837
      if ((cur != root) && (ctxt->format == 1))
1249
0
                xmlSaveWriteIndent(ctxt, 0);
1250
1251
837
            if (cur->content != NULL) {
1252
749
                xmlOutputBufferWrite(buf, 4, "<!--");
1253
749
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1254
749
                xmlOutputBufferWrite(buf, 3, "-->");
1255
749
            }
1256
837
            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
1.86k
        case XML_CDATA_SECTION_NODE:
1265
1.86k
            if (cur->content == NULL || *cur->content == '\0') {
1266
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1267
1.86k
            } else {
1268
1.86k
                start = end = cur->content;
1269
59.2k
                while (*end != '\0') {
1270
57.4k
                    if ((*end == ']') && (*(end + 1) == ']') &&
1271
57.4k
                        (*(end + 2) == '>')) {
1272
1
                        end = end + 2;
1273
1
                        xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1274
1
                        xmlOutputBufferWrite(buf, end - start,
1275
1
                                (const char *)start);
1276
1
                        xmlOutputBufferWrite(buf, 3, "]]>");
1277
1
                        start = end;
1278
1
                    }
1279
57.4k
                    end++;
1280
57.4k
                }
1281
1.86k
                if (start != end) {
1282
1.86k
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1283
1.86k
                    xmlOutputBufferWriteString(buf, (const char *)start);
1284
1.86k
                    xmlOutputBufferWrite(buf, 3, "]]>");
1285
1.86k
                }
1286
1.86k
            }
1287
1.86k
            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
284k
        }
1300
1301
284k
        while (1) {
1302
284k
            if (cur == root)
1303
90.5k
                return;
1304
194k
            if ((ctxt->format == 1) &&
1305
194k
                (cur->type != XML_XINCLUDE_START) &&
1306
194k
                (cur->type != XML_XINCLUDE_END))
1307
3.99k
                xmlOutputBufferWrite(buf, 1, "\n");
1308
194k
            if (cur->next != NULL) {
1309
130k
                cur = cur->next;
1310
130k
                break;
1311
130k
            }
1312
1313
63.7k
            cur = parent;
1314
            /* cur->parent was validated when descending. */
1315
63.7k
            parent = cur->parent;
1316
1317
63.7k
            if (cur->type == XML_ELEMENT_NODE) {
1318
63.7k
                if (ctxt->level > 0) ctxt->level--;
1319
63.7k
                if (ctxt->format == 1)
1320
1.86k
                    xmlSaveWriteIndent(ctxt, 0);
1321
1322
63.7k
                xmlOutputBufferWrite(buf, 2, "</");
1323
63.7k
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1324
33.3k
                    xmlOutputBufferWriteString(buf,
1325
33.3k
                            (const char *)cur->ns->prefix);
1326
33.3k
                    xmlOutputBufferWrite(buf, 1, ":");
1327
33.3k
                }
1328
1329
63.7k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1330
63.7k
                if (ctxt->format == 2)
1331
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1332
63.7k
                xmlOutputBufferWrite(buf, 1, ">");
1333
1334
63.7k
                if (cur == unformattedNode) {
1335
2.33k
                    ctxt->format = format;
1336
2.33k
                    unformattedNode = NULL;
1337
2.33k
                }
1338
63.7k
            }
1339
63.7k
        }
1340
220k
    }
1341
90.5k
}
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
0
{
2285
0
    int flags = XML_ESCAPE_ATTR;
2286
2287
0
    if ((doc == NULL) || (doc->encoding == NULL))
2288
0
        flags |= XML_ESCAPE_NON_ASCII;
2289
0
    xmlSerializeText(buf, string, flags);
2290
0
}
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
90.5k
{
2467
90.5k
    xmlSaveCtxt ctxt;
2468
90.5k
    int options;
2469
90.5k
#ifdef LIBXML_HTML_ENABLED
2470
90.5k
    xmlDtdPtr dtd;
2471
90.5k
    int is_xhtml = 0;
2472
90.5k
#endif
2473
2474
90.5k
    (void) doc;
2475
2476
90.5k
    xmlInitParser();
2477
2478
90.5k
    if ((buf == NULL) || (cur == NULL)) return;
2479
2480
90.5k
    if (level < 0)
2481
0
        level = 0;
2482
90.5k
    else if (level > 100)
2483
0
        level = 100;
2484
2485
90.5k
    if (encoding == NULL)
2486
37.9k
        encoding = "UTF-8";
2487
2488
90.5k
    memset(&ctxt, 0, sizeof(ctxt));
2489
90.5k
    ctxt.buf = buf;
2490
90.5k
    ctxt.level = level;
2491
90.5k
    ctxt.encoding = (const xmlChar *) encoding;
2492
2493
90.5k
    options = XML_SAVE_AS_XML;
2494
90.5k
    if (format)
2495
1.35k
        options |= XML_SAVE_FORMAT;
2496
90.5k
    xmlSaveCtxtInit(&ctxt, options);
2497
2498
90.5k
#ifdef LIBXML_HTML_ENABLED
2499
90.5k
    dtd = xmlGetIntSubset(doc);
2500
90.5k
    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
90.5k
    if (is_xhtml)
2507
0
        xhtmlNodeDumpOutput(&ctxt, cur);
2508
90.5k
    else
2509
90.5k
#endif
2510
90.5k
        xmlNodeDumpOutputInternal(&ctxt, cur);
2511
90.5k
}
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