Coverage Report

Created: 2025-10-10 06:33

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