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