Coverage Report

Created: 2022-02-21 09:34

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