Coverage Report

Created: 2023-12-13 20:02

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