Coverage Report

Created: 2025-07-18 06:31

/src/libxml2/xmlsave.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * xmlsave.c: Implementation of the document serializer
3
 *
4
 * See Copyright for the status of this software.
5
 *
6
 * daniel@veillard.com
7
 */
8
9
#define IN_LIBXML
10
#include "libxml.h"
11
12
#include <limits.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <libxml/xmlmemory.h>
16
#include <libxml/parserInternals.h>
17
#include <libxml/tree.h>
18
#include <libxml/xmlsave.h>
19
20
117k
#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
601k
                 unsigned flags) {
158
601k
    const char *cur;
159
601k
    const signed char *tab;
160
161
601k
    if (string == NULL)
162
0
        return;
163
164
601k
    if (flags & XML_ESCAPE_ATTR)
165
399k
        tab = xmlEscapeTabAttr;
166
201k
    else
167
201k
        tab = xmlEscapeTab;
168
169
601k
    cur = (const char *) string;
170
171
91.6M
    while (*cur != 0) {
172
91.6M
        const char *base;
173
91.6M
        int c;
174
91.6M
        int offset;
175
176
91.6M
        base = cur;
177
91.6M
        offset = -1;
178
179
3.72G
        while (1) {
180
3.72G
            c = (unsigned char) *cur;
181
182
3.72G
            if (c < 0x80) {
183
1.36G
                offset = tab[c];
184
1.36G
                if (offset >= 0)
185
91.6M
                    break;
186
2.35G
            } else if (flags & XML_ESCAPE_NON_ASCII) {
187
1.90k
                break;
188
1.90k
            }
189
190
3.63G
            cur += 1;
191
3.63G
        }
192
193
91.6M
        if (cur > base)
194
21.1M
            xmlOutputBufferWrite(buf, cur - base, base);
195
196
91.6M
        if (offset >= 0) {
197
91.6M
            if (c == 0)
198
597k
                break;
199
200
91.0M
            xmlOutputBufferWrite(buf, xmlEscapeContent[offset],
201
91.0M
                                 &xmlEscapeContent[offset+1]);
202
91.0M
            cur += 1;
203
91.0M
        } else {
204
1.90k
            char tempBuf[12];
205
1.90k
            int tempSize;
206
1.90k
            int val = 0, len = 4;
207
208
1.90k
            val = xmlGetUTF8Char((const xmlChar *) cur, &len);
209
1.90k
            if (val < 0) {
210
0
                val = 0xFFFD;
211
0
                cur += 1;
212
1.90k
            } else {
213
1.90k
                if (!IS_CHAR(val))
214
0
                    val = 0xFFFD;
215
1.90k
                cur += len;
216
1.90k
            }
217
218
1.90k
            tempSize = xmlSerializeHexCharRef(tempBuf, val);
219
1.90k
            xmlOutputBufferWrite(buf, tempSize, tempBuf);
220
1.90k
        }
221
91.6M
    }
222
601k
}
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
58.8k
xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt, const char *indent) {
243
58.8k
    size_t len;
244
58.8k
    int i;
245
246
58.8k
    if ((ctxt == NULL) || (indent == NULL))
247
0
        return(-1);
248
249
58.8k
    len = strlen(indent);
250
58.8k
    if ((len <= 0) || (len > MAX_INDENT))
251
0
        return(-1);
252
253
58.8k
    ctxt->indent_size = len;
254
58.8k
    ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
255
1.82M
    for (i = 0; i < ctxt->indent_nr; i++)
256
1.76M
        memcpy(&ctxt->indent[i * ctxt->indent_size], indent, len);
257
258
58.8k
    return(0);
259
58.8k
}
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
58.8k
{
270
58.8k
    if (ctxt == NULL) return;
271
272
58.8k
    xmlSaveSetIndentString(ctxt, xmlTreeIndentString);
273
274
58.8k
    if (options & XML_SAVE_FORMAT)
275
1.39k
        ctxt->format = 1;
276
57.4k
    else if (options & XML_SAVE_WSNONSIG)
277
0
        ctxt->format = 2;
278
279
58.8k
    if (((options & XML_SAVE_EMPTY) == 0) &&
280
58.8k
        (xmlSaveNoEmptyTags))
281
0
  options |= XML_SAVE_NO_EMPTY;
282
283
58.8k
    ctxt->options = options;
284
58.8k
}
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
238k
xmlSaveWriteText(xmlSaveCtxt *ctxt, const xmlChar *text, unsigned flags) {
347
238k
    if (ctxt->encoding == NULL)
348
0
        flags |= XML_ESCAPE_NON_ASCII;
349
350
238k
    xmlSerializeText(ctxt->buf, text, flags);
351
238k
}
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
36.7k
{
363
36.7k
    xmlNodePtr children;
364
36.7k
    xmlOutputBufferPtr buf = ctxt->buf;
365
366
36.7k
    children = attr->children;
367
73.4k
    while (children != NULL) {
368
36.7k
        switch (children->type) {
369
36.7k
            case XML_TEXT_NODE:
370
36.7k
          xmlSaveWriteText(ctxt, children->content, XML_ESCAPE_ATTR);
371
36.7k
    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
36.7k
        }
381
36.7k
        children = children->next;
382
36.7k
    }
383
36.7k
}
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
7.55k
{
829
7.55k
    int level;
830
831
7.55k
    if ((ctxt->options & XML_SAVE_NO_INDENT) ||
832
7.55k
        (((ctxt->options & XML_SAVE_INDENT) == 0) &&
833
7.55k
         (xmlIndentTreeOutput == 0)))
834
0
        return;
835
836
7.55k
    level = ctxt->level + extra;
837
7.55k
    if (level > ctxt->indent_nr)
838
643
        level = ctxt->indent_nr;
839
7.55k
    xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size * level, ctxt->indent);
840
7.55k
}
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
362k
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
876
362k
    unsigned escapeFlags = XML_ESCAPE_ATTR;
877
878
362k
    if ((cur == NULL) || (buf == NULL)) return;
879
880
362k
    if ((ctxt == NULL) || (ctxt->encoding == NULL))
881
87.9k
        escapeFlags |= XML_ESCAPE_NON_ASCII;
882
883
362k
    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
884
362k
  if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
885
0
      return;
886
887
362k
  if (ctxt != NULL && ctxt->format == 2)
888
0
      xmlOutputBufferWriteWSNonSig(ctxt, 2);
889
362k
  else
890
362k
      xmlOutputBufferWrite(buf, 1, " ");
891
892
        /* Within the context of an element attributes */
893
362k
  if (cur->prefix != NULL) {
894
360k
      xmlOutputBufferWrite(buf, 6, "xmlns:");
895
360k
      xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
896
360k
  } else
897
2.18k
      xmlOutputBufferWrite(buf, 5, "xmlns");
898
362k
        xmlOutputBufferWrite(buf, 2, "=\"");
899
362k
        xmlSerializeText(buf, cur->href, escapeFlags);
900
362k
        xmlOutputBufferWrite(buf, 1, "\"");
901
362k
    }
902
362k
}
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
32.2k
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
914
307k
    while (cur != NULL) {
915
274k
        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
916
274k
  cur = cur->next;
917
274k
    }
918
32.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
10.8k
xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
930
98.8k
    while (cur != NULL) {
931
87.9k
        xmlNsDumpOutput(buf, cur, NULL);
932
87.9k
  cur = cur->next;
933
87.9k
    }
934
10.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
36.7k
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1000
36.7k
    xmlOutputBufferPtr buf;
1001
1002
36.7k
    if (cur == NULL) return;
1003
36.7k
    buf = ctxt->buf;
1004
36.7k
    if (buf == NULL) return;
1005
36.7k
    if (ctxt->format == 2)
1006
0
        xmlOutputBufferWriteWSNonSig(ctxt, 2);
1007
36.7k
    else
1008
36.7k
        xmlOutputBufferWrite(buf, 1, " ");
1009
36.7k
    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1010
673
        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1011
673
  xmlOutputBufferWrite(buf, 1, ":");
1012
673
    }
1013
36.7k
    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1014
36.7k
    xmlOutputBufferWrite(buf, 2, "=\"");
1015
36.7k
#ifdef LIBXML_HTML_ENABLED
1016
36.7k
    if ((ctxt->options & XML_SAVE_XHTML) &&
1017
36.7k
        (cur->ns == NULL) &&
1018
36.7k
        ((cur->children == NULL) ||
1019
0
         (cur->children->content == NULL) ||
1020
0
         (cur->children->content[0] == 0)) &&
1021
36.7k
        (htmlIsBooleanAttr(cur->name))) {
1022
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
1023
0
    } else
1024
36.7k
#endif
1025
36.7k
    {
1026
36.7k
        xmlSaveWriteAttrContent(ctxt, cur);
1027
36.7k
    }
1028
36.7k
    xmlOutputBufferWrite(buf, 1, "\"");
1029
36.7k
}
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
58.8k
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1088
58.8k
    int format = ctxt->format;
1089
58.8k
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1090
58.8k
    xmlAttrPtr attr;
1091
58.8k
    xmlChar *start, *end;
1092
58.8k
    xmlOutputBufferPtr buf;
1093
1094
58.8k
    if (cur == NULL) return;
1095
58.8k
    buf = ctxt->buf;
1096
1097
58.8k
    root = cur;
1098
58.8k
    parent = cur->parent;
1099
344k
    while (1) {
1100
344k
        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
133k
        case XML_ELEMENT_NODE:
1132
133k
      if ((cur != root) && (ctxt->format == 1))
1133
5.58k
                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
133k
            if ((cur->parent != parent) && (cur->children != NULL)) {
1141
0
                xmlNodeDumpOutputInternal(ctxt, cur);
1142
0
                break;
1143
0
            }
1144
1145
133k
            xmlOutputBufferWrite(buf, 1, "<");
1146
133k
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1147
60.9k
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1148
60.9k
                xmlOutputBufferWrite(buf, 1, ":");
1149
60.9k
            }
1150
133k
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1151
133k
            if (cur->nsDef)
1152
32.2k
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1153
170k
            for (attr = cur->properties; attr != NULL; attr = attr->next)
1154
36.7k
                xmlAttrDumpOutput(ctxt, attr);
1155
1156
133k
            if (cur->children == NULL) {
1157
41.0k
                if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
1158
41.0k
                    if (ctxt->format == 2)
1159
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1160
41.0k
                    xmlOutputBufferWrite(buf, 2, "/>");
1161
41.0k
                } else {
1162
0
                    if (ctxt->format == 2)
1163
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 1);
1164
0
                    xmlOutputBufferWrite(buf, 3, "></");
1165
0
                    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1166
0
                        xmlOutputBufferWriteString(buf,
1167
0
                                (const char *)cur->ns->prefix);
1168
0
                        xmlOutputBufferWrite(buf, 1, ":");
1169
0
                    }
1170
0
                    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1171
0
                    if (ctxt->format == 2)
1172
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1173
0
                    xmlOutputBufferWrite(buf, 1, ">");
1174
0
                }
1175
92.3k
            } else {
1176
92.3k
                if (ctxt->format == 1) {
1177
4.71k
                    tmp = cur->children;
1178
11.1k
                    while (tmp != NULL) {
1179
9.17k
                        if ((tmp->type == XML_TEXT_NODE) ||
1180
9.17k
                            (tmp->type == XML_CDATA_SECTION_NODE) ||
1181
9.17k
                            (tmp->type == XML_ENTITY_REF_NODE)) {
1182
2.73k
                            ctxt->format = 0;
1183
2.73k
                            unformattedNode = cur;
1184
2.73k
                            break;
1185
2.73k
                        }
1186
6.44k
                        tmp = tmp->next;
1187
6.44k
                    }
1188
4.71k
                }
1189
92.3k
                if (ctxt->format == 2)
1190
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 1);
1191
92.3k
                xmlOutputBufferWrite(buf, 1, ">");
1192
92.3k
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1193
92.3k
                if (ctxt->level >= 0) ctxt->level++;
1194
92.3k
                parent = cur;
1195
92.3k
                cur = cur->children;
1196
92.3k
                continue;
1197
92.3k
            }
1198
1199
41.0k
            break;
1200
1201
201k
        case XML_TEXT_NODE:
1202
201k
      if (cur->content == NULL)
1203
0
                break;
1204
201k
      if (cur->name != xmlStringTextNoenc) {
1205
201k
                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
201k
                else
1213
201k
                    xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1214
201k
      } else {
1215
    /*
1216
     * Disable escaping, needed for XSLT
1217
     */
1218
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1219
0
      }
1220
201k
      break;
1221
1222
6.33k
        case XML_PI_NODE:
1223
6.33k
      if ((cur != root) && (ctxt->format == 1))
1224
0
                xmlSaveWriteIndent(ctxt, 0);
1225
1226
6.33k
            if (cur->content != NULL) {
1227
365
                xmlOutputBufferWrite(buf, 2, "<?");
1228
365
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1229
365
                if (cur->content != NULL) {
1230
365
                    if (ctxt->format == 2)
1231
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1232
365
                    else
1233
365
                        xmlOutputBufferWrite(buf, 1, " ");
1234
365
                    xmlOutputBufferWriteString(buf,
1235
365
                            (const char *)cur->content);
1236
365
                }
1237
365
                xmlOutputBufferWrite(buf, 2, "?>");
1238
5.97k
            } else {
1239
5.97k
                xmlOutputBufferWrite(buf, 2, "<?");
1240
5.97k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1241
5.97k
                if (ctxt->format == 2)
1242
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1243
5.97k
                xmlOutputBufferWrite(buf, 2, "?>");
1244
5.97k
            }
1245
6.33k
            break;
1246
1247
1.43k
        case XML_COMMENT_NODE:
1248
1.43k
      if ((cur != root) && (ctxt->format == 1))
1249
0
                xmlSaveWriteIndent(ctxt, 0);
1250
1251
1.43k
            if (cur->content != NULL) {
1252
1.37k
                xmlOutputBufferWrite(buf, 4, "<!--");
1253
1.37k
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1254
1.37k
                xmlOutputBufferWrite(buf, 3, "-->");
1255
1.37k
            }
1256
1.43k
            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
2.14k
        case XML_CDATA_SECTION_NODE:
1265
2.14k
            if (cur->content == NULL || *cur->content == '\0') {
1266
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1267
2.14k
            } else {
1268
2.14k
                start = end = cur->content;
1269
113k
                while (*end != '\0') {
1270
111k
                    if ((*end == ']') && (*(end + 1) == ']') &&
1271
111k
                        (*(end + 2) == '>')) {
1272
36
                        end = end + 2;
1273
36
                        xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1274
36
                        xmlOutputBufferWrite(buf, end - start,
1275
36
                                (const char *)start);
1276
36
                        xmlOutputBufferWrite(buf, 3, "]]>");
1277
36
                        start = end;
1278
36
                    }
1279
111k
                    end++;
1280
111k
                }
1281
2.14k
                if (start != end) {
1282
2.14k
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1283
2.14k
                    xmlOutputBufferWriteString(buf, (const char *)start);
1284
2.14k
                    xmlOutputBufferWrite(buf, 3, "]]>");
1285
2.14k
                }
1286
2.14k
            }
1287
2.14k
            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
344k
        }
1300
1301
344k
        while (1) {
1302
344k
            if (cur == root)
1303
58.8k
                return;
1304
286k
            if ((ctxt->format == 1) &&
1305
286k
                (cur->type != XML_XINCLUDE_START) &&
1306
286k
                (cur->type != XML_XINCLUDE_END))
1307
5.58k
                xmlOutputBufferWrite(buf, 1, "\n");
1308
286k
            if (cur->next != NULL) {
1309
193k
                cur = cur->next;
1310
193k
                break;
1311
193k
            }
1312
1313
92.3k
            cur = parent;
1314
            /* cur->parent was validated when descending. */
1315
92.3k
            parent = cur->parent;
1316
1317
92.3k
            if (cur->type == XML_ELEMENT_NODE) {
1318
92.3k
                if (ctxt->level > 0) ctxt->level--;
1319
92.3k
                if (ctxt->format == 1)
1320
1.97k
                    xmlSaveWriteIndent(ctxt, 0);
1321
1322
92.3k
                xmlOutputBufferWrite(buf, 2, "</");
1323
92.3k
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1324
59.0k
                    xmlOutputBufferWriteString(buf,
1325
59.0k
                            (const char *)cur->ns->prefix);
1326
59.0k
                    xmlOutputBufferWrite(buf, 1, ":");
1327
59.0k
                }
1328
1329
92.3k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1330
92.3k
                if (ctxt->format == 2)
1331
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1332
92.3k
                xmlOutputBufferWrite(buf, 1, ">");
1333
1334
92.3k
                if (cur == unformattedNode) {
1335
2.73k
                    ctxt->format = format;
1336
2.73k
                    unformattedNode = NULL;
1337
2.73k
                }
1338
92.3k
            }
1339
92.3k
        }
1340
252k
    }
1341
58.8k
}
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
58.8k
{
2467
58.8k
    xmlSaveCtxt ctxt;
2468
58.8k
    int options;
2469
58.8k
#ifdef LIBXML_HTML_ENABLED
2470
58.8k
    xmlDtdPtr dtd;
2471
58.8k
    int is_xhtml = 0;
2472
58.8k
#endif
2473
2474
58.8k
    (void) doc;
2475
2476
58.8k
    xmlInitParser();
2477
2478
58.8k
    if ((buf == NULL) || (cur == NULL)) return;
2479
2480
58.8k
    if (level < 0)
2481
0
        level = 0;
2482
58.8k
    else if (level > 100)
2483
0
        level = 100;
2484
2485
58.8k
    if (encoding == NULL)
2486
28.6k
        encoding = "UTF-8";
2487
2488
58.8k
    memset(&ctxt, 0, sizeof(ctxt));
2489
58.8k
    ctxt.buf = buf;
2490
58.8k
    ctxt.level = level;
2491
58.8k
    ctxt.encoding = (const xmlChar *) encoding;
2492
2493
58.8k
    options = XML_SAVE_AS_XML;
2494
58.8k
    if (format)
2495
1.39k
        options |= XML_SAVE_FORMAT;
2496
58.8k
    xmlSaveCtxtInit(&ctxt, options);
2497
2498
58.8k
#ifdef LIBXML_HTML_ENABLED
2499
58.8k
    dtd = xmlGetIntSubset(doc);
2500
58.8k
    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
58.8k
    if (is_xhtml)
2507
0
        xhtmlNodeDumpOutput(&ctxt, cur);
2508
58.8k
    else
2509
58.8k
#endif
2510
58.8k
        xmlNodeDumpOutputInternal(&ctxt, cur);
2511
58.8k
}
2512
2513
/**
2514
 * xmlDocDumpFormatMemoryEnc:
2515
 * @out_doc:  Document to generate XML text from
2516
 * @doc_txt_ptr:  Memory pointer for allocated XML text
2517
 * @doc_txt_len:  Length of the generated XML text
2518
 * @txt_encoding:  Character encoding to use when generating XML text
2519
 * @format:  should formatting spaces been added
2520
 *
2521
 * Dump the current DOM tree into memory using the character encoding specified
2522
 * by the caller.  Note it is up to the caller of this function to free the
2523
 * allocated memory with xmlFree().
2524
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2525
 * or xmlKeepBlanksDefault(0) was called
2526
 */
2527
2528
void
2529
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2530
    int * doc_txt_len, const char * txt_encoding,
2531
0
    int format) {
2532
0
    xmlSaveCtxt ctxt;
2533
0
    int options;
2534
0
    int                         dummy = 0;
2535
0
    xmlOutputBufferPtr          out_buff = NULL;
2536
0
    xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
2537
2538
0
    if (doc_txt_len == NULL) {
2539
0
        doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
2540
0
    }
2541
2542
0
    if (doc_txt_ptr == NULL) {
2543
0
        *doc_txt_len = 0;
2544
0
        return;
2545
0
    }
2546
2547
0
    *doc_txt_ptr = NULL;
2548
0
    *doc_txt_len = 0;
2549
2550
0
    if (out_doc == NULL) {
2551
        /*  No document, no output  */
2552
0
        return;
2553
0
    }
2554
2555
    /*
2556
     *  Validate the encoding value, if provided.
2557
     *  This logic is copied from xmlSaveFileEnc.
2558
     */
2559
2560
0
    if (txt_encoding == NULL)
2561
0
  txt_encoding = (const char *) out_doc->encoding;
2562
0
    if (txt_encoding != NULL) {
2563
0
        xmlParserErrors res;
2564
2565
0
  res = xmlOpenCharEncodingHandler(txt_encoding, /* output */ 1,
2566
0
                                         &conv_hdlr);
2567
0
  if (res != XML_ERR_OK) {
2568
0
            xmlSaveErr(NULL, res, NULL, txt_encoding);
2569
0
      return;
2570
0
  }
2571
0
    }
2572
2573
0
    out_buff = xmlAllocOutputBuffer(conv_hdlr);
2574
0
    if (out_buff == NULL ) {
2575
0
        xmlSaveErrMemory(NULL);
2576
0
        return;
2577
0
    }
2578
2579
0
    memset(&ctxt, 0, sizeof(ctxt));
2580
0
    ctxt.buf = out_buff;
2581
0
    ctxt.level = 0;
2582
0
    ctxt.encoding = (const xmlChar *) txt_encoding;
2583
2584
0
    options = XML_SAVE_AS_XML;
2585
0
    if (format)
2586
0
        options |= XML_SAVE_FORMAT;
2587
0
    xmlSaveCtxtInit(&ctxt, options);
2588
2589
0
    xmlDocContentDumpOutput(&ctxt, out_doc);
2590
0
    xmlOutputBufferFlush(out_buff);
2591
2592
0
    if (!out_buff->error) {
2593
0
        if (out_buff->conv != NULL) {
2594
0
            *doc_txt_len = xmlBufUse(out_buff->conv);
2595
0
            *doc_txt_ptr = xmlBufDetach(out_buff->conv);
2596
0
        } else {
2597
0
            *doc_txt_len = xmlBufUse(out_buff->buffer);
2598
0
            *doc_txt_ptr = xmlBufDetach(out_buff->buffer);
2599
0
        }
2600
0
    }
2601
2602
0
    xmlOutputBufferClose(out_buff);
2603
0
}
2604
2605
/**
2606
 * xmlDocDumpMemory:
2607
 * @cur:  the document
2608
 * @mem:  OUT: the memory pointer
2609
 * @size:  OUT: the memory length
2610
 *
2611
 * Dump an XML document in memory and return the #xmlChar * and it's size
2612
 * in bytes. It's up to the caller to free the memory with xmlFree().
2613
 * The resulting byte array is zero terminated, though the last 0 is not
2614
 * included in the returned size.
2615
 */
2616
void
2617
0
xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2618
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2619
0
}
2620
2621
/**
2622
 * xmlDocDumpFormatMemory:
2623
 * @cur:  the document
2624
 * @mem:  OUT: the memory pointer
2625
 * @size:  OUT: the memory length
2626
 * @format:  should formatting spaces been added
2627
 *
2628
 *
2629
 * Dump an XML document in memory and return the #xmlChar * and it's size.
2630
 * It's up to the caller to free the memory with xmlFree().
2631
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2632
 * or xmlKeepBlanksDefault(0) was called
2633
 */
2634
void
2635
0
xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2636
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2637
0
}
2638
2639
/**
2640
 * xmlDocDumpMemoryEnc:
2641
 * @out_doc:  Document to generate XML text from
2642
 * @doc_txt_ptr:  Memory pointer for allocated XML text
2643
 * @doc_txt_len:  Length of the generated XML text
2644
 * @txt_encoding:  Character encoding to use when generating XML text
2645
 *
2646
 * Dump the current DOM tree into memory using the character encoding specified
2647
 * by the caller.  Note it is up to the caller of this function to free the
2648
 * allocated memory with xmlFree().
2649
 */
2650
2651
void
2652
xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2653
0
              int * doc_txt_len, const char * txt_encoding) {
2654
0
    xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2655
0
                        txt_encoding, 0);
2656
0
}
2657
2658
/**
2659
 * xmlDocFormatDump:
2660
 * @f:  the FILE*
2661
 * @cur:  the document
2662
 * @format: should formatting spaces been added
2663
 *
2664
 * Dump an XML document to an open FILE.
2665
 *
2666
 * returns: the number of bytes written or -1 in case of failure.
2667
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2668
 * or xmlKeepBlanksDefault(0) was called
2669
 */
2670
int
2671
0
xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2672
0
    xmlSaveCtxt ctxt;
2673
0
    xmlOutputBufferPtr buf;
2674
0
    const char * encoding;
2675
0
    xmlCharEncodingHandlerPtr handler = NULL;
2676
0
    int ret;
2677
0
    int options;
2678
2679
0
    if (cur == NULL) {
2680
0
  return(-1);
2681
0
    }
2682
0
    encoding = (const char *) cur->encoding;
2683
2684
0
    if (encoding != NULL) {
2685
0
        xmlParserErrors res;
2686
2687
0
  res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2688
0
  if (res != XML_ERR_OK) {
2689
0
      xmlFree((char *) cur->encoding);
2690
0
      encoding = NULL;
2691
0
  }
2692
0
    }
2693
0
    buf = xmlOutputBufferCreateFile(f, handler);
2694
0
    if (buf == NULL) return(-1);
2695
0
    memset(&ctxt, 0, sizeof(ctxt));
2696
0
    ctxt.buf = buf;
2697
0
    ctxt.level = 0;
2698
0
    ctxt.encoding = (const xmlChar *) encoding;
2699
2700
0
    options = XML_SAVE_AS_XML;
2701
0
    if (format)
2702
0
        options |= XML_SAVE_FORMAT;
2703
0
    xmlSaveCtxtInit(&ctxt, options);
2704
2705
0
    xmlDocContentDumpOutput(&ctxt, cur);
2706
2707
0
    ret = xmlOutputBufferClose(buf);
2708
0
    return(ret);
2709
0
}
2710
2711
/**
2712
 * xmlDocDump:
2713
 * @f:  the FILE*
2714
 * @cur:  the document
2715
 *
2716
 * Dump an XML document to an open FILE.
2717
 *
2718
 * returns: the number of bytes written or -1 in case of failure.
2719
 */
2720
int
2721
0
xmlDocDump(FILE *f, xmlDocPtr cur) {
2722
0
    return(xmlDocFormatDump (f, cur, 0));
2723
0
}
2724
2725
/**
2726
 * xmlSaveFileTo:
2727
 * @buf:  an output I/O buffer
2728
 * @cur:  the document
2729
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2730
 *
2731
 * Dump an XML document to an I/O buffer.
2732
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2733
 * after this call.
2734
 *
2735
 * returns: the number of bytes written or -1 in case of failure.
2736
 */
2737
int
2738
0
xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2739
0
    xmlSaveCtxt ctxt;
2740
0
    int ret;
2741
2742
0
    if (buf == NULL) return(-1);
2743
0
    if (cur == NULL) {
2744
0
        xmlOutputBufferClose(buf);
2745
0
  return(-1);
2746
0
    }
2747
0
    memset(&ctxt, 0, sizeof(ctxt));
2748
0
    ctxt.buf = buf;
2749
0
    ctxt.level = 0;
2750
0
    ctxt.encoding = (const xmlChar *) encoding;
2751
2752
0
    xmlSaveCtxtInit(&ctxt, XML_SAVE_AS_XML);
2753
2754
0
    xmlDocContentDumpOutput(&ctxt, cur);
2755
0
    ret = xmlOutputBufferClose(buf);
2756
0
    return(ret);
2757
0
}
2758
2759
/**
2760
 * xmlSaveFormatFileTo:
2761
 * @buf:  an output I/O buffer
2762
 * @cur:  the document
2763
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2764
 * @format: should formatting spaces been added
2765
 *
2766
 * Dump an XML document to an I/O buffer.
2767
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2768
 * after this call.
2769
 *
2770
 * returns: the number of bytes written or -1 in case of failure.
2771
 */
2772
int
2773
xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2774
                    const char *encoding, int format)
2775
0
{
2776
0
    xmlSaveCtxt ctxt;
2777
0
    int ret;
2778
0
    int options;
2779
2780
0
    if (buf == NULL) return(-1);
2781
0
    if ((cur == NULL) ||
2782
0
        ((cur->type != XML_DOCUMENT_NODE) &&
2783
0
   (cur->type != XML_HTML_DOCUMENT_NODE))) {
2784
0
        xmlOutputBufferClose(buf);
2785
0
  return(-1);
2786
0
    }
2787
0
    memset(&ctxt, 0, sizeof(ctxt));
2788
0
    ctxt.buf = buf;
2789
0
    ctxt.level = 0;
2790
0
    ctxt.encoding = (const xmlChar *) encoding;
2791
2792
0
    options = XML_SAVE_AS_XML;
2793
0
    if (format)
2794
0
        options |= XML_SAVE_FORMAT;
2795
0
    xmlSaveCtxtInit(&ctxt, options);
2796
2797
0
    xmlDocContentDumpOutput(&ctxt, cur);
2798
0
    ret = xmlOutputBufferClose(buf);
2799
0
    return (ret);
2800
0
}
2801
2802
/**
2803
 * xmlSaveFormatFileEnc:
2804
 * @filename:  the filename or URL to output
2805
 * @cur:  the document being saved
2806
 * @encoding:  the name of the encoding to use or NULL.
2807
 * @format:  should formatting spaces be added.
2808
 *
2809
 * Dump an XML document to a file or an URL.
2810
 *
2811
 * Returns the number of bytes written or -1 in case of error.
2812
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2813
 * or xmlKeepBlanksDefault(0) was called
2814
 */
2815
int
2816
xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2817
0
      const char * encoding, int format ) {
2818
0
    xmlSaveCtxt ctxt;
2819
0
    xmlOutputBufferPtr buf;
2820
0
    xmlCharEncodingHandlerPtr handler = NULL;
2821
0
    int ret;
2822
0
    int options;
2823
2824
0
    if (cur == NULL)
2825
0
  return(-1);
2826
2827
0
    if (encoding == NULL)
2828
0
  encoding = (const char *) cur->encoding;
2829
2830
0
    if (encoding != NULL) {
2831
0
        xmlParserErrors res;
2832
2833
0
        res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2834
0
        if (res != XML_ERR_OK)
2835
0
            return(-1);
2836
0
    }
2837
2838
#ifdef LIBXML_ZLIB_ENABLED
2839
    if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2840
#endif
2841
    /*
2842
     * save the content to a temp buffer.
2843
     */
2844
0
    buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2845
0
    if (buf == NULL) return(-1);
2846
0
    memset(&ctxt, 0, sizeof(ctxt));
2847
0
    ctxt.buf = buf;
2848
0
    ctxt.level = 0;
2849
0
    ctxt.encoding = (const xmlChar *) encoding;
2850
2851
0
    options = XML_SAVE_AS_XML;
2852
0
    if (format)
2853
0
        options |= XML_SAVE_FORMAT;
2854
0
    xmlSaveCtxtInit(&ctxt, options);
2855
2856
0
    xmlDocContentDumpOutput(&ctxt, cur);
2857
2858
0
    ret = xmlOutputBufferClose(buf);
2859
0
    return(ret);
2860
0
}
2861
2862
2863
/**
2864
 * xmlSaveFileEnc:
2865
 * @filename:  the filename (or URL)
2866
 * @cur:  the document
2867
 * @encoding:  the name of an encoding (or NULL)
2868
 *
2869
 * Dump an XML document, converting it to the given encoding
2870
 *
2871
 * returns: the number of bytes written or -1 in case of failure.
2872
 */
2873
int
2874
0
xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2875
0
    return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2876
0
}
2877
2878
/**
2879
 * xmlSaveFormatFile:
2880
 * @filename:  the filename (or URL)
2881
 * @cur:  the document
2882
 * @format:  should formatting spaces been added
2883
 *
2884
 * Dump an XML document to a file. Will use compression if
2885
 * compiled in and enabled. If @filename is "-" the stdout file is
2886
 * used. If @format is set then the document will be indented on output.
2887
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2888
 * or xmlKeepBlanksDefault(0) was called
2889
 *
2890
 * returns: the number of bytes written or -1 in case of failure.
2891
 */
2892
int
2893
0
xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2894
0
    return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2895
0
}
2896
2897
/**
2898
 * xmlSaveFile:
2899
 * @filename:  the filename (or URL)
2900
 * @cur:  the document
2901
 *
2902
 * Dump an XML document to a file. Will use compression if
2903
 * compiled in and enabled. If @filename is "-" the stdout file is
2904
 * used.
2905
 * returns: the number of bytes written or -1 in case of failure.
2906
 */
2907
int
2908
0
xmlSaveFile(const char *filename, xmlDocPtr cur) {
2909
0
    return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2910
0
}
2911
2912
#endif /* LIBXML_OUTPUT_ENABLED */
2913