Coverage Report

Created: 2025-08-26 07:07

/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
155k
#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
531k
                 unsigned flags) {
158
531k
    const char *cur;
159
531k
    const signed char *tab;
160
161
531k
    if (string == NULL)
162
0
        return;
163
164
531k
    if (flags & XML_ESCAPE_ATTR)
165
437k
        tab = xmlEscapeTabAttr;
166
93.8k
    else
167
93.8k
        tab = xmlEscapeTab;
168
169
531k
    cur = (const char *) string;
170
171
37.9M
    while (*cur != 0) {
172
37.9M
        const char *base;
173
37.9M
        int c;
174
37.9M
        int offset;
175
176
37.9M
        base = cur;
177
37.9M
        offset = -1;
178
179
5.37G
        while (1) {
180
5.37G
            c = (unsigned char) *cur;
181
182
5.37G
            if (c < 0x80) {
183
3.52G
                offset = tab[c];
184
3.52G
                if (offset >= 0)
185
37.8M
                    break;
186
3.52G
            } else if (flags & XML_ESCAPE_NON_ASCII) {
187
11.8k
                break;
188
11.8k
            }
189
190
5.34G
            cur += 1;
191
5.34G
        }
192
193
37.9M
        if (cur > base)
194
9.84M
            xmlOutputBufferWrite(buf, cur - base, base);
195
196
37.9M
        if (offset >= 0) {
197
37.8M
            if (c == 0)
198
530k
                break;
199
200
37.3M
            xmlOutputBufferWrite(buf, xmlEscapeContent[offset],
201
37.3M
                                 &xmlEscapeContent[offset+1]);
202
37.3M
            cur += 1;
203
37.3M
        } else {
204
11.8k
            char tempBuf[12];
205
11.8k
            int tempSize;
206
11.8k
            int val = 0, len = 4;
207
208
11.8k
            val = xmlGetUTF8Char((const xmlChar *) cur, &len);
209
11.8k
            if (val < 0) {
210
0
                val = 0xFFFD;
211
0
                cur += 1;
212
11.8k
            } else {
213
11.8k
                if (!IS_CHAR(val))
214
0
                    val = 0xFFFD;
215
11.8k
                cur += len;
216
11.8k
            }
217
218
11.8k
            tempSize = xmlSerializeHexCharRef(tempBuf, val);
219
11.8k
            xmlOutputBufferWrite(buf, tempSize, tempBuf);
220
11.8k
        }
221
37.9M
    }
222
531k
}
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
77.9k
xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt, const char *indent) {
243
77.9k
    size_t len;
244
77.9k
    int i;
245
246
77.9k
    if ((ctxt == NULL) || (indent == NULL))
247
0
        return(-1);
248
249
77.9k
    len = strlen(indent);
250
77.9k
    if ((len <= 0) || (len > MAX_INDENT))
251
0
        return(-1);
252
253
77.9k
    ctxt->indent_size = len;
254
77.9k
    ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
255
2.41M
    for (i = 0; i < ctxt->indent_nr; i++)
256
2.33M
        memcpy(&ctxt->indent[i * ctxt->indent_size], indent, len);
257
258
77.9k
    return(0);
259
77.9k
}
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
77.9k
{
270
77.9k
    if (ctxt == NULL) return;
271
272
77.9k
    xmlSaveSetIndentString(ctxt, xmlTreeIndentString);
273
274
77.9k
    if (options & XML_SAVE_FORMAT)
275
2.02k
        ctxt->format = 1;
276
75.9k
    else if (options & XML_SAVE_WSNONSIG)
277
0
        ctxt->format = 2;
278
279
77.9k
    if (((options & XML_SAVE_EMPTY) == 0) &&
280
77.9k
        (xmlSaveNoEmptyTags))
281
0
  options |= XML_SAVE_NO_EMPTY;
282
283
77.9k
    ctxt->options = options;
284
77.9k
}
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
137k
xmlSaveWriteText(xmlSaveCtxt *ctxt, const xmlChar *text, unsigned flags) {
347
137k
    if (ctxt->encoding == NULL)
348
0
        flags |= XML_ESCAPE_NON_ASCII;
349
350
137k
    xmlSerializeText(ctxt->buf, text, flags);
351
137k
}
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
43.8k
{
363
43.8k
    xmlNodePtr children;
364
43.8k
    xmlOutputBufferPtr buf = ctxt->buf;
365
366
43.8k
    children = attr->children;
367
87.6k
    while (children != NULL) {
368
43.8k
        switch (children->type) {
369
43.8k
            case XML_TEXT_NODE:
370
43.8k
          xmlSaveWriteText(ctxt, children->content, XML_ESCAPE_ATTR);
371
43.8k
    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
43.8k
        }
381
43.8k
        children = children->next;
382
43.8k
    }
383
43.8k
}
384
385
/**
386
 * xmlBufDumpNotationDecl:
387
 * @buf:  the XML buffer output
388
 * @nota:  A notation declaration
389
 *
390
 * This will dump the content the notation declaration as an XML DTD definition
391
 */
392
static void
393
0
xmlBufDumpNotationDecl(xmlOutputBufferPtr buf, xmlNotationPtr nota) {
394
0
    xmlOutputBufferWrite(buf, 11, "<!NOTATION ");
395
0
    xmlOutputBufferWriteString(buf, (const char *) nota->name);
396
397
0
    if (nota->PublicID != NULL) {
398
0
  xmlOutputBufferWrite(buf, 8, " PUBLIC ");
399
0
  xmlOutputBufferWriteQuotedString(buf, nota->PublicID);
400
0
  if (nota->SystemID != NULL) {
401
0
      xmlOutputBufferWrite(buf, 1, " ");
402
0
      xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
403
0
  }
404
0
    } else {
405
0
  xmlOutputBufferWrite(buf, 8, " SYSTEM ");
406
0
  xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
407
0
    }
408
409
0
    xmlOutputBufferWrite(buf, 3, " >\n");
410
0
}
411
412
/**
413
 * xmlBufDumpNotationDeclScan:
414
 * @nota:  A notation declaration
415
 * @buf:  the XML buffer output
416
 *
417
 * This is called with the hash scan function, and just reverses args
418
 */
419
static void
420
xmlBufDumpNotationDeclScan(void *nota, void *buf,
421
0
                           const xmlChar *name ATTRIBUTE_UNUSED) {
422
0
    xmlBufDumpNotationDecl((xmlOutputBufferPtr) buf, (xmlNotationPtr) nota);
423
0
}
424
425
/**
426
 * xmlBufDumpNotationTable:
427
 * @buf:  an xmlBufPtr output
428
 * @table:  A notation table
429
 *
430
 * This will dump the content of the notation table as an XML DTD definition
431
 */
432
static void
433
0
xmlBufDumpNotationTable(xmlOutputBufferPtr buf, xmlNotationTablePtr table) {
434
0
    xmlHashScan(table, xmlBufDumpNotationDeclScan, buf);
435
0
}
436
437
/**
438
 * xmlBufDumpElementOccur:
439
 * @buf:  output buffer
440
 * @cur:  element table
441
 *
442
 * Dump the occurrence operator of an element.
443
 */
444
static void
445
0
xmlBufDumpElementOccur(xmlOutputBufferPtr buf, xmlElementContentPtr cur) {
446
0
    switch (cur->ocur) {
447
0
        case XML_ELEMENT_CONTENT_ONCE:
448
0
            break;
449
0
        case XML_ELEMENT_CONTENT_OPT:
450
0
            xmlOutputBufferWrite(buf, 1, "?");
451
0
            break;
452
0
        case XML_ELEMENT_CONTENT_MULT:
453
0
            xmlOutputBufferWrite(buf, 1, "*");
454
0
            break;
455
0
        case XML_ELEMENT_CONTENT_PLUS:
456
0
            xmlOutputBufferWrite(buf, 1, "+");
457
0
            break;
458
0
    }
459
0
}
460
461
/**
462
 * xmlBufDumpElementContent:
463
 * @buf:  output buffer
464
 * @content:  element table
465
 *
466
 * This will dump the content of the element table as an XML DTD definition
467
 */
468
static void
469
xmlBufDumpElementContent(xmlOutputBufferPtr buf,
470
0
                         xmlElementContentPtr content) {
471
0
    xmlElementContentPtr cur;
472
473
0
    if (content == NULL) return;
474
475
0
    xmlOutputBufferWrite(buf, 1, "(");
476
0
    cur = content;
477
478
0
    do {
479
0
        if (cur == NULL) return;
480
481
0
        switch (cur->type) {
482
0
            case XML_ELEMENT_CONTENT_PCDATA:
483
0
                xmlOutputBufferWrite(buf, 7, "#PCDATA");
484
0
                break;
485
0
            case XML_ELEMENT_CONTENT_ELEMENT:
486
0
                if (cur->prefix != NULL) {
487
0
                    xmlOutputBufferWriteString(buf,
488
0
                            (const char *) cur->prefix);
489
0
                    xmlOutputBufferWrite(buf, 1, ":");
490
0
                }
491
0
                xmlOutputBufferWriteString(buf, (const char *) cur->name);
492
0
                break;
493
0
            case XML_ELEMENT_CONTENT_SEQ:
494
0
            case XML_ELEMENT_CONTENT_OR:
495
0
                if ((cur != content) &&
496
0
                    (cur->parent != NULL) &&
497
0
                    ((cur->type != cur->parent->type) ||
498
0
                     (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
499
0
                    xmlOutputBufferWrite(buf, 1, "(");
500
0
                cur = cur->c1;
501
0
                continue;
502
0
        }
503
504
0
        while (cur != content) {
505
0
            xmlElementContentPtr parent = cur->parent;
506
507
0
            if (parent == NULL) return;
508
509
0
            if (((cur->type == XML_ELEMENT_CONTENT_OR) ||
510
0
                 (cur->type == XML_ELEMENT_CONTENT_SEQ)) &&
511
0
                ((cur->type != parent->type) ||
512
0
                 (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
513
0
                xmlOutputBufferWrite(buf, 1, ")");
514
0
            xmlBufDumpElementOccur(buf, cur);
515
516
0
            if (cur == parent->c1) {
517
0
                if (parent->type == XML_ELEMENT_CONTENT_SEQ)
518
0
                    xmlOutputBufferWrite(buf, 3, " , ");
519
0
                else if (parent->type == XML_ELEMENT_CONTENT_OR)
520
0
                    xmlOutputBufferWrite(buf, 3, " | ");
521
522
0
                cur = parent->c2;
523
0
                break;
524
0
            }
525
526
0
            cur = parent;
527
0
        }
528
0
    } while (cur != content);
529
530
0
    xmlOutputBufferWrite(buf, 1, ")");
531
0
    xmlBufDumpElementOccur(buf, content);
532
0
}
533
534
/**
535
 * xmlBufDumpElementDecl:
536
 * @buf:  an xmlBufPtr output
537
 * @elem:  An element table
538
 *
539
 * This will dump the content of the element declaration as an XML
540
 * DTD definition
541
 */
542
static void
543
0
xmlBufDumpElementDecl(xmlOutputBufferPtr buf, xmlElementPtr elem) {
544
0
    xmlOutputBufferWrite(buf, 10, "<!ELEMENT ");
545
0
    if (elem->prefix != NULL) {
546
0
        xmlOutputBufferWriteString(buf, (const char *) elem->prefix);
547
0
        xmlOutputBufferWrite(buf, 1, ":");
548
0
    }
549
0
    xmlOutputBufferWriteString(buf, (const char *) elem->name);
550
0
    xmlOutputBufferWrite(buf, 1, " ");
551
552
0
    switch (elem->etype) {
553
0
  case XML_ELEMENT_TYPE_EMPTY:
554
0
      xmlOutputBufferWrite(buf, 5, "EMPTY");
555
0
      break;
556
0
  case XML_ELEMENT_TYPE_ANY:
557
0
      xmlOutputBufferWrite(buf, 3, "ANY");
558
0
      break;
559
0
  case XML_ELEMENT_TYPE_MIXED:
560
0
  case XML_ELEMENT_TYPE_ELEMENT:
561
0
      xmlBufDumpElementContent(buf, elem->content);
562
0
      break;
563
0
        default:
564
            /* assert(0); */
565
0
            break;
566
0
    }
567
568
0
    xmlOutputBufferWrite(buf, 2, ">\n");
569
0
}
570
571
/**
572
 * xmlBufDumpEnumeration:
573
 * @buf:  output buffer
574
 * @enum:  An enumeration
575
 *
576
 * This will dump the content of the enumeration
577
 */
578
static void
579
0
xmlBufDumpEnumeration(xmlOutputBufferPtr buf, xmlEnumerationPtr cur) {
580
0
    while (cur != NULL) {
581
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
582
0
        if (cur->next != NULL)
583
0
            xmlOutputBufferWrite(buf, 3, " | ");
584
585
0
        cur = cur->next;
586
0
    }
587
588
0
    xmlOutputBufferWrite(buf, 1, ")");
589
0
}
590
/**
591
 * xmlBufDumpAttributeDecl:
592
 * @buf:  output buffer
593
 * @attr:  An attribute declaration
594
 *
595
 * This will dump the content of the attribute declaration as an XML
596
 * DTD definition
597
 */
598
static void
599
0
xmlSaveWriteAttributeDecl(xmlSaveCtxtPtr ctxt, xmlAttributePtr attr) {
600
0
    xmlOutputBufferPtr buf = ctxt->buf;
601
602
0
    xmlOutputBufferWrite(buf, 10, "<!ATTLIST ");
603
0
    xmlOutputBufferWriteString(buf, (const char *) attr->elem);
604
0
    xmlOutputBufferWrite(buf, 1, " ");
605
0
    if (attr->prefix != NULL) {
606
0
  xmlOutputBufferWriteString(buf, (const char *) attr->prefix);
607
0
  xmlOutputBufferWrite(buf, 1, ":");
608
0
    }
609
0
    xmlOutputBufferWriteString(buf, (const char *) attr->name);
610
611
0
    switch (attr->atype) {
612
0
  case XML_ATTRIBUTE_CDATA:
613
0
      xmlOutputBufferWrite(buf, 6, " CDATA");
614
0
      break;
615
0
  case XML_ATTRIBUTE_ID:
616
0
      xmlOutputBufferWrite(buf, 3, " ID");
617
0
      break;
618
0
  case XML_ATTRIBUTE_IDREF:
619
0
      xmlOutputBufferWrite(buf, 6, " IDREF");
620
0
      break;
621
0
  case XML_ATTRIBUTE_IDREFS:
622
0
      xmlOutputBufferWrite(buf, 7, " IDREFS");
623
0
      break;
624
0
  case XML_ATTRIBUTE_ENTITY:
625
0
      xmlOutputBufferWrite(buf, 7, " ENTITY");
626
0
      break;
627
0
  case XML_ATTRIBUTE_ENTITIES:
628
0
      xmlOutputBufferWrite(buf, 9, " ENTITIES");
629
0
      break;
630
0
  case XML_ATTRIBUTE_NMTOKEN:
631
0
      xmlOutputBufferWrite(buf, 8, " NMTOKEN");
632
0
      break;
633
0
  case XML_ATTRIBUTE_NMTOKENS:
634
0
      xmlOutputBufferWrite(buf, 9, " NMTOKENS");
635
0
      break;
636
0
  case XML_ATTRIBUTE_ENUMERATION:
637
0
      xmlOutputBufferWrite(buf, 2, " (");
638
0
      xmlBufDumpEnumeration(buf, attr->tree);
639
0
      break;
640
0
  case XML_ATTRIBUTE_NOTATION:
641
0
      xmlOutputBufferWrite(buf, 11, " NOTATION (");
642
0
      xmlBufDumpEnumeration(buf, attr->tree);
643
0
      break;
644
0
  default:
645
            /* assert(0); */
646
0
            break;
647
0
    }
648
649
0
    switch (attr->def) {
650
0
  case XML_ATTRIBUTE_NONE:
651
0
      break;
652
0
  case XML_ATTRIBUTE_REQUIRED:
653
0
      xmlOutputBufferWrite(buf, 10, " #REQUIRED");
654
0
      break;
655
0
  case XML_ATTRIBUTE_IMPLIED:
656
0
      xmlOutputBufferWrite(buf, 9, " #IMPLIED");
657
0
      break;
658
0
  case XML_ATTRIBUTE_FIXED:
659
0
      xmlOutputBufferWrite(buf, 7, " #FIXED");
660
0
      break;
661
0
  default:
662
            /* assert(0); */
663
0
            break;
664
0
    }
665
666
0
    if (attr->defaultValue != NULL) {
667
0
        xmlOutputBufferWrite(buf, 2, " \"");
668
0
        xmlSaveWriteText(ctxt, attr->defaultValue, XML_ESCAPE_ATTR);
669
0
        xmlOutputBufferWrite(buf, 1, "\"");
670
0
    }
671
672
0
    xmlOutputBufferWrite(buf, 2, ">\n");
673
0
}
674
675
/**
676
 * xmlBufDumpEntityContent:
677
 * @buf:  output buffer
678
 * @content:  entity content.
679
 *
680
 * This will dump the quoted string value, taking care of the special
681
 * treatment required by %
682
 */
683
static void
684
0
xmlBufDumpEntityContent(xmlOutputBufferPtr buf, const xmlChar *content) {
685
0
    if (xmlStrchr(content, '%')) {
686
0
        const char * base, *cur;
687
688
0
  xmlOutputBufferWrite(buf, 1, "\"");
689
0
  base = cur = (const char *) content;
690
0
  while (*cur != 0) {
691
0
      if (*cur == '"') {
692
0
    if (base != cur)
693
0
        xmlOutputBufferWrite(buf, cur - base, base);
694
0
    xmlOutputBufferWrite(buf, 6, "&quot;");
695
0
    cur++;
696
0
    base = cur;
697
0
      } else if (*cur == '%') {
698
0
    if (base != cur)
699
0
        xmlOutputBufferWrite(buf, cur - base, base);
700
0
    xmlOutputBufferWrite(buf, 6, "&#x25;");
701
0
    cur++;
702
0
    base = cur;
703
0
      } else {
704
0
    cur++;
705
0
      }
706
0
  }
707
0
  if (base != cur)
708
0
      xmlOutputBufferWrite(buf, cur - base, base);
709
0
  xmlOutputBufferWrite(buf, 1, "\"");
710
0
    } else {
711
0
        xmlOutputBufferWriteQuotedString(buf, content);
712
0
    }
713
0
}
714
715
/**
716
 * xmlBufDumpEntityDecl:
717
 * @buf:  an xmlBufPtr output
718
 * @ent:  An entity table
719
 *
720
 * This will dump the content of the entity table as an XML DTD definition
721
 */
722
static void
723
0
xmlBufDumpEntityDecl(xmlOutputBufferPtr buf, xmlEntityPtr ent) {
724
0
    if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) ||
725
0
        (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY))
726
0
        xmlOutputBufferWrite(buf, 11, "<!ENTITY % ");
727
0
    else
728
0
        xmlOutputBufferWrite(buf, 9, "<!ENTITY ");
729
0
    xmlOutputBufferWriteString(buf, (const char *) ent->name);
730
0
    xmlOutputBufferWrite(buf, 1, " ");
731
732
0
    if ((ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) ||
733
0
        (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) ||
734
0
        (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
735
0
        if (ent->ExternalID != NULL) {
736
0
             xmlOutputBufferWrite(buf, 7, "PUBLIC ");
737
0
             xmlOutputBufferWriteQuotedString(buf, ent->ExternalID);
738
0
             xmlOutputBufferWrite(buf, 1, " ");
739
0
        } else {
740
0
             xmlOutputBufferWrite(buf, 7, "SYSTEM ");
741
0
        }
742
0
        xmlOutputBufferWriteQuotedString(buf, ent->SystemID);
743
0
    }
744
745
0
    if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
746
0
        if (ent->content != NULL) { /* Should be true ! */
747
0
            xmlOutputBufferWrite(buf, 7, " NDATA ");
748
0
            if (ent->orig != NULL)
749
0
                xmlOutputBufferWriteString(buf, (const char *) ent->orig);
750
0
            else
751
0
                xmlOutputBufferWriteString(buf, (const char *) ent->content);
752
0
        }
753
0
    }
754
755
0
    if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
756
0
        (ent->etype == XML_INTERNAL_PARAMETER_ENTITY)) {
757
0
        if (ent->orig != NULL)
758
0
            xmlOutputBufferWriteQuotedString(buf, ent->orig);
759
0
        else
760
0
            xmlBufDumpEntityContent(buf, ent->content);
761
0
    }
762
763
0
    xmlOutputBufferWrite(buf, 2, ">\n");
764
0
}
765
766
/************************************************************************
767
 *                  *
768
 *    Dumping XML tree content to an I/O output buffer  *
769
 *                  *
770
 ************************************************************************/
771
772
0
static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
773
0
    xmlOutputBufferPtr buf = ctxt->buf;
774
775
0
    if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
776
0
        xmlCharEncodingHandler *handler;
777
0
        xmlParserErrors res;
778
779
0
  res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
780
0
        if (res != XML_ERR_OK) {
781
0
            xmlSaveErr(buf, res, NULL, encoding);
782
0
            return(-1);
783
0
        }
784
785
0
        if (handler != NULL) {
786
0
            buf->conv = xmlBufCreate(4000 /* MINLEN */);
787
0
            if (buf->conv == NULL) {
788
0
                xmlCharEncCloseFunc(handler);
789
0
                xmlSaveErrMemory(buf);
790
0
                return(-1);
791
0
            }
792
0
            buf->encoder = handler;
793
0
        }
794
795
0
        ctxt->encoding = (const xmlChar *) encoding;
796
797
  /*
798
   * initialize the state, e.g. if outputting a BOM
799
   */
800
0
        xmlCharEncOutput(buf, 1);
801
0
    }
802
0
    return(0);
803
0
}
804
805
0
static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
806
0
    xmlOutputBufferPtr buf = ctxt->buf;
807
808
0
    xmlOutputBufferFlush(buf);
809
0
    xmlCharEncCloseFunc(buf->encoder);
810
0
    xmlBufFree(buf->conv);
811
0
    buf->encoder = NULL;
812
0
    buf->conv = NULL;
813
814
0
    ctxt->encoding = NULL;
815
816
0
    return(0);
817
0
}
818
819
#ifdef LIBXML_HTML_ENABLED
820
static void
821
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
822
#endif
823
static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
824
static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
825
826
static void
827
xmlSaveWriteIndent(xmlSaveCtxtPtr ctxt, int extra)
828
4.22k
{
829
4.22k
    int level;
830
831
4.22k
    if ((ctxt->options & XML_SAVE_NO_INDENT) ||
832
4.22k
        (((ctxt->options & XML_SAVE_INDENT) == 0) &&
833
4.22k
         (xmlIndentTreeOutput == 0)))
834
0
        return;
835
836
4.22k
    level = ctxt->level + extra;
837
4.22k
    if (level > ctxt->indent_nr)
838
89
        level = ctxt->indent_nr;
839
4.22k
    xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size * level, ctxt->indent);
840
4.22k
}
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
393k
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
876
393k
    unsigned escapeFlags = XML_ESCAPE_ATTR;
877
878
393k
    if ((cur == NULL) || (buf == NULL)) return;
879
880
393k
    if ((ctxt == NULL) || (ctxt->encoding == NULL))
881
58.6k
        escapeFlags |= XML_ESCAPE_NON_ASCII;
882
883
393k
    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
884
393k
  if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
885
0
      return;
886
887
393k
  if (ctxt != NULL && ctxt->format == 2)
888
0
      xmlOutputBufferWriteWSNonSig(ctxt, 2);
889
393k
  else
890
393k
      xmlOutputBufferWrite(buf, 1, " ");
891
892
        /* Within the context of an element attributes */
893
393k
  if (cur->prefix != NULL) {
894
392k
      xmlOutputBufferWrite(buf, 6, "xmlns:");
895
392k
      xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
896
392k
  } else
897
1.34k
      xmlOutputBufferWrite(buf, 5, "xmlns");
898
393k
        xmlOutputBufferWrite(buf, 2, "=\"");
899
393k
        xmlSerializeText(buf, cur->href, escapeFlags);
900
393k
        xmlOutputBufferWrite(buf, 1, "\"");
901
393k
    }
902
393k
}
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
37.9k
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
914
373k
    while (cur != NULL) {
915
335k
        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
916
335k
  cur = cur->next;
917
335k
    }
918
37.9k
}
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
7.21k
xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
930
65.8k
    while (cur != NULL) {
931
58.6k
        xmlNsDumpOutput(buf, cur, NULL);
932
58.6k
  cur = cur->next;
933
58.6k
    }
934
7.21k
}
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
43.8k
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1000
43.8k
    xmlOutputBufferPtr buf;
1001
1002
43.8k
    if (cur == NULL) return;
1003
43.8k
    buf = ctxt->buf;
1004
43.8k
    if (buf == NULL) return;
1005
43.8k
    if (ctxt->format == 2)
1006
0
        xmlOutputBufferWriteWSNonSig(ctxt, 2);
1007
43.8k
    else
1008
43.8k
        xmlOutputBufferWrite(buf, 1, " ");
1009
43.8k
    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1010
490
        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1011
490
  xmlOutputBufferWrite(buf, 1, ":");
1012
490
    }
1013
43.8k
    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1014
43.8k
    xmlOutputBufferWrite(buf, 2, "=\"");
1015
43.8k
#ifdef LIBXML_HTML_ENABLED
1016
43.8k
    if ((ctxt->options & XML_SAVE_XHTML) &&
1017
43.8k
        (cur->ns == NULL) &&
1018
43.8k
        ((cur->children == NULL) ||
1019
0
         (cur->children->content == NULL) ||
1020
0
         (cur->children->content[0] == 0)) &&
1021
43.8k
        (htmlIsBooleanAttr(cur->name))) {
1022
0
        xmlOutputBufferWriteString(buf, (const char *) cur->name);
1023
0
    } else
1024
43.8k
#endif
1025
43.8k
    {
1026
43.8k
        xmlSaveWriteAttrContent(ctxt, cur);
1027
43.8k
    }
1028
43.8k
    xmlOutputBufferWrite(buf, 1, "\"");
1029
43.8k
}
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
77.9k
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1088
77.9k
    int format = ctxt->format;
1089
77.9k
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1090
77.9k
    xmlAttrPtr attr;
1091
77.9k
    xmlChar *start, *end;
1092
77.9k
    xmlOutputBufferPtr buf;
1093
1094
77.9k
    if (cur == NULL) return;
1095
77.9k
    buf = ctxt->buf;
1096
1097
77.9k
    root = cur;
1098
77.9k
    parent = cur->parent;
1099
188k
    while (1) {
1100
188k
        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
85.5k
        case XML_ELEMENT_NODE:
1132
85.5k
      if ((cur != root) && (ctxt->format == 1))
1133
3.42k
                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
85.5k
            if ((cur->parent != parent) && (cur->children != NULL)) {
1141
0
                xmlNodeDumpOutputInternal(ctxt, cur);
1142
0
                break;
1143
0
            }
1144
1145
85.5k
            xmlOutputBufferWrite(buf, 1, "<");
1146
85.5k
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1147
7.06k
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1148
7.06k
                xmlOutputBufferWrite(buf, 1, ":");
1149
7.06k
            }
1150
85.5k
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1151
85.5k
            if (cur->nsDef)
1152
37.9k
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1153
129k
            for (attr = cur->properties; attr != NULL; attr = attr->next)
1154
43.8k
                xmlAttrDumpOutput(ctxt, attr);
1155
1156
85.5k
            if (cur->children == NULL) {
1157
47.0k
                if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
1158
47.0k
                    if (ctxt->format == 2)
1159
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1160
47.0k
                    xmlOutputBufferWrite(buf, 2, "/>");
1161
47.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
47.0k
            } else {
1176
38.5k
                if (ctxt->format == 1) {
1177
4.08k
                    tmp = cur->children;
1178
8.24k
                    while (tmp != NULL) {
1179
7.44k
                        if ((tmp->type == XML_TEXT_NODE) ||
1180
7.44k
                            (tmp->type == XML_CDATA_SECTION_NODE) ||
1181
7.44k
                            (tmp->type == XML_ENTITY_REF_NODE)) {
1182
3.28k
                            ctxt->format = 0;
1183
3.28k
                            unformattedNode = cur;
1184
3.28k
                            break;
1185
3.28k
                        }
1186
4.16k
                        tmp = tmp->next;
1187
4.16k
                    }
1188
4.08k
                }
1189
38.5k
                if (ctxt->format == 2)
1190
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 1);
1191
38.5k
                xmlOutputBufferWrite(buf, 1, ">");
1192
38.5k
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1193
38.5k
                if (ctxt->level >= 0) ctxt->level++;
1194
38.5k
                parent = cur;
1195
38.5k
                cur = cur->children;
1196
38.5k
                continue;
1197
38.5k
            }
1198
1199
47.0k
            break;
1200
1201
93.8k
        case XML_TEXT_NODE:
1202
93.8k
      if (cur->content == NULL)
1203
0
                break;
1204
93.8k
      if (cur->name != xmlStringTextNoenc) {
1205
93.8k
                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
93.8k
                else
1213
93.8k
                    xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1214
93.8k
      } else {
1215
    /*
1216
     * Disable escaping, needed for XSLT
1217
     */
1218
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1219
0
      }
1220
93.8k
      break;
1221
1222
3.67k
        case XML_PI_NODE:
1223
3.67k
      if ((cur != root) && (ctxt->format == 1))
1224
0
                xmlSaveWriteIndent(ctxt, 0);
1225
1226
3.67k
            if (cur->content != NULL) {
1227
106
                xmlOutputBufferWrite(buf, 2, "<?");
1228
106
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1229
106
                if (cur->content != NULL) {
1230
106
                    if (ctxt->format == 2)
1231
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
1232
106
                    else
1233
106
                        xmlOutputBufferWrite(buf, 1, " ");
1234
106
                    xmlOutputBufferWriteString(buf,
1235
106
                            (const char *)cur->content);
1236
106
                }
1237
106
                xmlOutputBufferWrite(buf, 2, "?>");
1238
3.56k
            } else {
1239
3.56k
                xmlOutputBufferWrite(buf, 2, "<?");
1240
3.56k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1241
3.56k
                if (ctxt->format == 2)
1242
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1243
3.56k
                xmlOutputBufferWrite(buf, 2, "?>");
1244
3.56k
            }
1245
3.67k
            break;
1246
1247
3.36k
        case XML_COMMENT_NODE:
1248
3.36k
      if ((cur != root) && (ctxt->format == 1))
1249
0
                xmlSaveWriteIndent(ctxt, 0);
1250
1251
3.36k
            if (cur->content != NULL) {
1252
3.28k
                xmlOutputBufferWrite(buf, 4, "<!--");
1253
3.28k
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1254
3.28k
                xmlOutputBufferWrite(buf, 3, "-->");
1255
3.28k
            }
1256
3.36k
            break;
1257
1258
0
        case XML_ENTITY_REF_NODE:
1259
0
            xmlOutputBufferWrite(buf, 1, "&");
1260
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1261
0
            xmlOutputBufferWrite(buf, 1, ";");
1262
0
            break;
1263
1264
1.60k
        case XML_CDATA_SECTION_NODE:
1265
1.60k
            if (cur->content == NULL || *cur->content == '\0') {
1266
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1267
1.60k
            } else {
1268
1.60k
                start = end = cur->content;
1269
78.3k
                while (*end != '\0') {
1270
76.7k
                    if ((*end == ']') && (*(end + 1) == ']') &&
1271
76.7k
                        (*(end + 2) == '>')) {
1272
1
                        end = end + 2;
1273
1
                        xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1274
1
                        xmlOutputBufferWrite(buf, end - start,
1275
1
                                (const char *)start);
1276
1
                        xmlOutputBufferWrite(buf, 3, "]]>");
1277
1
                        start = end;
1278
1
                    }
1279
76.7k
                    end++;
1280
76.7k
                }
1281
1.60k
                if (start != end) {
1282
1.60k
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1283
1.60k
                    xmlOutputBufferWriteString(buf, (const char *)start);
1284
1.60k
                    xmlOutputBufferWrite(buf, 3, "]]>");
1285
1.60k
                }
1286
1.60k
            }
1287
1.60k
            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
188k
        }
1300
1301
188k
        while (1) {
1302
188k
            if (cur == root)
1303
77.9k
                return;
1304
110k
            if ((ctxt->format == 1) &&
1305
110k
                (cur->type != XML_XINCLUDE_START) &&
1306
110k
                (cur->type != XML_XINCLUDE_END))
1307
3.42k
                xmlOutputBufferWrite(buf, 1, "\n");
1308
110k
            if (cur->next != NULL) {
1309
71.5k
                cur = cur->next;
1310
71.5k
                break;
1311
71.5k
            }
1312
1313
38.5k
            cur = parent;
1314
            /* cur->parent was validated when descending. */
1315
38.5k
            parent = cur->parent;
1316
1317
38.5k
            if (cur->type == XML_ELEMENT_NODE) {
1318
38.5k
                if (ctxt->level > 0) ctxt->level--;
1319
38.5k
                if (ctxt->format == 1)
1320
800
                    xmlSaveWriteIndent(ctxt, 0);
1321
1322
38.5k
                xmlOutputBufferWrite(buf, 2, "</");
1323
38.5k
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1324
6.23k
                    xmlOutputBufferWriteString(buf,
1325
6.23k
                            (const char *)cur->ns->prefix);
1326
6.23k
                    xmlOutputBufferWrite(buf, 1, ":");
1327
6.23k
                }
1328
1329
38.5k
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1330
38.5k
                if (ctxt->format == 2)
1331
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1332
38.5k
                xmlOutputBufferWrite(buf, 1, ">");
1333
1334
38.5k
                if (cur == unformattedNode) {
1335
3.28k
                    ctxt->format = format;
1336
3.28k
                    unformattedNode = NULL;
1337
3.28k
                }
1338
38.5k
            }
1339
38.5k
        }
1340
149k
    }
1341
77.9k
}
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
77.9k
{
2467
77.9k
    xmlSaveCtxt ctxt;
2468
77.9k
    int options;
2469
77.9k
#ifdef LIBXML_HTML_ENABLED
2470
77.9k
    xmlDtdPtr dtd;
2471
77.9k
    int is_xhtml = 0;
2472
77.9k
#endif
2473
2474
77.9k
    (void) doc;
2475
2476
77.9k
    xmlInitParser();
2477
2478
77.9k
    if ((buf == NULL) || (cur == NULL)) return;
2479
2480
77.9k
    if (level < 0)
2481
0
        level = 0;
2482
77.9k
    else if (level > 100)
2483
0
        level = 100;
2484
2485
77.9k
    if (encoding == NULL)
2486
32.8k
        encoding = "UTF-8";
2487
2488
77.9k
    memset(&ctxt, 0, sizeof(ctxt));
2489
77.9k
    ctxt.buf = buf;
2490
77.9k
    ctxt.level = level;
2491
77.9k
    ctxt.encoding = (const xmlChar *) encoding;
2492
2493
77.9k
    options = XML_SAVE_AS_XML;
2494
77.9k
    if (format)
2495
2.02k
        options |= XML_SAVE_FORMAT;
2496
77.9k
    xmlSaveCtxtInit(&ctxt, options);
2497
2498
77.9k
#ifdef LIBXML_HTML_ENABLED
2499
77.9k
    dtd = xmlGetIntSubset(doc);
2500
77.9k
    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
77.9k
    if (is_xhtml)
2507
0
        xhtmlNodeDumpOutput(&ctxt, cur);
2508
77.9k
    else
2509
77.9k
#endif
2510
77.9k
        xmlNodeDumpOutputInternal(&ctxt, cur);
2511
77.9k
}
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