Coverage Report

Created: 2024-09-06 07:53

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