Coverage Report

Created: 2024-02-25 06:14

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