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