Coverage Report

Created: 2023-06-07 06:06

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