Coverage Report

Created: 2026-06-10 06:23

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