Coverage Report

Created: 2023-11-19 06:13

/src/libxml2-2.11.5/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
0
#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
0
#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
0
{
82
0
    const char *msg = NULL;
83
84
0
    switch(code) {
85
0
        case XML_SAVE_NOT_UTF8:
86
0
      msg = "string is not in UTF-8\n";
87
0
      break;
88
0
  case XML_SAVE_CHAR_INVALID:
89
0
      msg = "invalid character value\n";
90
0
      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
0
    }
100
0
    __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
101
0
}
102
103
/************************************************************************
104
 *                  *
105
 *      Special escaping routines     *
106
 *                  *
107
 ************************************************************************/
108
static unsigned char *
109
0
xmlSerializeHexCharRef(unsigned char *out, int val) {
110
0
    unsigned char *ptr;
111
112
0
    *out++ = '&';
113
0
    *out++ = '#';
114
0
    *out++ = 'x';
115
0
    if (val < 0x10) ptr = out;
116
0
    else if (val < 0x100) ptr = out + 1;
117
0
    else if (val < 0x1000) ptr = out + 2;
118
0
    else if (val < 0x10000) ptr = out + 3;
119
0
    else if (val < 0x100000) ptr = out + 4;
120
0
    else ptr = out + 5;
121
0
    out = ptr + 1;
122
0
    while (val > 0) {
123
0
  switch (val & 0xF) {
124
0
      case 0: *ptr-- = '0'; break;
125
0
      case 1: *ptr-- = '1'; break;
126
0
      case 2: *ptr-- = '2'; break;
127
0
      case 3: *ptr-- = '3'; break;
128
0
      case 4: *ptr-- = '4'; break;
129
0
      case 5: *ptr-- = '5'; break;
130
0
      case 6: *ptr-- = '6'; break;
131
0
      case 7: *ptr-- = '7'; break;
132
0
      case 8: *ptr-- = '8'; break;
133
0
      case 9: *ptr-- = '9'; break;
134
0
      case 0xA: *ptr-- = 'A'; break;
135
0
      case 0xB: *ptr-- = 'B'; break;
136
0
      case 0xC: *ptr-- = 'C'; break;
137
0
      case 0xD: *ptr-- = 'D'; break;
138
0
      case 0xE: *ptr-- = 'E'; break;
139
0
      case 0xF: *ptr-- = 'F'; break;
140
0
      default: *ptr-- = '0'; break;
141
0
  }
142
0
  val >>= 4;
143
0
    }
144
0
    *out++ = ';';
145
0
    *out = 0;
146
0
    return(out);
147
0
}
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
0
                 const xmlChar* in, int *inlen) {
167
0
    unsigned char* outstart = out;
168
0
    const unsigned char* base = in;
169
0
    unsigned char* outend = out + *outlen;
170
0
    const unsigned char* inend;
171
0
    int val;
172
173
0
    inend = in + (*inlen);
174
175
0
    while ((in < inend) && (out < outend)) {
176
0
  if (*in == '<') {
177
0
      if (outend - out < 4) break;
178
0
      *out++ = '&';
179
0
      *out++ = 'l';
180
0
      *out++ = 't';
181
0
      *out++ = ';';
182
0
      in++;
183
0
      continue;
184
0
  } else if (*in == '>') {
185
0
      if (outend - out < 4) break;
186
0
      *out++ = '&';
187
0
      *out++ = 'g';
188
0
      *out++ = 't';
189
0
      *out++ = ';';
190
0
      in++;
191
0
      continue;
192
0
  } else if (*in == '&') {
193
0
      if (outend - out < 5) break;
194
0
      *out++ = '&';
195
0
      *out++ = 'a';
196
0
      *out++ = 'm';
197
0
      *out++ = 'p';
198
0
      *out++ = ';';
199
0
      in++;
200
0
      continue;
201
0
  } else if (((*in >= 0x20) && (*in < 0x80)) ||
202
0
             (*in == '\n') || (*in == '\t')) {
203
      /*
204
       * default case, just copy !
205
       */
206
0
      *out++ = *in++;
207
0
      continue;
208
0
  } else if (*in >= 0x80) {
209
      /*
210
       * We assume we have UTF-8 input.
211
       */
212
0
      if (outend - out < 11) break;
213
214
0
      if (*in < 0xC0) {
215
0
    xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
216
0
    in++;
217
0
    goto error;
218
0
      } else if (*in < 0xE0) {
219
0
    if (inend - in < 2) break;
220
0
    val = (in[0]) & 0x1F;
221
0
    val <<= 6;
222
0
    val |= (in[1]) & 0x3F;
223
0
    in += 2;
224
0
      } else if (*in < 0xF0) {
225
0
    if (inend - in < 3) break;
226
0
    val = (in[0]) & 0x0F;
227
0
    val <<= 6;
228
0
    val |= (in[1]) & 0x3F;
229
0
    val <<= 6;
230
0
    val |= (in[2]) & 0x3F;
231
0
    in += 3;
232
0
      } else if (*in < 0xF8) {
233
0
    if (inend - in < 4) break;
234
0
    val = (in[0]) & 0x07;
235
0
    val <<= 6;
236
0
    val |= (in[1]) & 0x3F;
237
0
    val <<= 6;
238
0
    val |= (in[2]) & 0x3F;
239
0
    val <<= 6;
240
0
    val |= (in[3]) & 0x3F;
241
0
    in += 4;
242
0
      } else {
243
0
    xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
244
0
    in++;
245
0
    goto error;
246
0
      }
247
0
      if (!IS_CHAR(val)) {
248
0
    xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
249
0
    in++;
250
0
    goto error;
251
0
      }
252
253
      /*
254
       * We could do multiple things here. Just save as a char ref
255
       */
256
0
      out = xmlSerializeHexCharRef(out, val);
257
0
  } else if (IS_BYTE_CHAR(*in)) {
258
0
      if (outend - out < 6) break;
259
0
      out = xmlSerializeHexCharRef(out, *in++);
260
0
  } else {
261
0
      xmlGenericError(xmlGenericErrorContext,
262
0
    "xmlEscapeEntities : char out of range\n");
263
0
      in++;
264
0
      goto error;
265
0
  }
266
0
    }
267
0
    *outlen = out - outstart;
268
0
    *inlen = in - base;
269
0
    return(0);
270
0
error:
271
0
    *outlen = out - outstart;
272
0
    *inlen = in - base;
273
0
    return(-1);
274
0
}
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
0
{
290
0
    int i;
291
0
    int len;
292
293
0
    if (ctxt == NULL) return;
294
0
    if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
295
0
        ctxt->escape = xmlEscapeEntities;
296
0
    len = xmlStrlen((xmlChar *)xmlTreeIndentString);
297
0
    if ((xmlTreeIndentString == NULL) || (len == 0)) {
298
0
        memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
299
0
    } else {
300
0
  ctxt->indent_size = len;
301
0
  ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
302
0
  for (i = 0;i < ctxt->indent_nr;i++)
303
0
      memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
304
0
       ctxt->indent_size);
305
0
        ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
306
0
    }
307
308
0
    if (xmlSaveNoEmptyTags) {
309
0
  ctxt->options |= XML_SAVE_NO_EMPTY;
310
0
    }
311
0
}
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
0
{
394
0
    xmlNodePtr children;
395
396
0
    children = attr->children;
397
0
    while (children != NULL) {
398
0
        switch (children->type) {
399
0
            case XML_TEXT_NODE:
400
0
          xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
401
0
                                  attr, children->content);
402
0
    break;
403
0
            case XML_ENTITY_REF_NODE:
404
0
                xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
405
0
                xmlBufAdd(buf->buffer, children->name,
406
0
                             xmlStrlen(children->name));
407
0
                xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
408
0
                break;
409
0
            default:
410
                /* should not happen unless we have a badly built tree */
411
0
                break;
412
0
        }
413
0
        children = children->next;
414
0
    }
415
0
}
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
0
xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
426
0
    xmlBufferPtr buffer;
427
428
0
    buffer = xmlBufferCreate();
429
0
    if (buffer == NULL) {
430
        /*
431
         * TODO set the error in buf
432
         */
433
0
        return;
434
0
    }
435
0
    xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
436
0
    xmlDumpNotationTable(buffer, table);
437
0
    xmlBufMergeBuffer(buf, buffer);
438
0
}
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
0
xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
450
0
    xmlBufferPtr buffer;
451
452
0
    buffer = xmlBufferCreate();
453
0
    if (buffer == NULL) {
454
        /*
455
         * TODO set the error in buf
456
         */
457
0
        return;
458
0
    }
459
0
    xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
460
0
    xmlDumpElementDecl(buffer, elem);
461
0
    xmlBufMergeBuffer(buf, buffer);
462
0
}
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
0
xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
474
0
    xmlBufferPtr buffer;
475
476
0
    buffer = xmlBufferCreate();
477
0
    if (buffer == NULL) {
478
        /*
479
         * TODO set the error in buf
480
         */
481
0
        return;
482
0
    }
483
0
    xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
484
0
    xmlDumpAttributeDecl(buffer, attr);
485
0
    xmlBufMergeBuffer(buf, buffer);
486
0
}
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
0
xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
497
0
    xmlBufferPtr buffer;
498
499
0
    buffer = xmlBufferCreate();
500
0
    if (buffer == NULL) {
501
        /*
502
         * TODO set the error in buf
503
         */
504
0
        return;
505
0
    }
506
0
    xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
507
0
    xmlDumpEntityDecl(buffer, ent);
508
0
    xmlBufMergeBuffer(buf, buffer);
509
0
}
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
0
xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
592
0
    if ((cur == NULL) || (buf == NULL)) return;
593
0
    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
594
0
  if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
595
0
      return;
596
597
0
  if (ctxt != NULL && ctxt->format == 2)
598
0
      xmlOutputBufferWriteWSNonSig(ctxt, 2);
599
0
  else
600
0
      xmlOutputBufferWrite(buf, 1, " ");
601
602
        /* Within the context of an element attributes */
603
0
  if (cur->prefix != NULL) {
604
0
      xmlOutputBufferWrite(buf, 6, "xmlns:");
605
0
      xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
606
0
  } else
607
0
      xmlOutputBufferWrite(buf, 5, "xmlns");
608
0
  xmlOutputBufferWrite(buf, 1, "=");
609
0
  xmlBufWriteQuotedString(buf->buffer, cur->href);
610
0
    }
611
0
}
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
0
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
636
0
    while (cur != NULL) {
637
0
        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
638
0
  cur = cur->next;
639
0
    }
640
0
}
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
0
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
667
0
    xmlOutputBufferPtr buf;
668
0
    xmlNodePtr cur;
669
0
    int format, level;
670
671
0
    if (dtd == NULL) return;
672
0
    if ((ctxt == NULL) || (ctxt->buf == NULL))
673
0
        return;
674
0
    buf = ctxt->buf;
675
0
    xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
676
0
    xmlOutputBufferWriteString(buf, (const char *)dtd->name);
677
0
    if (dtd->ExternalID != NULL) {
678
0
  xmlOutputBufferWrite(buf, 8, " PUBLIC ");
679
0
  xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
680
0
  xmlOutputBufferWrite(buf, 1, " ");
681
0
  xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
682
0
    }  else if (dtd->SystemID != NULL) {
683
0
  xmlOutputBufferWrite(buf, 8, " SYSTEM ");
684
0
  xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
685
0
    }
686
0
    if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
687
0
        (dtd->attributes == NULL) && (dtd->notations == NULL) &&
688
0
  (dtd->pentities == NULL)) {
689
0
  xmlOutputBufferWrite(buf, 1, ">");
690
0
  return;
691
0
    }
692
0
    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
0
    if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
698
0
        (dtd->doc->intSubset == dtd))) {
699
0
        xmlBufDumpNotationTable(buf->buffer,
700
0
                                (xmlNotationTablePtr) dtd->notations);
701
0
    }
702
0
    format = ctxt->format;
703
0
    level = ctxt->level;
704
0
    ctxt->format = 0;
705
0
    ctxt->level = -1;
706
0
    for (cur = dtd->children; cur != NULL; cur = cur->next) {
707
0
        xmlNodeDumpOutputInternal(ctxt, cur);
708
0
    }
709
0
    ctxt->format = format;
710
0
    ctxt->level = level;
711
0
    xmlOutputBufferWrite(buf, 2, "]>");
712
0
}
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
0
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
723
0
    xmlOutputBufferPtr buf;
724
725
0
    if (cur == NULL) return;
726
0
    buf = ctxt->buf;
727
0
    if (buf == NULL) return;
728
0
    if (ctxt->format == 2)
729
0
        xmlOutputBufferWriteWSNonSig(ctxt, 2);
730
0
    else
731
0
        xmlOutputBufferWrite(buf, 1, " ");
732
0
    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
733
0
        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
734
0
  xmlOutputBufferWrite(buf, 1, ":");
735
0
    }
736
0
    xmlOutputBufferWriteString(buf, (const char *)cur->name);
737
0
    xmlOutputBufferWrite(buf, 2, "=\"");
738
0
    xmlAttrSerializeContent(buf, cur);
739
0
    xmlOutputBufferWrite(buf, 1, "\"");
740
0
}
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
0
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
810
0
    int format = ctxt->format;
811
0
    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
812
0
    xmlAttrPtr attr;
813
0
    xmlChar *start, *end;
814
0
    xmlOutputBufferPtr buf;
815
816
0
    if (cur == NULL) return;
817
0
    buf = ctxt->buf;
818
819
0
    root = cur;
820
0
    parent = cur->parent;
821
0
    while (1) {
822
0
        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
0
        case XML_DTD_NODE:
829
0
            xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
830
0
            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
0
        case XML_ELEMENT_DECL:
842
0
            xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
843
0
            break;
844
845
0
        case XML_ATTRIBUTE_DECL:
846
0
            xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
847
0
            break;
848
849
0
        case XML_ENTITY_DECL:
850
0
            xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
851
0
            break;
852
853
0
        case XML_ELEMENT_NODE:
854
0
      if ((cur != root) && (ctxt->format == 1) &&
855
0
                (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
0
            if ((cur->parent != parent) && (cur->children != NULL)) {
867
0
                xmlNodeDumpOutputInternal(ctxt, cur);
868
0
                break;
869
0
            }
870
871
0
            xmlOutputBufferWrite(buf, 1, "<");
872
0
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
873
0
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
874
0
                xmlOutputBufferWrite(buf, 1, ":");
875
0
            }
876
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
877
0
            if (cur->nsDef)
878
0
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
879
0
            for (attr = cur->properties; attr != NULL; attr = attr->next)
880
0
                xmlAttrDumpOutput(ctxt, attr);
881
882
0
            if (cur->children == NULL) {
883
0
                if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
884
0
                    if (ctxt->format == 2)
885
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
886
0
                    xmlOutputBufferWrite(buf, 2, "/>");
887
0
                } 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
0
            } else {
902
0
                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
0
                if (ctxt->format == 2)
916
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 1);
917
0
                xmlOutputBufferWrite(buf, 1, ">");
918
0
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
919
0
                if (ctxt->level >= 0) ctxt->level++;
920
0
                parent = cur;
921
0
                cur = cur->children;
922
0
                continue;
923
0
            }
924
925
0
            break;
926
927
0
        case XML_TEXT_NODE:
928
0
      if (cur->content == NULL)
929
0
                break;
930
0
      if (cur->name != xmlStringTextNoenc) {
931
0
                xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
932
0
      } else {
933
    /*
934
     * Disable escaping, needed for XSLT
935
     */
936
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
937
0
      }
938
0
      break;
939
940
0
        case XML_PI_NODE:
941
0
      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
0
            if (cur->content != NULL) {
948
0
                xmlOutputBufferWrite(buf, 2, "<?");
949
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
950
0
                if (cur->content != NULL) {
951
0
                    if (ctxt->format == 2)
952
0
                        xmlOutputBufferWriteWSNonSig(ctxt, 0);
953
0
                    else
954
0
                        xmlOutputBufferWrite(buf, 1, " ");
955
0
                    xmlOutputBufferWriteString(buf,
956
0
                            (const char *)cur->content);
957
0
                }
958
0
                xmlOutputBufferWrite(buf, 2, "?>");
959
0
            } else {
960
0
                xmlOutputBufferWrite(buf, 2, "<?");
961
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
962
0
                if (ctxt->format == 2)
963
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
964
0
                xmlOutputBufferWrite(buf, 2, "?>");
965
0
            }
966
0
            break;
967
968
0
        case XML_COMMENT_NODE:
969
0
      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
0
            if (cur->content != NULL) {
976
0
                xmlOutputBufferWrite(buf, 4, "<!--");
977
0
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
978
0
                xmlOutputBufferWrite(buf, 3, "-->");
979
0
            }
980
0
            break;
981
982
0
        case XML_ENTITY_REF_NODE:
983
0
            xmlOutputBufferWrite(buf, 1, "&");
984
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
985
0
            xmlOutputBufferWrite(buf, 1, ";");
986
0
            break;
987
988
0
        case XML_CDATA_SECTION_NODE:
989
0
            if (cur->content == NULL || *cur->content == '\0') {
990
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
991
0
            } else {
992
0
                start = end = cur->content;
993
0
                while (*end != '\0') {
994
0
                    if ((*end == ']') && (*(end + 1) == ']') &&
995
0
                        (*(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
0
                    end++;
1004
0
                }
1005
0
                if (start != end) {
1006
0
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1007
0
                    xmlOutputBufferWriteString(buf, (const char *)start);
1008
0
                    xmlOutputBufferWrite(buf, 3, "]]>");
1009
0
                }
1010
0
            }
1011
0
            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
0
        default:
1022
0
            break;
1023
0
        }
1024
1025
0
        while (1) {
1026
0
            if (cur == root)
1027
0
                return;
1028
0
            if ((ctxt->format == 1) &&
1029
0
                (cur->type != XML_XINCLUDE_START) &&
1030
0
                (cur->type != XML_XINCLUDE_END))
1031
0
                xmlOutputBufferWrite(buf, 1, "\n");
1032
0
            if (cur->next != NULL) {
1033
0
                cur = cur->next;
1034
0
                break;
1035
0
            }
1036
1037
0
            cur = parent;
1038
            /* cur->parent was validated when descending. */
1039
0
            parent = cur->parent;
1040
1041
0
            if (cur->type == XML_ELEMENT_NODE) {
1042
0
                if (ctxt->level > 0) ctxt->level--;
1043
0
                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
0
                xmlOutputBufferWrite(buf, 2, "</");
1050
0
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1051
0
                    xmlOutputBufferWriteString(buf,
1052
0
                            (const char *)cur->ns->prefix);
1053
0
                    xmlOutputBufferWrite(buf, 1, ":");
1054
0
                }
1055
1056
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1057
0
                if (ctxt->format == 2)
1058
0
                    xmlOutputBufferWriteWSNonSig(ctxt, 0);
1059
0
                xmlOutputBufferWrite(buf, 1, ">");
1060
1061
0
                if (cur == unformattedNode) {
1062
0
                    ctxt->format = format;
1063
0
                    unformattedNode = NULL;
1064
0
                }
1065
0
            }
1066
0
        }
1067
0
    }
1068
0
}
1069
1070
/**
1071
 * xmlDocContentDumpOutput:
1072
 * @cur:  the document
1073
 *
1074
 * Dump an XML document.
1075
 */
1076
static int
1077
0
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1078
0
#ifdef LIBXML_HTML_ENABLED
1079
0
    xmlDtdPtr dtd;
1080
0
    int is_xhtml = 0;
1081
0
#endif
1082
0
    const xmlChar *oldenc = cur->encoding;
1083
0
    const xmlChar *oldctxtenc = ctxt->encoding;
1084
0
    const xmlChar *encoding = ctxt->encoding;
1085
0
    xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1086
0
    xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1087
0
    xmlOutputBufferPtr buf = ctxt->buf;
1088
0
    xmlCharEncoding enc;
1089
0
    int switched_encoding = 0;
1090
1091
0
    xmlInitParser();
1092
1093
0
    if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1094
0
        (cur->type != XML_DOCUMENT_NODE))
1095
0
   return(-1);
1096
1097
0
    if (ctxt->encoding != NULL) {
1098
0
        cur->encoding = BAD_CAST ctxt->encoding;
1099
0
    } else if (cur->encoding != NULL) {
1100
0
  encoding = cur->encoding;
1101
0
    }
1102
1103
0
    if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1104
0
         ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1105
0
         ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1106
0
        (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
0
    } else if ((cur->type == XML_DOCUMENT_NODE) ||
1134
0
               (ctxt->options & XML_SAVE_AS_XML) ||
1135
0
               (ctxt->options & XML_SAVE_XHTML)) {
1136
0
  enc = xmlParseCharEncoding((const char*) encoding);
1137
0
  if ((encoding != NULL) && (oldctxtenc == NULL) &&
1138
0
      (buf->encoder == NULL) && (buf->conv == NULL) &&
1139
0
      ((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
0
  if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1165
0
      xmlOutputBufferWrite(buf, 14, "<?xml version=");
1166
0
      if (cur->version != NULL)
1167
0
    xmlBufWriteQuotedString(buf->buffer, cur->version);
1168
0
      else
1169
0
    xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1170
0
      if (encoding != NULL) {
1171
0
    xmlOutputBufferWrite(buf, 10, " encoding=");
1172
0
    xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1173
0
      }
1174
0
      switch (cur->standalone) {
1175
0
    case 0:
1176
0
        xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1177
0
        break;
1178
0
    case 1:
1179
0
        xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1180
0
        break;
1181
0
      }
1182
0
      xmlOutputBufferWrite(buf, 3, "?>\n");
1183
0
  }
1184
1185
0
#ifdef LIBXML_HTML_ENABLED
1186
0
        if (ctxt->options & XML_SAVE_XHTML)
1187
0
            is_xhtml = 1;
1188
0
  if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1189
0
      dtd = xmlGetIntSubset(cur);
1190
0
      if (dtd != NULL) {
1191
0
    is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1192
0
    if (is_xhtml < 0) is_xhtml = 0;
1193
0
      }
1194
0
  }
1195
0
#endif
1196
0
  if (cur->children != NULL) {
1197
0
      xmlNodePtr child = cur->children;
1198
1199
0
      while (child != NULL) {
1200
0
    ctxt->level = 0;
1201
0
#ifdef LIBXML_HTML_ENABLED
1202
0
    if (is_xhtml)
1203
0
        xhtmlNodeDumpOutput(ctxt, child);
1204
0
    else
1205
0
#endif
1206
0
        xmlNodeDumpOutputInternal(ctxt, child);
1207
0
                if ((child->type != XML_XINCLUDE_START) &&
1208
0
                    (child->type != XML_XINCLUDE_END))
1209
0
                    xmlOutputBufferWrite(buf, 1, "\n");
1210
0
    child = child->next;
1211
0
      }
1212
0
  }
1213
0
    }
1214
1215
    /*
1216
     * Restore the state of the saving context at the end of the document
1217
     */
1218
0
    if ((switched_encoding) && (oldctxtenc == NULL)) {
1219
0
  xmlSaveClearEncoding(ctxt);
1220
0
  ctxt->escape = oldescape;
1221
0
  ctxt->escapeAttr = oldescapeAttr;
1222
0
    }
1223
0
    cur->encoding = oldenc;
1224
0
    return(0);
1225
0
}
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
0
xhtmlIsEmpty(xmlNodePtr node) {
1244
0
    if (node == NULL)
1245
0
  return(-1);
1246
0
    if (node->type != XML_ELEMENT_NODE)
1247
0
  return(0);
1248
0
    if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1249
0
  return(0);
1250
0
    if (node->children != NULL)
1251
0
  return(0);
1252
0
    switch (node->name[0]) {
1253
0
  case 'a':
1254
0
      if (xmlStrEqual(node->name, BAD_CAST "area"))
1255
0
    return(1);
1256
0
      return(0);
1257
0
  case 'b':
1258
0
      if (xmlStrEqual(node->name, BAD_CAST "br"))
1259
0
    return(1);
1260
0
      if (xmlStrEqual(node->name, BAD_CAST "base"))
1261
0
    return(1);
1262
0
      if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1263
0
    return(1);
1264
0
      return(0);
1265
0
  case 'c':
1266
0
      if (xmlStrEqual(node->name, BAD_CAST "col"))
1267
0
    return(1);
1268
0
      return(0);
1269
0
  case 'f':
1270
0
      if (xmlStrEqual(node->name, BAD_CAST "frame"))
1271
0
    return(1);
1272
0
      return(0);
1273
0
  case 'h':
1274
0
      if (xmlStrEqual(node->name, BAD_CAST "hr"))
1275
0
    return(1);
1276
0
      return(0);
1277
0
  case 'i':
1278
0
      if (xmlStrEqual(node->name, BAD_CAST "img"))
1279
0
    return(1);
1280
0
      if (xmlStrEqual(node->name, BAD_CAST "input"))
1281
0
    return(1);
1282
0
      if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1283
0
    return(1);
1284
0
      return(0);
1285
0
  case 'l':
1286
0
      if (xmlStrEqual(node->name, BAD_CAST "link"))
1287
0
    return(1);
1288
0
      return(0);
1289
0
  case 'm':
1290
0
      if (xmlStrEqual(node->name, BAD_CAST "meta"))
1291
0
    return(1);
1292
0
      return(0);
1293
0
  case 'p':
1294
0
      if (xmlStrEqual(node->name, BAD_CAST "param"))
1295
0
    return(1);
1296
0
      return(0);
1297
0
    }
1298
0
    return(0);
1299
0
}
1300
1301
/**
1302
 * xhtmlAttrListDumpOutput:
1303
 * @cur:  the first attribute pointer
1304
 *
1305
 * Dump a list of XML attributes
1306
 */
1307
static void
1308
0
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1309
0
    xmlAttrPtr xml_lang = NULL;
1310
0
    xmlAttrPtr lang = NULL;
1311
0
    xmlAttrPtr name = NULL;
1312
0
    xmlAttrPtr id = NULL;
1313
0
    xmlNodePtr parent;
1314
0
    xmlOutputBufferPtr buf;
1315
1316
0
    if (cur == NULL) return;
1317
0
    buf = ctxt->buf;
1318
0
    parent = cur->parent;
1319
0
    while (cur != NULL) {
1320
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1321
0
      id = cur;
1322
0
  else
1323
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1324
0
      name = cur;
1325
0
  else
1326
0
  if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1327
0
      lang = cur;
1328
0
  else
1329
0
  if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1330
0
      (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1331
0
      xml_lang = cur;
1332
0
  else if ((cur->ns == NULL) &&
1333
0
     ((cur->children == NULL) ||
1334
0
      (cur->children->content == NULL) ||
1335
0
      (cur->children->content[0] == 0)) &&
1336
0
     (htmlIsBooleanAttr(cur->name))) {
1337
0
      if (cur->children != NULL)
1338
0
    xmlFreeNode(cur->children);
1339
0
      cur->children = xmlNewDocText(cur->doc, cur->name);
1340
0
      if (cur->children != NULL)
1341
0
    cur->children->parent = (xmlNodePtr) cur;
1342
0
  }
1343
0
        xmlAttrDumpOutput(ctxt, cur);
1344
0
  cur = cur->next;
1345
0
    }
1346
    /*
1347
     * C.8
1348
     */
1349
0
    if ((name != NULL) && (id == NULL)) {
1350
0
  if ((parent != NULL) && (parent->name != NULL) &&
1351
0
      ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1352
0
       (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1353
0
       (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1354
0
       (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1355
0
       (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1356
0
       (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1357
0
       (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1358
0
       (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1359
0
       (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1360
0
      xmlOutputBufferWrite(buf, 5, " id=\"");
1361
0
      xmlAttrSerializeContent(buf, name);
1362
0
      xmlOutputBufferWrite(buf, 1, "\"");
1363
0
  }
1364
0
    }
1365
    /*
1366
     * C.7.
1367
     */
1368
0
    if ((lang != NULL) && (xml_lang == NULL)) {
1369
0
  xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1370
0
  xmlAttrSerializeContent(buf, lang);
1371
0
  xmlOutputBufferWrite(buf, 1, "\"");
1372
0
    } else
1373
0
    if ((xml_lang != NULL) && (lang == NULL)) {
1374
0
  xmlOutputBufferWrite(buf, 7, " lang=\"");
1375
0
  xmlAttrSerializeContent(buf, xml_lang);
1376
0
  xmlOutputBufferWrite(buf, 1, "\"");
1377
0
    }
1378
0
}
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
0
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1393
0
    int format = ctxt->format, addmeta;
1394
0
    xmlNodePtr tmp, root, unformattedNode = NULL;
1395
0
    xmlChar *start, *end;
1396
0
    xmlOutputBufferPtr buf = ctxt->buf;
1397
1398
0
    if (cur == NULL) return;
1399
1400
0
    root = cur;
1401
0
    while (1) {
1402
0
        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
0
        case XML_DTD_NODE:
1413
0
            xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1414
0
      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
0
        case XML_ELEMENT_NODE:
1436
0
            addmeta = 0;
1437
1438
0
      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
0
            xmlOutputBufferWrite(buf, 1, "<");
1445
0
            if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1446
0
                xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1447
0
                xmlOutputBufferWrite(buf, 1, ":");
1448
0
            }
1449
1450
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1451
0
            if (cur->nsDef)
1452
0
                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1453
0
            if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1454
0
                (cur->ns == NULL) && (cur->nsDef == NULL))) {
1455
                /*
1456
                 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1457
                 */
1458
0
                xmlOutputBufferWriteString(buf,
1459
0
                        " xmlns=\"http://www.w3.org/1999/xhtml\"");
1460
0
            }
1461
0
            if (cur->properties != NULL)
1462
0
                xhtmlAttrListDumpOutput(ctxt, cur->properties);
1463
1464
0
            if ((cur->parent != NULL) &&
1465
0
                (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1466
0
                xmlStrEqual(cur->name, BAD_CAST"head") &&
1467
0
                xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1468
1469
0
                tmp = cur->children;
1470
0
                while (tmp != NULL) {
1471
0
                    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
0
                    tmp = tmp->next;
1485
0
                }
1486
0
                if (tmp == NULL)
1487
0
                    addmeta = 1;
1488
0
            }
1489
1490
0
            if (cur->children == NULL) {
1491
0
                if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1492
0
                    ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1493
                    /*
1494
                     * C.2. Empty Elements
1495
                     */
1496
0
                    xmlOutputBufferWrite(buf, 3, " />");
1497
0
                } else {
1498
0
                    if (addmeta == 1) {
1499
0
                        xmlOutputBufferWrite(buf, 1, ">");
1500
0
                        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
0
                        xmlOutputBufferWriteString(buf,
1509
0
                                "<meta http-equiv=\"Content-Type\" "
1510
0
                                "content=\"text/html; charset=");
1511
0
                        if (ctxt->encoding) {
1512
0
                            xmlOutputBufferWriteString(buf,
1513
0
                                    (const char *)ctxt->encoding);
1514
0
                        } else {
1515
0
                            xmlOutputBufferWrite(buf, 5, "UTF-8");
1516
0
                        }
1517
0
                        xmlOutputBufferWrite(buf, 4, "\" />");
1518
0
                        if (ctxt->format == 1)
1519
0
                            xmlOutputBufferWrite(buf, 1, "\n");
1520
0
                    } else {
1521
0
                        xmlOutputBufferWrite(buf, 1, ">");
1522
0
                    }
1523
                    /*
1524
                     * C.3. Element Minimization and Empty Element Content
1525
                     */
1526
0
                    xmlOutputBufferWrite(buf, 2, "</");
1527
0
                    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1528
0
                        xmlOutputBufferWriteString(buf,
1529
0
                                (const char *)cur->ns->prefix);
1530
0
                        xmlOutputBufferWrite(buf, 1, ":");
1531
0
                    }
1532
0
                    xmlOutputBufferWriteString(buf, (const char *)cur->name);
1533
0
                    xmlOutputBufferWrite(buf, 1, ">");
1534
0
                }
1535
0
            } else {
1536
0
                xmlOutputBufferWrite(buf, 1, ">");
1537
0
                if (addmeta == 1) {
1538
0
                    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
0
                    xmlOutputBufferWriteString(buf,
1547
0
                            "<meta http-equiv=\"Content-Type\" "
1548
0
                            "content=\"text/html; charset=");
1549
0
                    if (ctxt->encoding) {
1550
0
                        xmlOutputBufferWriteString(buf,
1551
0
                                (const char *)ctxt->encoding);
1552
0
                    } else {
1553
0
                        xmlOutputBufferWrite(buf, 5, "UTF-8");
1554
0
                    }
1555
0
                    xmlOutputBufferWrite(buf, 4, "\" />");
1556
0
                }
1557
1558
0
                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
0
                if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1572
0
                if (ctxt->level >= 0) ctxt->level++;
1573
0
                cur = cur->children;
1574
0
                continue;
1575
0
            }
1576
1577
0
            break;
1578
1579
0
        case XML_TEXT_NODE:
1580
0
      if (cur->content == NULL)
1581
0
                break;
1582
0
      if ((cur->name == xmlStringText) ||
1583
0
    (cur->name != xmlStringTextNoenc)) {
1584
0
                xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1585
0
      } else {
1586
    /*
1587
     * Disable escaping, needed for XSLT
1588
     */
1589
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1590
0
      }
1591
0
      break;
1592
1593
0
        case XML_PI_NODE:
1594
0
            if (cur->content != NULL) {
1595
0
                xmlOutputBufferWrite(buf, 2, "<?");
1596
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1597
0
                if (cur->content != NULL) {
1598
0
                    xmlOutputBufferWrite(buf, 1, " ");
1599
0
                    xmlOutputBufferWriteString(buf,
1600
0
                            (const char *)cur->content);
1601
0
                }
1602
0
                xmlOutputBufferWrite(buf, 2, "?>");
1603
0
            } else {
1604
0
                xmlOutputBufferWrite(buf, 2, "<?");
1605
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1606
0
                xmlOutputBufferWrite(buf, 2, "?>");
1607
0
            }
1608
0
            break;
1609
1610
0
        case XML_COMMENT_NODE:
1611
0
            if (cur->content != NULL) {
1612
0
                xmlOutputBufferWrite(buf, 4, "<!--");
1613
0
                xmlOutputBufferWriteString(buf, (const char *)cur->content);
1614
0
                xmlOutputBufferWrite(buf, 3, "-->");
1615
0
            }
1616
0
            break;
1617
1618
0
        case XML_ENTITY_REF_NODE:
1619
0
            xmlOutputBufferWrite(buf, 1, "&");
1620
0
            xmlOutputBufferWriteString(buf, (const char *)cur->name);
1621
0
            xmlOutputBufferWrite(buf, 1, ";");
1622
0
            break;
1623
1624
0
        case XML_CDATA_SECTION_NODE:
1625
0
            if (cur->content == NULL || *cur->content == '\0') {
1626
0
                xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1627
0
            } else {
1628
0
                start = end = cur->content;
1629
0
                while (*end != '\0') {
1630
0
                    if (*end == ']' && *(end + 1) == ']' &&
1631
0
                        *(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
0
                    end++;
1640
0
                }
1641
0
                if (start != end) {
1642
0
                    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1643
0
                    xmlOutputBufferWriteString(buf, (const char *)start);
1644
0
                    xmlOutputBufferWrite(buf, 3, "]]>");
1645
0
                }
1646
0
            }
1647
0
            break;
1648
1649
0
        case XML_ATTRIBUTE_NODE:
1650
0
            xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1651
0
      break;
1652
1653
0
        default:
1654
0
            break;
1655
0
        }
1656
1657
0
        while (1) {
1658
0
            if (cur == root)
1659
0
                return;
1660
0
            if (ctxt->format == 1)
1661
0
                xmlOutputBufferWrite(buf, 1, "\n");
1662
0
            if (cur->next != NULL) {
1663
0
                cur = cur->next;
1664
0
                break;
1665
0
            }
1666
1667
            /*
1668
             * The parent should never be NULL here but we want to handle
1669
             * corrupted documents gracefully.
1670
             */
1671
0
            if (cur->parent == NULL)
1672
0
                return;
1673
0
            cur = cur->parent;
1674
1675
0
            if (cur->type == XML_ELEMENT_NODE) {
1676
0
                if (ctxt->level > 0) ctxt->level--;
1677
0
                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
0
                xmlOutputBufferWrite(buf, 2, "</");
1684
0
                if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1685
0
                    xmlOutputBufferWriteString(buf,
1686
0
                            (const char *)cur->ns->prefix);
1687
0
                    xmlOutputBufferWrite(buf, 1, ":");
1688
0
                }
1689
1690
0
                xmlOutputBufferWriteString(buf, (const char *)cur->name);
1691
0
                xmlOutputBufferWrite(buf, 1, ">");
1692
1693
0
                if (cur == unformattedNode) {
1694
0
                    ctxt->format = format;
1695
0
                    unformattedNode = NULL;
1696
0
                }
1697
0
            }
1698
0
        }
1699
0
    }
1700
0
}
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
0
{
1971
0
    xmlChar *base, *cur;
1972
1973
0
    if (string == NULL)
1974
0
        return;
1975
0
    base = cur = (xmlChar *) string;
1976
0
    while (*cur != 0) {
1977
0
        if (*cur == '\n') {
1978
0
            if (base != cur)
1979
0
                xmlBufAdd(buf, base, cur - base);
1980
0
            xmlBufAdd(buf, BAD_CAST "&#10;", 5);
1981
0
            cur++;
1982
0
            base = cur;
1983
0
        } else if (*cur == '\r') {
1984
0
            if (base != cur)
1985
0
                xmlBufAdd(buf, base, cur - base);
1986
0
            xmlBufAdd(buf, BAD_CAST "&#13;", 5);
1987
0
            cur++;
1988
0
            base = cur;
1989
0
        } else if (*cur == '\t') {
1990
0
            if (base != cur)
1991
0
                xmlBufAdd(buf, base, cur - base);
1992
0
            xmlBufAdd(buf, BAD_CAST "&#9;", 4);
1993
0
            cur++;
1994
0
            base = cur;
1995
0
        } else if (*cur == '"') {
1996
0
            if (base != cur)
1997
0
                xmlBufAdd(buf, base, cur - base);
1998
0
            xmlBufAdd(buf, BAD_CAST "&quot;", 6);
1999
0
            cur++;
2000
0
            base = cur;
2001
0
        } else if (*cur == '<') {
2002
0
            if (base != cur)
2003
0
                xmlBufAdd(buf, base, cur - base);
2004
0
            xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2005
0
            cur++;
2006
0
            base = cur;
2007
0
        } else if (*cur == '>') {
2008
0
            if (base != cur)
2009
0
                xmlBufAdd(buf, base, cur - base);
2010
0
            xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2011
0
            cur++;
2012
0
            base = cur;
2013
0
        } else if (*cur == '&') {
2014
0
            if (base != cur)
2015
0
                xmlBufAdd(buf, base, cur - base);
2016
0
            xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2017
0
            cur++;
2018
0
            base = cur;
2019
0
        } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2020
0
             ((doc == NULL) || (doc->encoding == NULL))) {
2021
            /*
2022
             * We assume we have UTF-8 content.
2023
             */
2024
0
            unsigned char tmp[12];
2025
0
            int val = 0, l = 1;
2026
2027
0
            if (base != cur)
2028
0
                xmlBufAdd(buf, base, cur - base);
2029
0
            if (*cur < 0xC0) {
2030
0
                xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2031
0
    xmlSerializeHexCharRef(tmp, *cur);
2032
0
                xmlBufAdd(buf, (xmlChar *) tmp, -1);
2033
0
                cur++;
2034
0
                base = cur;
2035
0
                continue;
2036
0
            } else if (*cur < 0xE0) {
2037
0
                val = (cur[0]) & 0x1F;
2038
0
                val <<= 6;
2039
0
                val |= (cur[1]) & 0x3F;
2040
0
                l = 2;
2041
0
            } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2042
0
                val = (cur[0]) & 0x0F;
2043
0
                val <<= 6;
2044
0
                val |= (cur[1]) & 0x3F;
2045
0
                val <<= 6;
2046
0
                val |= (cur[2]) & 0x3F;
2047
0
                l = 3;
2048
0
            } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2049
0
                val = (cur[0]) & 0x07;
2050
0
                val <<= 6;
2051
0
                val |= (cur[1]) & 0x3F;
2052
0
                val <<= 6;
2053
0
                val |= (cur[2]) & 0x3F;
2054
0
                val <<= 6;
2055
0
                val |= (cur[3]) & 0x3F;
2056
0
                l = 4;
2057
0
            }
2058
0
            if ((l == 1) || (!IS_CHAR(val))) {
2059
0
                xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2060
0
    xmlSerializeHexCharRef(tmp, *cur);
2061
0
                xmlBufAdd(buf, (xmlChar *) tmp, -1);
2062
0
                cur++;
2063
0
                base = cur;
2064
0
                continue;
2065
0
            }
2066
            /*
2067
             * We could do multiple things here. Just save
2068
             * as a char ref
2069
             */
2070
0
      xmlSerializeHexCharRef(tmp, val);
2071
0
            xmlBufAdd(buf, (xmlChar *) tmp, -1);
2072
0
            cur += l;
2073
0
            base = cur;
2074
0
        } else {
2075
0
            cur++;
2076
0
        }
2077
0
    }
2078
0
    if (base != cur)
2079
0
        xmlBufAdd(buf, base, cur - base);
2080
0
}
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
0
    int format) {
2326
0
    xmlSaveCtxt ctxt;
2327
0
    int                         dummy = 0;
2328
0
    xmlOutputBufferPtr          out_buff = NULL;
2329
0
    xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
2330
2331
0
    if (doc_txt_len == NULL) {
2332
0
        doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
2333
0
    }
2334
2335
0
    if (doc_txt_ptr == NULL) {
2336
0
        *doc_txt_len = 0;
2337
0
        return;
2338
0
    }
2339
2340
0
    *doc_txt_ptr = NULL;
2341
0
    *doc_txt_len = 0;
2342
2343
0
    if (out_doc == NULL) {
2344
        /*  No document, no output  */
2345
0
        return;
2346
0
    }
2347
2348
    /*
2349
     *  Validate the encoding value, if provided.
2350
     *  This logic is copied from xmlSaveFileEnc.
2351
     */
2352
2353
0
    if (txt_encoding == NULL)
2354
0
  txt_encoding = (const char *) out_doc->encoding;
2355
0
    if (txt_encoding != NULL) {
2356
0
  conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2357
0
  if ( conv_hdlr == NULL ) {
2358
0
      xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2359
0
           txt_encoding);
2360
0
      return;
2361
0
  }
2362
0
    }
2363
2364
0
    if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2365
0
        xmlSaveErrMemory("creating buffer");
2366
0
        xmlCharEncCloseFunc(conv_hdlr);
2367
0
        return;
2368
0
    }
2369
2370
0
    memset(&ctxt, 0, sizeof(ctxt));
2371
0
    ctxt.buf = out_buff;
2372
0
    ctxt.level = 0;
2373
0
    ctxt.format = format ? 1 : 0;
2374
0
    ctxt.encoding = (const xmlChar *) txt_encoding;
2375
0
    xmlSaveCtxtInit(&ctxt);
2376
0
    ctxt.options |= XML_SAVE_AS_XML;
2377
0
    xmlDocContentDumpOutput(&ctxt, out_doc);
2378
0
    xmlOutputBufferFlush(out_buff);
2379
0
    if (out_buff->conv != NULL) {
2380
0
  *doc_txt_len = xmlBufUse(out_buff->conv);
2381
0
  *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2382
0
    } else {
2383
0
  *doc_txt_len = xmlBufUse(out_buff->buffer);
2384
0
  *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2385
0
    }
2386
0
    (void)xmlOutputBufferClose(out_buff);
2387
2388
0
    if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2389
0
        *doc_txt_len = 0;
2390
0
        xmlSaveErrMemory("creating output");
2391
0
    }
2392
2393
0
    return;
2394
0
}
2395
2396
/**
2397
 * xmlDocDumpMemory:
2398
 * @cur:  the document
2399
 * @mem:  OUT: the memory pointer
2400
 * @size:  OUT: the memory length
2401
 *
2402
 * Dump an XML document in memory and return the #xmlChar * and it's size
2403
 * in bytes. It's up to the caller to free the memory with xmlFree().
2404
 * The resulting byte array is zero terminated, though the last 0 is not
2405
 * included in the returned size.
2406
 */
2407
void
2408
0
xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2409
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2410
0
}
2411
2412
/**
2413
 * xmlDocDumpFormatMemory:
2414
 * @cur:  the document
2415
 * @mem:  OUT: the memory pointer
2416
 * @size:  OUT: the memory length
2417
 * @format:  should formatting spaces been added
2418
 *
2419
 *
2420
 * Dump an XML document in memory and return the #xmlChar * and it's size.
2421
 * It's up to the caller to free the memory with xmlFree().
2422
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2423
 * or xmlKeepBlanksDefault(0) was called
2424
 */
2425
void
2426
0
xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2427
0
    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2428
0
}
2429
2430
/**
2431
 * xmlDocDumpMemoryEnc:
2432
 * @out_doc:  Document to generate XML text from
2433
 * @doc_txt_ptr:  Memory pointer for allocated XML text
2434
 * @doc_txt_len:  Length of the generated XML text
2435
 * @txt_encoding:  Character encoding to use when generating XML text
2436
 *
2437
 * Dump the current DOM tree into memory using the character encoding specified
2438
 * by the caller.  Note it is up to the caller of this function to free the
2439
 * allocated memory with xmlFree().
2440
 */
2441
2442
void
2443
xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2444
0
              int * doc_txt_len, const char * txt_encoding) {
2445
0
    xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2446
0
                        txt_encoding, 0);
2447
0
}
2448
2449
/**
2450
 * xmlDocFormatDump:
2451
 * @f:  the FILE*
2452
 * @cur:  the document
2453
 * @format: should formatting spaces been added
2454
 *
2455
 * Dump an XML document to an open FILE.
2456
 *
2457
 * returns: the number of bytes written or -1 in case of failure.
2458
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2459
 * or xmlKeepBlanksDefault(0) was called
2460
 */
2461
int
2462
0
xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2463
0
    xmlSaveCtxt ctxt;
2464
0
    xmlOutputBufferPtr buf;
2465
0
    const char * encoding;
2466
0
    xmlCharEncodingHandlerPtr handler = NULL;
2467
0
    int ret;
2468
2469
0
    if (cur == NULL) {
2470
#ifdef DEBUG_TREE
2471
        xmlGenericError(xmlGenericErrorContext,
2472
    "xmlDocDump : document == NULL\n");
2473
#endif
2474
0
  return(-1);
2475
0
    }
2476
0
    encoding = (const char *) cur->encoding;
2477
2478
0
    if (encoding != NULL) {
2479
0
  handler = xmlFindCharEncodingHandler(encoding);
2480
0
  if (handler == NULL) {
2481
0
      xmlFree((char *) cur->encoding);
2482
0
      cur->encoding = NULL;
2483
0
      encoding = NULL;
2484
0
  }
2485
0
    }
2486
0
    buf = xmlOutputBufferCreateFile(f, handler);
2487
0
    if (buf == NULL) return(-1);
2488
0
    memset(&ctxt, 0, sizeof(ctxt));
2489
0
    ctxt.buf = buf;
2490
0
    ctxt.level = 0;
2491
0
    ctxt.format = format ? 1 : 0;
2492
0
    ctxt.encoding = (const xmlChar *) encoding;
2493
0
    xmlSaveCtxtInit(&ctxt);
2494
0
    ctxt.options |= XML_SAVE_AS_XML;
2495
0
    xmlDocContentDumpOutput(&ctxt, cur);
2496
2497
0
    ret = xmlOutputBufferClose(buf);
2498
0
    return(ret);
2499
0
}
2500
2501
/**
2502
 * xmlDocDump:
2503
 * @f:  the FILE*
2504
 * @cur:  the document
2505
 *
2506
 * Dump an XML document to an open FILE.
2507
 *
2508
 * returns: the number of bytes written or -1 in case of failure.
2509
 */
2510
int
2511
0
xmlDocDump(FILE *f, xmlDocPtr cur) {
2512
0
    return(xmlDocFormatDump (f, cur, 0));
2513
0
}
2514
2515
/**
2516
 * xmlSaveFileTo:
2517
 * @buf:  an output I/O buffer
2518
 * @cur:  the document
2519
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2520
 *
2521
 * Dump an XML document to an I/O buffer.
2522
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2523
 * after this call.
2524
 *
2525
 * returns: the number of bytes written or -1 in case of failure.
2526
 */
2527
int
2528
0
xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2529
0
    xmlSaveCtxt ctxt;
2530
0
    int ret;
2531
2532
0
    if (buf == NULL) return(-1);
2533
0
    if (cur == NULL) {
2534
0
        xmlOutputBufferClose(buf);
2535
0
  return(-1);
2536
0
    }
2537
0
    memset(&ctxt, 0, sizeof(ctxt));
2538
0
    ctxt.buf = buf;
2539
0
    ctxt.level = 0;
2540
0
    ctxt.format = 0;
2541
0
    ctxt.encoding = (const xmlChar *) encoding;
2542
0
    xmlSaveCtxtInit(&ctxt);
2543
0
    ctxt.options |= XML_SAVE_AS_XML;
2544
0
    xmlDocContentDumpOutput(&ctxt, cur);
2545
0
    ret = xmlOutputBufferClose(buf);
2546
0
    return(ret);
2547
0
}
2548
2549
/**
2550
 * xmlSaveFormatFileTo:
2551
 * @buf:  an output I/O buffer
2552
 * @cur:  the document
2553
 * @encoding:  the encoding if any assuming the I/O layer handles the transcoding
2554
 * @format: should formatting spaces been added
2555
 *
2556
 * Dump an XML document to an I/O buffer.
2557
 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2558
 * after this call.
2559
 *
2560
 * returns: the number of bytes written or -1 in case of failure.
2561
 */
2562
int
2563
xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2564
                    const char *encoding, int format)
2565
0
{
2566
0
    xmlSaveCtxt ctxt;
2567
0
    int ret;
2568
2569
0
    if (buf == NULL) return(-1);
2570
0
    if ((cur == NULL) ||
2571
0
        ((cur->type != XML_DOCUMENT_NODE) &&
2572
0
   (cur->type != XML_HTML_DOCUMENT_NODE))) {
2573
0
        xmlOutputBufferClose(buf);
2574
0
  return(-1);
2575
0
    }
2576
0
    memset(&ctxt, 0, sizeof(ctxt));
2577
0
    ctxt.buf = buf;
2578
0
    ctxt.level = 0;
2579
0
    ctxt.format = format ? 1 : 0;
2580
0
    ctxt.encoding = (const xmlChar *) encoding;
2581
0
    xmlSaveCtxtInit(&ctxt);
2582
0
    ctxt.options |= XML_SAVE_AS_XML;
2583
0
    xmlDocContentDumpOutput(&ctxt, cur);
2584
0
    ret = xmlOutputBufferClose(buf);
2585
0
    return (ret);
2586
0
}
2587
2588
/**
2589
 * xmlSaveFormatFileEnc:
2590
 * @filename:  the filename or URL to output
2591
 * @cur:  the document being saved
2592
 * @encoding:  the name of the encoding to use or NULL.
2593
 * @format:  should formatting spaces be added.
2594
 *
2595
 * Dump an XML document to a file or an URL.
2596
 *
2597
 * Returns the number of bytes written or -1 in case of error.
2598
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2599
 * or xmlKeepBlanksDefault(0) was called
2600
 */
2601
int
2602
xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2603
0
      const char * encoding, int format ) {
2604
0
    xmlSaveCtxt ctxt;
2605
0
    xmlOutputBufferPtr buf;
2606
0
    xmlCharEncodingHandlerPtr handler = NULL;
2607
0
    int ret;
2608
2609
0
    if (cur == NULL)
2610
0
  return(-1);
2611
2612
0
    if (encoding == NULL)
2613
0
  encoding = (const char *) cur->encoding;
2614
2615
0
    if (encoding != NULL) {
2616
2617
0
      handler = xmlFindCharEncodingHandler(encoding);
2618
0
      if (handler == NULL)
2619
0
    return(-1);
2620
0
    }
2621
2622
#ifdef LIBXML_ZLIB_ENABLED
2623
    if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2624
#endif
2625
    /*
2626
     * save the content to a temp buffer.
2627
     */
2628
0
    buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2629
0
    if (buf == NULL) return(-1);
2630
0
    memset(&ctxt, 0, sizeof(ctxt));
2631
0
    ctxt.buf = buf;
2632
0
    ctxt.level = 0;
2633
0
    ctxt.format = format ? 1 : 0;
2634
0
    ctxt.encoding = (const xmlChar *) encoding;
2635
0
    xmlSaveCtxtInit(&ctxt);
2636
0
    ctxt.options |= XML_SAVE_AS_XML;
2637
2638
0
    xmlDocContentDumpOutput(&ctxt, cur);
2639
2640
0
    ret = xmlOutputBufferClose(buf);
2641
0
    return(ret);
2642
0
}
2643
2644
2645
/**
2646
 * xmlSaveFileEnc:
2647
 * @filename:  the filename (or URL)
2648
 * @cur:  the document
2649
 * @encoding:  the name of an encoding (or NULL)
2650
 *
2651
 * Dump an XML document, converting it to the given encoding
2652
 *
2653
 * returns: the number of bytes written or -1 in case of failure.
2654
 */
2655
int
2656
0
xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2657
0
    return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2658
0
}
2659
2660
/**
2661
 * xmlSaveFormatFile:
2662
 * @filename:  the filename (or URL)
2663
 * @cur:  the document
2664
 * @format:  should formatting spaces been added
2665
 *
2666
 * Dump an XML document to a file. Will use compression if
2667
 * compiled in and enabled. If @filename is "-" the stdout file is
2668
 * used. If @format is set then the document will be indented on output.
2669
 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2670
 * or xmlKeepBlanksDefault(0) was called
2671
 *
2672
 * returns: the number of bytes written or -1 in case of failure.
2673
 */
2674
int
2675
0
xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2676
0
    return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2677
0
}
2678
2679
/**
2680
 * xmlSaveFile:
2681
 * @filename:  the filename (or URL)
2682
 * @cur:  the document
2683
 *
2684
 * Dump an XML document to a file. Will use compression if
2685
 * compiled in and enabled. If @filename is "-" the stdout file is
2686
 * used.
2687
 * returns: the number of bytes written or -1 in case of failure.
2688
 */
2689
int
2690
0
xmlSaveFile(const char *filename, xmlDocPtr cur) {
2691
0
    return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2692
0
}
2693
2694
#endif /* LIBXML_OUTPUT_ENABLED */
2695