commiting the new xmlsave module before the actuall big code change.

* Makefile.am tree.c xmlsave.c include/libxml/xmlsave.h: commiting
  the new xmlsave module before the actuall big code change.
Daniel
diff --git a/xmlsave.c b/xmlsave.c
new file mode 100644
index 0000000..15630dc
--- /dev/null
+++ b/xmlsave.c
@@ -0,0 +1,1950 @@
+/*
+ * xmlsave.c: Implemetation of the document serializer
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parserInternals.h>
+#include <libxml/tree.h>
+#include <libxml/xmlsave.h>
+#ifdef LIBXML_HTML_ENABLED
+#include <libxml/HTMLtree.h>
+
+/************************************************************************
+ *									*
+ *			XHTML detection					*
+ *									*
+ ************************************************************************/
+#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
+   "-//W3C//DTD XHTML 1.0 Strict//EN"
+#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
+   "-//W3C//DTD XHTML 1.0 Frameset//EN"
+#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
+#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
+   "-//W3C//DTD XHTML 1.0 Transitional//EN"
+#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+
+#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
+/**
+ * xmlIsXHTML:
+ * @systemID:  the system identifier
+ * @publicID:  the public identifier
+ *
+ * Try to find if the document correspond to an XHTML DTD
+ *
+ * Returns 1 if true, 0 if not and -1 in case of error
+ */
+int
+xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
+    if ((systemID == NULL) && (publicID == NULL))
+	return(-1);
+    if (publicID != NULL) {
+	if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
+	if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
+	if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
+    }
+    if (systemID != NULL) {
+	if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
+	if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
+	if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
+    }
+    return(0);
+}
+#endif /* LIBXML_HTML_ENABLED */
+
+
+#ifdef LIBXML_OUTPUT_ENABLED
+
+#define TODO 								\
+    xmlGenericError(xmlGenericErrorContext,				\
+	    "Unimplemented block at %s:%d\n",				\
+            __FILE__, __LINE__);
+
+struct _xmlSaveCtxt {
+    void *_private;
+    int type;
+    int fd;
+    const xmlChar *filename;
+    const xmlChar *encoding;
+    xmlCharEncodingHandlerPtr handler;
+    xmlOutputBufferPtr out;
+    int options;
+    int level;
+};
+
+/************************************************************************
+ *									*
+ * 			Output error handlers				*
+ *									*
+ ************************************************************************/
+/**
+ * xmlSaveErrMemory:
+ * @extra:  extra informations
+ *
+ * Handle an out of memory condition
+ */
+static void
+xmlSaveErrMemory(const char *extra)
+{
+    __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
+}
+
+/**
+ * xmlSaveErr:
+ * @code:  the error number
+ * @node:  the location of the error.
+ * @extra:  extra informations
+ *
+ * Handle an out of memory condition
+ */
+static void
+xmlSaveErr(int code, xmlNodePtr node, const char *extra)
+{
+    const char *msg = NULL;
+
+    switch(code) {
+        case XML_SAVE_NOT_UTF8:
+	    msg = "string is not in UTF-8";
+	    break;
+	case XML_SAVE_CHAR_INVALID:
+	    msg = "invalid character value";
+	    break;
+	case XML_SAVE_UNKNOWN_ENCODING:
+	    msg = "unknown encoding %s";
+	    break;
+	case XML_SAVE_NO_DOCTYPE:
+	    msg = "document has no DOCTYPE";
+	    break;
+	default:
+	    msg = "unexpected error number";
+    }
+    __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
+}
+
+/************************************************************************
+ *									*
+ *			Allocation and deallocation			*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlFreeSaveCtxt:
+ *
+ * Free a saving context, destroying the ouptut in any remaining buffer
+ */
+static void
+xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
+{
+    if (ctxt == NULL) return;
+    if (ctxt->encoding != NULL)
+        xmlFree((char *) ctxt->encoding);
+    xmlFree(ctxt);
+}
+
+/**
+ * xmlNewSaveCtxt:
+ *
+ * Create a new saving context
+ *
+ * Returns the new structure or NULL in case of error
+ */
+static xmlSaveCtxtPtr
+xmlNewSaveCtxt(const char *encoding, int options)
+{
+    xmlSaveCtxtPtr ret;
+
+    ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
+    if (ret == NULL) {
+	xmlSaveErrMemory("creating saving context");
+	return ( NULL );
+    }
+    memset(ret, 0, sizeof(xmlSaveCtxt));
+    ret->options = options;
+    if (encoding != NULL) {
+        ret->handler = xmlFindCharEncodingHandler(encoding);
+	if (ret->handler == NULL) {
+	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
+            xmlFreeSaveCtxt(ret);
+	    return(NULL);
+	}
+        ret->encoding = xmlStrdup((const xmlChar *)encoding);
+    }
+    return(ret);
+}
+
+/************************************************************************
+ *									*
+ *   		Dumping XML tree content to a simple buffer		*
+ *									*
+ ************************************************************************/
+/**
+ * xmlAttrSerializeContent:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @attr:  the attribute pointer
+ *
+ * Serialize the attribute in the buffer
+ */
+static void
+xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
+{
+    xmlNodePtr children;
+
+    children = attr->children;
+    while (children != NULL) {
+        switch (children->type) {
+            case XML_TEXT_NODE:
+		xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
+		break;
+            case XML_ENTITY_REF_NODE:
+                xmlBufferAdd(buf, BAD_CAST "&", 1);
+                xmlBufferAdd(buf, children->name,
+                             xmlStrlen(children->name));
+                xmlBufferAdd(buf, BAD_CAST ";", 1);
+                break;
+            default:
+                /* should not happen unless we have a badly built tree */
+                break;
+        }
+        children = children->next;
+    }
+}
+
+/************************************************************************
+ *									*
+ *   		Dumping XML tree content to an I/O output buffer	*
+ *									*
+ ************************************************************************/
+
+#ifdef LIBXML_HTML_ENABLED
+static void
+xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
+            int level, int format, const char *encoding);
+#endif
+static void
+xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
+                  int level, int format, const char *encoding);
+static void
+xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
+	    xmlNodePtr cur, int level, int format, const char *encoding);
+
+void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
+
+/**
+ * xmlNsDumpOutput:
+ * @buf:  the XML buffer output
+ * @cur:  a namespace
+ *
+ * Dump a local Namespace definition.
+ * Should be called in the context of attributes dumps.
+ */
+static void
+xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlNsDumpOutput : Ns == NULL\n");
+#endif
+	return;
+    }
+    if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
+	if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
+	    return;
+
+        /* Within the context of an element attributes */
+	if (cur->prefix != NULL) {
+	    xmlOutputBufferWriteString(buf, " xmlns:");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
+	} else
+	    xmlOutputBufferWriteString(buf, " xmlns");
+	xmlOutputBufferWriteString(buf, "=");
+	xmlBufferWriteQuotedString(buf->buffer, cur->href);
+    }
+}
+
+/**
+ * xmlNsListDumpOutput:
+ * @buf:  the XML buffer output
+ * @cur:  the first namespace
+ *
+ * Dump a list of local Namespace definitions.
+ * Should be called in the context of attributes dumps.
+ */
+void
+xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
+    while (cur != NULL) {
+        xmlNsDumpOutput(buf, cur);
+	cur = cur->next;
+    }
+}
+
+/**
+ * xmlDtdDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @encoding:  an optional encoding string
+ * 
+ * Dump the XML document DTD, if any.
+ */
+static void
+xmlDtdDumpOutput(xmlOutputBufferPtr buf, xmlDtdPtr dtd, const char *encoding) {
+    if (dtd == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlDtdDumpOutput : no internal subset\n");
+#endif
+	return;
+    }
+    xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
+    xmlOutputBufferWriteString(buf, (const char *)dtd->name);
+    if (dtd->ExternalID != NULL) {
+	xmlOutputBufferWriteString(buf, " PUBLIC ");
+	xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
+	xmlOutputBufferWriteString(buf, " ");
+	xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
+    }  else if (dtd->SystemID != NULL) {
+	xmlOutputBufferWriteString(buf, " SYSTEM ");
+	xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
+    }
+    if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
+            (dtd->attributes == NULL) && (dtd->notations == NULL) &&
+	    (dtd->pentities == NULL)) {
+	xmlOutputBufferWriteString(buf, ">");
+	return;
+    }
+    xmlOutputBufferWriteString(buf, " [\n");
+    xmlNodeListDumpOutput(buf, dtd->doc, dtd->children, -1, 0, encoding);
+    xmlOutputBufferWriteString(buf, "]>");
+}
+
+/**
+ * xmlAttrDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the attribute pointer
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XML attribute
+ */
+static void
+xmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
+	          const char *encoding ATTRIBUTE_UNUSED) {
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlAttrDumpOutput : property == NULL\n");
+#endif
+	return;
+    }
+    xmlOutputBufferWriteString(buf, " ");
+    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+	xmlOutputBufferWriteString(buf, ":");
+    }
+    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+    xmlOutputBufferWriteString(buf, "=\"");
+    xmlAttrSerializeContent(buf->buffer, doc, cur);
+    xmlOutputBufferWriteString(buf, "\"");
+}
+
+/**
+ * xmlAttrListDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the first attribute pointer
+ * @encoding:  an optional encoding string
+ *
+ * Dump a list of XML attributes
+ */
+static void
+xmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
+	              xmlAttrPtr cur, const char *encoding) {
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlAttrListDumpOutput : property == NULL\n");
+#endif
+	return;
+    }
+    while (cur != NULL) {
+        xmlAttrDumpOutput(buf, doc, cur, encoding);
+	cur = cur->next;
+    }
+}
+
+
+
+/**
+ * xmlNodeListDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the first node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XML node list, recursive behaviour, children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+static void
+xmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
+                xmlNodePtr cur, int level, int format, const char *encoding) {
+    int i;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlNodeListDumpOutput : node == NULL\n");
+#endif
+	return;
+    }
+    while (cur != NULL) {
+	if ((format) && (xmlIndentTreeOutput) &&
+	    (cur->type == XML_ELEMENT_NODE))
+	    for (i = 0;i < level;i++)
+		xmlOutputBufferWriteString(buf, xmlTreeIndentString);
+        xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
+	if (format) {
+	    xmlOutputBufferWriteString(buf, "\n");
+	}
+	cur = cur->next;
+    }
+}
+
+/**
+ * xmlNodeDumpOutputInternal:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the current node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XML node, recursive behaviour, children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+static void
+xmlNodeDumpOutputInternal(xmlOutputBufferPtr buf, xmlDocPtr doc,
+	    xmlNodePtr cur, int level, int format, const char *encoding) {
+    int i;
+    xmlNodePtr tmp;
+    xmlChar *start, *end;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlNodeDumpOutput : node == NULL\n");
+#endif
+	return;
+    }
+    if (cur->type == XML_XINCLUDE_START)
+	return;
+    if (cur->type == XML_XINCLUDE_END)
+	return;
+    if (cur->type == XML_DTD_NODE) {
+        xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
+	return;
+    }
+    if (cur->type == XML_DOCUMENT_FRAG_NODE) {
+        xmlNodeListDumpOutput(buf, doc, cur->children, level, format, encoding);
+	return;
+    }
+    if (cur->type == XML_ELEMENT_DECL) {
+        xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
+	return;
+    }
+    if (cur->type == XML_ATTRIBUTE_DECL) {
+        xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
+	return;
+    }
+    if (cur->type == XML_ENTITY_DECL) {
+        xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
+	return;
+    }
+    if (cur->type == XML_TEXT_NODE) {
+	if (cur->content != NULL) {
+	    if ((cur->name == xmlStringText) ||
+		(cur->name != xmlStringTextNoenc)) {
+		xmlChar *buffer;
+
+		if (encoding == NULL)
+		    buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
+		else
+		    buffer = xmlEncodeSpecialChars(doc, cur->content);
+		if (buffer != NULL) {
+		    xmlOutputBufferWriteString(buf, (const char *)buffer);
+		    xmlFree(buffer);
+		}
+	    } else {
+		/*
+		 * Disable escaping, needed for XSLT
+		 */
+		xmlOutputBufferWriteString(buf, (const char *) cur->content);
+	    }
+	}
+
+	return;
+    }
+    if (cur->type == XML_PI_NODE) {
+	if (cur->content != NULL) {
+	    xmlOutputBufferWriteString(buf, "<?");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+	    if (cur->content != NULL) {
+		xmlOutputBufferWriteString(buf, " ");
+		xmlOutputBufferWriteString(buf, (const char *)cur->content);
+	    }
+	    xmlOutputBufferWriteString(buf, "?>");
+	} else {
+	    xmlOutputBufferWriteString(buf, "<?");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+	    xmlOutputBufferWriteString(buf, "?>");
+	}
+	return;
+    }
+    if (cur->type == XML_COMMENT_NODE) {
+	if (cur->content != NULL) {
+	    xmlOutputBufferWriteString(buf, "<!--");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->content);
+	    xmlOutputBufferWriteString(buf, "-->");
+	}
+	return;
+    }
+    if (cur->type == XML_ENTITY_REF_NODE) {
+        xmlOutputBufferWriteString(buf, "&");
+	xmlOutputBufferWriteString(buf, (const char *)cur->name);
+        xmlOutputBufferWriteString(buf, ";");
+	return;
+    }
+    if (cur->type == XML_CDATA_SECTION_NODE) {
+	start = end = cur->content;
+	while (*end != '\0') {
+	    if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
+		end = end + 2;
+		xmlOutputBufferWriteString(buf, "<![CDATA[");
+		xmlOutputBufferWrite(buf, end - start, (const char *)start);
+		xmlOutputBufferWriteString(buf, "]]>");
+		start = end;
+	    }
+	    end++;
+	}
+	if (start != end) {
+	    xmlOutputBufferWriteString(buf, "<![CDATA[");
+	    xmlOutputBufferWriteString(buf, (const char *)start);
+	    xmlOutputBufferWriteString(buf, "]]>");
+	}
+	return;
+    }
+    if (cur->type == XML_ATTRIBUTE_NODE) {
+	xmlAttrDumpOutput(buf,doc, (xmlAttrPtr) cur,encoding);
+	return;
+    }
+    if (cur->type == XML_NAMESPACE_DECL) {
+	xmlNsDumpOutput(buf, (xmlNsPtr) cur);
+	return;
+    }
+
+    if (format == 1) {
+	tmp = cur->children;
+	while (tmp != NULL) {
+	    if ((tmp->type == XML_TEXT_NODE) ||
+		(tmp->type == XML_CDATA_SECTION_NODE) ||
+		(tmp->type == XML_ENTITY_REF_NODE)) {
+		format = 0;
+		break;
+	    }
+	    tmp = tmp->next;
+	}
+    }
+    xmlOutputBufferWriteString(buf, "<");
+    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+	xmlOutputBufferWriteString(buf, ":");
+    }
+
+    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+    if (cur->nsDef)
+        xmlNsListDumpOutput(buf, cur->nsDef);
+    if (cur->properties != NULL)
+        xmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
+
+    if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
+	(cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
+        xmlOutputBufferWriteString(buf, "/>");
+	return;
+    }
+    xmlOutputBufferWriteString(buf, ">");
+    if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
+	xmlChar *buffer;
+
+	if (encoding == NULL)
+	    buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
+	else
+	    buffer = xmlEncodeSpecialChars(doc, cur->content);
+	if (buffer != NULL) {
+	    xmlOutputBufferWriteString(buf, (const char *)buffer);
+	    xmlFree(buffer);
+	}
+    }
+    if (cur->children != NULL) {
+	if (format) xmlOutputBufferWriteString(buf, "\n");
+	xmlNodeListDumpOutput(buf, doc, cur->children,
+		        (level >= 0?level+1:-1), format, encoding);
+	if ((xmlIndentTreeOutput) && (format))
+	    for (i = 0;i < level;i++)
+		xmlOutputBufferWriteString(buf, xmlTreeIndentString);
+    }
+    xmlOutputBufferWriteString(buf, "</");
+    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+	xmlOutputBufferWriteString(buf, ":");
+    }
+
+    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+    xmlOutputBufferWriteString(buf, ">");
+}
+
+/**
+ * xmlDocContentDumpOutput:
+ * @buf:  the XML buffer output
+ * @cur:  the document
+ * @encoding:  an optional encoding string
+ * @format:  should formatting spaces been added
+ *
+ * Dump an XML document.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+static void
+xmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
+	                const char *encoding, int format) {
+#ifdef LIBXML_HTML_ENABLED
+    xmlDtdPtr dtd;
+    int is_xhtml = 0;
+#endif
+    const xmlChar *oldenc = cur->encoding;
+
+    xmlInitParser();
+
+    if (encoding != NULL)
+        cur->encoding = BAD_CAST encoding;
+
+    xmlOutputBufferWriteString(buf, "<?xml version=");
+    if (cur->version != NULL) 
+	xmlBufferWriteQuotedString(buf->buffer, cur->version);
+    else
+	xmlOutputBufferWriteString(buf, "\"1.0\"");
+    if (encoding == NULL) {
+	if (cur->encoding != NULL)
+	    encoding = (const char *) cur->encoding;
+	else if (cur->charset != XML_CHAR_ENCODING_UTF8)
+	    encoding = xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
+    }
+    if (encoding != NULL) {
+        xmlOutputBufferWriteString(buf, " encoding=");
+	xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
+    }
+    switch (cur->standalone) {
+        case 0:
+	    xmlOutputBufferWriteString(buf, " standalone=\"no\"");
+	    break;
+        case 1:
+	    xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
+	    break;
+    }
+    xmlOutputBufferWriteString(buf, "?>\n");
+
+#ifdef LIBXML_HTML_ENABLED
+    dtd = xmlGetIntSubset(cur);
+    if (dtd != NULL) {
+	is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
+	if (is_xhtml < 0) is_xhtml = 0;
+    }
+    if (is_xhtml) {
+	if (encoding != NULL)
+	    htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
+	else
+	    htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
+    }
+#endif
+    if (cur->children != NULL) {
+        xmlNodePtr child = cur->children;
+
+	while (child != NULL) {
+#ifdef LIBXML_HTML_ENABLED
+	    if (is_xhtml)
+		xhtmlNodeDumpOutput(buf, cur, child, 0, format, encoding);
+	    else
+#endif
+		xmlNodeDumpOutputInternal(buf, cur, child, 0, format, encoding);
+	    xmlOutputBufferWriteString(buf, "\n");
+	    child = child->next;
+	}
+    }
+    if (encoding != NULL)
+        cur->encoding = oldenc;
+}
+
+#ifdef LIBXML_HTML_ENABLED
+/************************************************************************
+ *									*
+ *		Functions specific to XHTML serialization		*
+ *									*
+ ************************************************************************/
+
+/**
+ * xhtmlIsEmpty:
+ * @node:  the node
+ *
+ * Check if a node is an empty xhtml node
+ *
+ * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
+ */
+static int
+xhtmlIsEmpty(xmlNodePtr node) {
+    if (node == NULL)
+	return(-1);
+    if (node->type != XML_ELEMENT_NODE)
+	return(0);
+    if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
+	return(0);
+    if (node->children != NULL)
+	return(0);
+    switch (node->name[0]) {
+	case 'a':
+	    if (xmlStrEqual(node->name, BAD_CAST "area"))
+		return(1);
+	    return(0);
+	case 'b':
+	    if (xmlStrEqual(node->name, BAD_CAST "br"))
+		return(1);
+	    if (xmlStrEqual(node->name, BAD_CAST "base"))
+		return(1);
+	    if (xmlStrEqual(node->name, BAD_CAST "basefont"))
+		return(1);
+	    return(0);
+	case 'c':
+	    if (xmlStrEqual(node->name, BAD_CAST "col"))
+		return(1);
+	    return(0);
+	case 'f':
+	    if (xmlStrEqual(node->name, BAD_CAST "frame"))
+		return(1);
+	    return(0);
+	case 'h':
+	    if (xmlStrEqual(node->name, BAD_CAST "hr"))
+		return(1);
+	    return(0);
+	case 'i':
+	    if (xmlStrEqual(node->name, BAD_CAST "img"))
+		return(1);
+	    if (xmlStrEqual(node->name, BAD_CAST "input"))
+		return(1);
+	    if (xmlStrEqual(node->name, BAD_CAST "isindex"))
+		return(1);
+	    return(0);
+	case 'l':
+	    if (xmlStrEqual(node->name, BAD_CAST "link"))
+		return(1);
+	    return(0);
+	case 'm':
+	    if (xmlStrEqual(node->name, BAD_CAST "meta"))
+		return(1);
+	    return(0);
+	case 'p':
+	    if (xmlStrEqual(node->name, BAD_CAST "param"))
+		return(1);
+	    return(0);
+    }
+    return(0);
+}
+
+/**
+ * xhtmlAttrListDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the first attribute pointer
+ * @encoding:  an optional encoding string
+ *
+ * Dump a list of XML attributes
+ */
+static void
+xhtmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
+	              xmlAttrPtr cur, const char *encoding) {
+    xmlAttrPtr xml_lang = NULL;
+    xmlAttrPtr lang = NULL;
+    xmlAttrPtr name = NULL;
+    xmlAttrPtr id = NULL;
+    xmlNodePtr parent;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlAttrListDumpOutput : property == NULL\n");
+#endif
+	return;
+    }
+    parent = cur->parent;
+    while (cur != NULL) {
+	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
+	    id = cur;
+	else
+	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
+	    name = cur;
+	else
+	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
+	    lang = cur;
+	else
+	if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
+	    (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
+	    xml_lang = cur;
+	else if ((cur->ns == NULL) && 
+		 ((cur->children == NULL) ||
+		  (cur->children->content == NULL) ||
+		  (cur->children->content[0] == 0)) &&
+		 (htmlIsBooleanAttr(cur->name))) {
+	    if (cur->children != NULL)
+		xmlFreeNode(cur->children);
+	    cur->children = xmlNewText(cur->name);
+	    if (cur->children != NULL)
+		cur->children->parent = (xmlNodePtr) cur;
+	}
+        xmlAttrDumpOutput(buf, doc, cur, encoding);
+	cur = cur->next;
+    }
+    /*
+     * C.8
+     */
+    if ((name != NULL) && (id == NULL)) {
+	if ((parent != NULL) && (parent->name != NULL) &&
+	    ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "p")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "div")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "img")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "map")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "form")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
+	     (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
+	    xmlOutputBufferWriteString(buf, " id=\"");
+	    xmlAttrSerializeContent(buf->buffer, doc, name);
+	    xmlOutputBufferWriteString(buf, "\"");
+	}
+    }
+    /*
+     * C.7.
+     */
+    if ((lang != NULL) && (xml_lang == NULL)) {
+	xmlOutputBufferWriteString(buf, " xml:lang=\"");
+	xmlAttrSerializeContent(buf->buffer, doc, lang);
+	xmlOutputBufferWriteString(buf, "\"");
+    } else 
+    if ((xml_lang != NULL) && (lang == NULL)) {
+	xmlOutputBufferWriteString(buf, " lang=\"");
+	xmlAttrSerializeContent(buf->buffer, doc, xml_lang);
+	xmlOutputBufferWriteString(buf, "\"");
+    }
+}
+
+/**
+ * xhtmlNodeListDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the XHTML document
+ * @cur:  the first node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XML node list, recursive behaviour, children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+static void
+xhtmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
+                xmlNodePtr cur, int level, int format, const char *encoding) {
+    int i;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xhtmlNodeListDumpOutput : node == NULL\n");
+#endif
+	return;
+    }
+    while (cur != NULL) {
+	if ((format) && (xmlIndentTreeOutput) &&
+	    (cur->type == XML_ELEMENT_NODE))
+	    for (i = 0;i < level;i++)
+		xmlOutputBufferWriteString(buf, xmlTreeIndentString);
+        xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
+	if (format) {
+	    xmlOutputBufferWriteString(buf, "\n");
+	}
+	cur = cur->next;
+    }
+}
+
+/**
+ * xhtmlNodeDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the XHTML document
+ * @cur:  the current node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XHTML node, recursive behaviour, children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+static void
+xhtmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
+            int level, int format, const char *encoding) {
+    int i;
+    xmlNodePtr tmp;
+    xmlChar *start, *end;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlNodeDumpOutput : node == NULL\n");
+#endif
+	return;
+    }
+    if (cur->type == XML_XINCLUDE_START)
+	return;
+    if (cur->type == XML_XINCLUDE_END)
+	return;
+    if (cur->type == XML_DTD_NODE) {
+        xmlDtdDumpOutput(buf, (xmlDtdPtr) cur, encoding);
+	return;
+    }
+    if (cur->type == XML_ELEMENT_DECL) {
+        xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
+	return;
+    }
+    if (cur->type == XML_ATTRIBUTE_DECL) {
+        xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
+	return;
+    }
+    if (cur->type == XML_ENTITY_DECL) {
+        xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
+	return;
+    }
+    if (cur->type == XML_TEXT_NODE) {
+	if (cur->content != NULL) {
+	    if ((cur->name == xmlStringText) ||
+		(cur->name != xmlStringTextNoenc)) {
+		xmlChar *buffer;
+
+		if (encoding == NULL)
+		    buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
+		else
+		    buffer = xmlEncodeSpecialChars(doc, cur->content);
+		if (buffer != NULL) {
+		    xmlOutputBufferWriteString(buf, (const char *)buffer);
+		    xmlFree(buffer);
+		}
+	    } else {
+		/*
+		 * Disable escaping, needed for XSLT
+		 */
+		xmlOutputBufferWriteString(buf, (const char *) cur->content);
+	    }
+	}
+
+	return;
+    }
+    if (cur->type == XML_PI_NODE) {
+	if (cur->content != NULL) {
+	    xmlOutputBufferWriteString(buf, "<?");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+	    if (cur->content != NULL) {
+		xmlOutputBufferWriteString(buf, " ");
+		xmlOutputBufferWriteString(buf, (const char *)cur->content);
+	    }
+	    xmlOutputBufferWriteString(buf, "?>");
+	} else {
+	    xmlOutputBufferWriteString(buf, "<?");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+	    xmlOutputBufferWriteString(buf, "?>");
+	}
+	return;
+    }
+    if (cur->type == XML_COMMENT_NODE) {
+	if (cur->content != NULL) {
+	    xmlOutputBufferWriteString(buf, "<!--");
+	    xmlOutputBufferWriteString(buf, (const char *)cur->content);
+	    xmlOutputBufferWriteString(buf, "-->");
+	}
+	return;
+    }
+    if (cur->type == XML_ENTITY_REF_NODE) {
+        xmlOutputBufferWriteString(buf, "&");
+	xmlOutputBufferWriteString(buf, (const char *)cur->name);
+        xmlOutputBufferWriteString(buf, ";");
+	return;
+    }
+    if (cur->type == XML_CDATA_SECTION_NODE) {
+	start = end = cur->content;
+	while (*end != '\0') {
+	    if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
+		end = end + 2;
+		xmlOutputBufferWriteString(buf, "<![CDATA[");
+		xmlOutputBufferWrite(buf, end - start, (const char *)start);
+		xmlOutputBufferWriteString(buf, "]]>");
+		start = end;
+	    }
+	    end++;
+	}
+	if (start != end) {
+	    xmlOutputBufferWriteString(buf, "<![CDATA[");
+	    xmlOutputBufferWriteString(buf, (const char *)start);
+	    xmlOutputBufferWriteString(buf, "]]>");
+	}
+	return;
+    }
+
+    if (format == 1) {
+	tmp = cur->children;
+	while (tmp != NULL) {
+	    if ((tmp->type == XML_TEXT_NODE) || 
+		(tmp->type == XML_ENTITY_REF_NODE)) {
+		format = 0;
+		break;
+	    }
+	    tmp = tmp->next;
+	}
+    }
+    xmlOutputBufferWriteString(buf, "<");
+    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+	xmlOutputBufferWriteString(buf, ":");
+    }
+
+    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+    if (cur->nsDef)
+        xmlNsListDumpOutput(buf, cur->nsDef);
+    if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
+	(cur->ns == NULL) && (cur->nsDef == NULL))) {
+	/*
+	 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
+	 */
+	xmlOutputBufferWriteString(buf,
+		" xmlns=\"http://www.w3.org/1999/xhtml\"");
+    }
+    if (cur->properties != NULL)
+        xhtmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
+
+    if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
+	if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
+	    (xhtmlIsEmpty(cur) == 1)) {
+	    /*
+	     * C.2. Empty Elements
+	     */
+	    xmlOutputBufferWriteString(buf, " />");
+	} else {
+	    /*
+	     * C.3. Element Minimization and Empty Element Content
+	     */
+	    xmlOutputBufferWriteString(buf, "></");
+	    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+		xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+		xmlOutputBufferWriteString(buf, ":");
+	    }
+	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+	    xmlOutputBufferWriteString(buf, ">");
+	}
+	return;
+    }
+    xmlOutputBufferWriteString(buf, ">");
+    if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
+	xmlChar *buffer;
+
+	if (encoding == NULL)
+	    buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
+	else
+	    buffer = xmlEncodeSpecialChars(doc, cur->content);
+	if (buffer != NULL) {
+	    xmlOutputBufferWriteString(buf, (const char *)buffer);
+	    xmlFree(buffer);
+	}
+    }
+
+    /*
+     * 4.8. Script and Style elements
+     */
+    if ((cur->type == XML_ELEMENT_NODE) &&
+	((xmlStrEqual(cur->name, BAD_CAST "script")) ||
+	 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
+	((cur->ns == NULL) ||
+	 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
+	xmlNodePtr child = cur->children;
+
+	while (child != NULL) {
+	    if ((child->type == XML_TEXT_NODE) ||
+		(child->type == XML_CDATA_SECTION_NODE)) {
+		/*
+		 * Apparently CDATA escaping for style just break on IE,
+		 * mozilla and galeon, so ...
+		 */
+		if (xmlStrEqual(cur->name, BAD_CAST "style") &&
+		    (xmlStrchr(child->content, '<') == NULL) &&
+		    (xmlStrchr(child->content, '>') == NULL) &&
+		    (xmlStrchr(child->content, '&') == NULL)) {
+		    xhtmlNodeDumpOutput(buf, doc, child, 0, 0, encoding);
+		} else {
+		    start = end = child->content;
+		    while (*end != '\0') {
+			if (*end == ']' &&
+			    *(end + 1) == ']' &&
+			    *(end + 2) == '>') {
+			    end = end + 2;
+			    xmlOutputBufferWriteString(buf, "<![CDATA[");
+			    xmlOutputBufferWrite(buf, end - start,
+						 (const char *)start);
+			    xmlOutputBufferWriteString(buf, "]]>");
+			    start = end;
+			}
+			end++;
+		    }
+		    if (start != end) {
+			xmlOutputBufferWriteString(buf, "<![CDATA[");
+			xmlOutputBufferWriteString(buf, (const char *)start);
+			xmlOutputBufferWriteString(buf, "]]>");
+		    }
+		}
+	    } else {
+		xhtmlNodeDumpOutput(buf, doc, child, 0, 0, encoding);
+	    }
+	    child = child->next;
+	}
+    } else if (cur->children != NULL) {
+	if (format) xmlOutputBufferWriteString(buf, "\n");
+	xhtmlNodeListDumpOutput(buf, doc, cur->children,
+		        (level >= 0?level+1:-1), format, encoding);
+	if ((xmlIndentTreeOutput) && (format))
+	    for (i = 0;i < level;i++)
+		xmlOutputBufferWriteString(buf, xmlTreeIndentString);
+    }
+    xmlOutputBufferWriteString(buf, "</");
+    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
+        xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
+	xmlOutputBufferWriteString(buf, ":");
+    }
+
+    xmlOutputBufferWriteString(buf, (const char *)cur->name);
+    xmlOutputBufferWriteString(buf, ">");
+}
+#endif
+
+/************************************************************************
+ *									*
+ *			Public entry points				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlSaveToFd:
+ * @fd:  a file descriptor number
+ * @encoding:  the encoding name to use or NULL
+ * @options:  a set of xmlSaveOptions
+ *
+ * Create a document saving context serializing to a file descriptor
+ * with the encoding and the options given.
+ *
+ * Returns a new serialization context or NULL in case of error.
+ */
+xmlSaveCtxtPtr
+xmlSaveToFd(int fd, const char *encoding, int options)
+{
+    xmlSaveCtxtPtr ret;
+
+    ret = xmlNewSaveCtxt(encoding, options);
+    if (ret == NULL) return(NULL);
+    ret->out = xmlOutputBufferCreateFd(fd, ret->handler);
+    if (ret->out == NULL) {
+	xmlFreeSaveCtxt(ret);
+	return(NULL);
+    }
+    return(ret);
+}
+
+/**
+ * xmlSaveToFilename:
+ * @filename:  a file name or an URL
+ * @encoding:  the encoding name to use or NULL
+ * @options:  a set of xmlSaveOptions
+ *
+ * Create a document saving context serializing to a filename or possibly
+ * to an URL (but this is less reliable) with the encoding and the options
+ * given.
+ *
+ * Returns a new serialization context or NULL in case of error.
+ */
+xmlSaveCtxtPtr
+xmlSaveToFilename(const char *filename, const char *encoding, int options)
+{
+    xmlSaveCtxtPtr ret;
+    int compression = 0; /* TODO handle compression option */
+
+    ret = xmlNewSaveCtxt(encoding, options);
+    if (ret == NULL) return(NULL);
+    ret->out = xmlOutputBufferCreateFilename(filename, ret->handler,
+                                             compression);
+    if (ret->out == NULL) {
+	xmlFreeSaveCtxt(ret);
+	return(NULL);
+    }
+    return(ret);
+}
+
+#if 0
+/**
+ * xmlSaveToBuffer:
+ * @buffer:  a buffer
+ * @encoding:  the encoding name to use or NULL
+ * @options:  a set of xmlSaveOptions
+ *
+ * Create a document saving context serializing to a buffer
+ * with the encoding and the options given
+ *
+ * Returns a new serialization context or NULL in case of error.
+ */
+xmlSaveCtxtPtr
+xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
+{
+    TODO
+    return(NULL);
+}
+#endif
+
+/**
+ * xmlSaveToIO:
+ * @iowrite:  an I/O write function
+ * @ioclose:  an I/O close function
+ * @ioctx:  an I/O handler
+ * @encoding:  the encoding name to use or NULL
+ * @options:  a set of xmlSaveOptions
+ *
+ * Create a document saving context serializing to a file descriptor
+ * with the encoding and the options given
+ *
+ * Returns a new serialization context or NULL in case of error.
+ */
+xmlSaveCtxtPtr
+xmlSaveToIO(xmlOutputWriteCallback iowrite,
+            xmlOutputCloseCallback ioclose,
+            void *ioctx, const char *encoding, int options)
+{
+    xmlSaveCtxtPtr ret;
+
+    ret = xmlNewSaveCtxt(encoding, options);
+    if (ret == NULL) return(NULL);
+    ret->out = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
+    if (ret->out == NULL) {
+	xmlFreeSaveCtxt(ret);
+	return(NULL);
+    }
+    return(ret);
+}
+
+/**
+ * xmlSaveDoc:
+ * @ctxt:  a document saving context
+ * @doc:  a document
+ *
+ * Save a full document to a saving context
+ *
+ * Returns the number of byte written or -1 in case of error
+ */
+long
+xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
+{
+}
+
+/**
+ * xmlSaveTree:
+ * @ctxt:  a document saving context
+ * @node:  a document
+ *
+ * Save a subtree starting at the node parameter to a saving context
+ *
+ * Returns the number of byte written or -1 in case of error
+ */
+long
+xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
+{
+}
+
+/**
+ * xmlSaveFlush:
+ * @ctxt:  a document saving context
+ *
+ * Flush a document saving context, i.e. make sure that all bytes have
+ * been output.
+ *
+ * Returns the number of byte written or -1 in case of error.
+ */
+int
+xmlSaveFlush(xmlSaveCtxtPtr ctxt)
+{
+    if (ctxt == NULL) return(-1);
+    if (ctxt->out == NULL) return(-1);
+    return(xmlOutputBufferFlush(ctxt->out));
+}
+
+/**
+ * xmlSaveClose:
+ * @ctxt:  a document saving context
+ *
+ * Close a document saving context, i.e. make sure that all bytes have
+ * been output and free the associated data.
+ *
+ * Returns the number of byte written or -1 in case of error.
+ */
+int
+xmlSaveClose(xmlSaveCtxtPtr ctxt)
+{
+    int ret;
+
+    if (ctxt == NULL) return(-1);
+    ret = xmlSaveFlush(ctxt);
+    xmlFreeSaveCtxt(ctxt);
+    return(ret);
+}
+
+/************************************************************************
+ *									*
+ *		Public entry points based on buffers			*
+ *									*
+ ************************************************************************/
+/**
+ * xmlAttrSerializeTxtContent:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @attr: the attribute node
+ * @string: the text content
+ *
+ * Serialize text attribute values to an xml simple buffer
+ */
+void
+xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
+        xmlAttrPtr attr, const xmlChar *string) {
+    xmlChar *base, *cur;
+
+    if (string == NULL) return;
+    base = cur = (xmlChar *)string;
+    while (*cur != 0) {
+        if (*cur == '\n') {
+            if (base != cur)
+                xmlBufferAdd(buf, base, cur - base);
+            xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
+            cur++;
+            base = cur;
+            } else if (*cur == '\r') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
+                cur++;
+                base = cur;
+            } else if (*cur == '\t') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
+                cur++;
+                base = cur;
+            } else if (*cur == '"') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
+                cur++;
+                base = cur;
+            } else if (*cur == '<') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
+                cur++;
+                base = cur;
+            } else if (*cur == '>') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
+                cur++;
+                base = cur;
+            } else if (*cur == '&') {
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
+                cur++;
+                base = cur;
+            } else if ((*cur >= 0x80) && ((doc == NULL) ||
+                          (doc->encoding == NULL))) {
+                /*
+                 * We assume we have UTF-8 content.
+                 */
+                char tmp[10];
+                int val = 0, l = 1;
+
+                if (base != cur)
+                    xmlBufferAdd(buf, base, cur - base);
+                if (*cur < 0xC0) {
+		    xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
+                    if (doc != NULL)
+                        doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
+                    snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
+                    tmp[sizeof(tmp) - 1] = 0;
+                    xmlBufferAdd(buf, (xmlChar *) tmp, -1);
+                    cur++;
+                    base = cur;
+                    continue;
+                } else if (*cur < 0xE0) {
+                    val = (cur[0]) & 0x1F;
+                    val <<= 6;
+                    val |= (cur[1]) & 0x3F;
+                    l = 2;
+                } else if (*cur < 0xF0) {
+                    val = (cur[0]) & 0x0F;
+                    val <<= 6;
+                    val |= (cur[1]) & 0x3F;
+                    val <<= 6;
+                    val |= (cur[2]) & 0x3F;
+                    l = 3;
+                } else if (*cur < 0xF8) {
+                    val = (cur[0]) & 0x07;
+                    val <<= 6;
+                    val |= (cur[1]) & 0x3F;
+                    val <<= 6;
+                    val |= (cur[2]) & 0x3F;
+                    val <<= 6;
+                    val |= (cur[3]) & 0x3F;
+                    l = 4;
+                }
+                if ((l == 1) || (!IS_CHAR(val))) {
+		    xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
+                    if (doc != NULL)
+                        doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
+                    snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
+                    tmp[sizeof(tmp) - 1] = 0;
+                    xmlBufferAdd(buf, (xmlChar *) tmp, -1);
+                    cur++;
+                    base = cur;
+                    continue;
+                }
+                /*
+                 * We could do multiple things here. Just save
+                 * as a char ref
+                 */
+                snprintf(tmp, sizeof(tmp), "&#x%X;", val);
+                tmp[sizeof(tmp) - 1] = 0;
+                xmlBufferAdd(buf, (xmlChar *) tmp, -1);
+                cur += l;
+                base = cur;
+            } else {
+                cur++;
+            }
+        }
+        if (base != cur)
+            xmlBufferAdd(buf, base, cur - base);
+}
+
+/**
+ * xmlNodeDump:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the current node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ *
+ * Dump an XML node, recursive behaviour,children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ *
+ * Returns the number of bytes written to the buffer or -1 in case of error
+ */
+int
+xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
+            int format)
+{
+    unsigned int use;
+    int ret;
+    xmlOutputBufferPtr outbuf;
+
+    xmlInitParser();
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+                        "xmlNodeDump : node == NULL\n");
+#endif
+        return (-1);
+    }
+    if (buf == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+                        "xmlNodeDump : buf == NULL\n");
+#endif
+        return (-1);
+    }
+    outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
+    if (outbuf == NULL) {
+        xmlSaveErrMemory("creating buffer");
+        return (-1);
+    }
+    memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
+    outbuf->buffer = buf;
+    outbuf->encoder = NULL;
+    outbuf->writecallback = NULL;
+    outbuf->closecallback = NULL;
+    outbuf->context = NULL;
+    outbuf->written = 0;
+
+    use = buf->use;
+    xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
+    xmlFree(outbuf);
+    ret = buf->use - use;
+    return (ret);
+}
+
+/**
+ * xmlElemDump:
+ * @f:  the FILE * for the output
+ * @doc:  the document
+ * @cur:  the current node
+ *
+ * Dump an XML/HTML node, recursive behaviour, children are printed too.
+ */
+void
+xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
+{
+    xmlOutputBufferPtr outbuf;
+
+    xmlInitParser();
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+                        "xmlElemDump : cur == NULL\n");
+#endif
+        return;
+    }
+#ifdef DEBUG_TREE
+    if (doc == NULL) {
+        xmlGenericError(xmlGenericErrorContext,
+                        "xmlElemDump : doc == NULL\n");
+    }
+#endif
+
+    outbuf = xmlOutputBufferCreateFile(f, NULL);
+    if (outbuf == NULL)
+        return;
+    if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
+#ifdef LIBXML_HTML_ENABLED
+        htmlNodeDumpOutput(outbuf, doc, cur, NULL);
+#else
+	xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
+#endif /* LIBXML_HTML_ENABLED */
+    } else
+        xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
+    xmlOutputBufferClose(outbuf);
+}
+
+/************************************************************************
+ *									*
+ *		Saving functions front-ends				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlNodeDumpOutput:
+ * @buf:  the XML buffer output
+ * @doc:  the document
+ * @cur:  the current node
+ * @level: the imbrication level for indenting
+ * @format: is formatting allowed
+ * @encoding:  an optional encoding string
+ *
+ * Dump an XML node, recursive behaviour, children are printed too.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+void
+xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
+                  int level, int format, const char *encoding)
+{
+#ifdef LIBXML_HTML_ENABLED
+    xmlDtdPtr dtd;
+    int is_xhtml = 0;
+#endif
+
+    xmlInitParser();
+
+#ifdef LIBXML_HTML_ENABLED
+    dtd = xmlGetIntSubset(doc);
+    if (dtd != NULL) {
+        is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
+        if (is_xhtml < 0)
+            is_xhtml = 0;
+        if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
+            (cur->type == XML_ELEMENT_NODE) &&
+            (xmlStrEqual(cur->name, BAD_CAST "html"))) {
+            if (encoding != NULL)
+                htmlSetMetaEncoding((htmlDocPtr) doc,
+                                    (const xmlChar *) encoding);
+            else
+                htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
+        }
+    }
+
+    if (is_xhtml)
+        xhtmlNodeDumpOutput(buf, doc, cur, level, format, encoding);
+    else
+#endif
+        xmlNodeDumpOutputInternal(buf, doc, cur, level, format, encoding);
+}
+
+/**
+ * xmlDocDumpFormatMemoryEnc:
+ * @out_doc:  Document to generate XML text from
+ * @doc_txt_ptr:  Memory pointer for allocated XML text
+ * @doc_txt_len:  Length of the generated XML text
+ * @txt_encoding:  Character encoding to use when generating XML text
+ * @format:  should formatting spaces been added
+ *
+ * Dump the current DOM tree into memory using the character encoding specified
+ * by the caller.  Note it is up to the caller of this function to free the
+ * allocated memory with xmlFree().
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+
+void
+xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
+		int * doc_txt_len, const char * txt_encoding,
+		int format) {
+    int                         dummy = 0;
+    xmlOutputBufferPtr          out_buff = NULL;
+    xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
+
+    if (doc_txt_len == NULL) {
+        doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
+    }
+
+    if (doc_txt_ptr == NULL) {
+        *doc_txt_len = 0;
+        return;
+    }
+
+    *doc_txt_ptr = NULL;
+    *doc_txt_len = 0;
+
+    if (out_doc == NULL) {
+        /*  No document, no output  */
+        return;
+    }
+
+    /*
+     *  Validate the encoding value, if provided.
+     *  This logic is copied from xmlSaveFileEnc.
+     */
+
+    if (txt_encoding == NULL)
+	txt_encoding = (const char *) out_doc->encoding;
+    if (txt_encoding != NULL) {
+	conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
+	if ( conv_hdlr == NULL ) {
+	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
+		       txt_encoding);
+	    return;
+	}
+    }
+
+    if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
+        xmlSaveErrMemory("creating buffer");
+        return;
+    }
+
+    xmlDocContentDumpOutput(out_buff, out_doc, txt_encoding, format);
+    xmlOutputBufferFlush(out_buff);
+    if (out_buff->conv != NULL) {
+	*doc_txt_len = out_buff->conv->use;
+	*doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
+    } else {
+	*doc_txt_len = out_buff->buffer->use;
+	*doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
+    }
+    (void)xmlOutputBufferClose(out_buff);
+
+    if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
+        *doc_txt_len = 0;
+        xmlSaveErrMemory("creating output");
+    }
+
+    return;
+}
+
+/**
+ * xmlDocDumpMemory:
+ * @cur:  the document
+ * @mem:  OUT: the memory pointer
+ * @size:  OUT: the memory length
+ *
+ * Dump an XML document in memory and return the #xmlChar * and it's size
+ * in bytes. It's up to the caller to free the memory with xmlFree().
+ * The resulting byte array is zero terminated, though the last 0 is not
+ * included in the returned size.
+ */
+void
+xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
+    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
+}
+
+/**
+ * xmlDocDumpFormatMemory:
+ * @cur:  the document
+ * @mem:  OUT: the memory pointer
+ * @size:  OUT: the memory length
+ * @format:  should formatting spaces been added
+ *
+ *
+ * Dump an XML document in memory and return the #xmlChar * and it's size.
+ * It's up to the caller to free the memory with xmlFree().
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+void
+xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
+    xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
+}
+
+/**
+ * xmlDocDumpMemoryEnc:
+ * @out_doc:  Document to generate XML text from
+ * @doc_txt_ptr:  Memory pointer for allocated XML text
+ * @doc_txt_len:  Length of the generated XML text
+ * @txt_encoding:  Character encoding to use when generating XML text
+ *
+ * Dump the current DOM tree into memory using the character encoding specified
+ * by the caller.  Note it is up to the caller of this function to free the
+ * allocated memory with xmlFree().
+ */
+
+void
+xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
+	            int * doc_txt_len, const char * txt_encoding) {
+    xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
+	                      txt_encoding, 0);
+}
+
+/**
+ * xmlDocFormatDump:
+ * @f:  the FILE*
+ * @cur:  the document
+ * @format: should formatting spaces been added
+ *
+ * Dump an XML document to an open FILE.
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+int
+xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
+    xmlOutputBufferPtr buf;
+    const char * encoding;
+    xmlCharEncodingHandlerPtr handler = NULL;
+    int ret;
+
+    if (cur == NULL) {
+#ifdef DEBUG_TREE
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlDocDump : document == NULL\n");
+#endif
+	return(-1);
+    }
+    encoding = (const char *) cur->encoding;
+
+    if (encoding != NULL) {
+		handler = xmlFindCharEncodingHandler(encoding);
+	    if (handler == NULL) {
+		xmlFree((char *) cur->encoding);
+		cur->encoding = NULL;
+	    }
+	}
+    buf = xmlOutputBufferCreateFile(f, handler);
+    if (buf == NULL) return(-1);
+    xmlDocContentDumpOutput(buf, cur, NULL, format);
+
+    ret = xmlOutputBufferClose(buf);
+    return(ret);
+}
+
+/**
+ * xmlDocDump:
+ * @f:  the FILE*
+ * @cur:  the document
+ *
+ * Dump an XML document to an open FILE.
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ */
+int
+xmlDocDump(FILE *f, xmlDocPtr cur) {
+    return(xmlDocFormatDump (f, cur, 0));
+}
+
+/**
+ * xmlSaveFileTo:
+ * @buf:  an output I/O buffer
+ * @cur:  the document
+ * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
+ *
+ * Dump an XML document to an I/O buffer.
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ */
+int
+xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
+    int ret;
+
+    if (buf == NULL) return(0);
+    xmlDocContentDumpOutput(buf, cur, encoding, 0);
+    ret = xmlOutputBufferClose(buf);
+    return(ret);
+}
+
+/**
+ * xmlSaveFormatFileTo:
+ * @buf:  an output I/O buffer
+ * @cur:  the document
+ * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
+ * @format: should formatting spaces been added
+ *
+ * Dump an XML document to an I/O buffer.
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+int
+xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding, int format) {
+  int ret;
+
+  if (buf == NULL) return(0);
+  xmlDocContentDumpOutput(buf, cur, encoding, format);
+  ret = xmlOutputBufferClose(buf);
+  return(ret);
+}
+
+/**
+ * xmlSaveFormatFileEnc:
+ * @filename:  the filename or URL to output
+ * @cur:  the document being saved
+ * @encoding:  the name of the encoding to use or NULL.
+ * @format:  should formatting spaces be added.
+ *
+ * Dump an XML document to a file or an URL.
+ *
+ * Returns the number of bytes written or -1 in case of error.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ */
+int
+xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
+			const char * encoding, int format ) {
+    xmlOutputBufferPtr buf;
+    xmlCharEncodingHandlerPtr handler = NULL;
+    int ret;
+
+    if (cur == NULL)
+	return(-1);
+
+    if (encoding == NULL)
+	encoding = (const char *) cur->encoding;
+
+    if (encoding != NULL) {
+
+	    handler = xmlFindCharEncodingHandler(encoding);
+	    if (handler == NULL)
+		return(-1);
+    }
+
+#ifdef HAVE_ZLIB_H
+    if (cur->compression < 0) cur->compression = xmlGetCompressMode();
+#endif
+    /* 
+     * save the content to a temp buffer.
+     */
+    buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
+    if (buf == NULL) return(-1);
+
+    xmlDocContentDumpOutput(buf, cur, encoding, format);
+
+    ret = xmlOutputBufferClose(buf);
+    return(ret);
+}
+
+
+/**
+ * xmlSaveFileEnc:
+ * @filename:  the filename (or URL)
+ * @cur:  the document
+ * @encoding:  the name of an encoding (or NULL)
+ *
+ * Dump an XML document, converting it to the given encoding
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ */
+int
+xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
+    return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
+}
+
+/**
+ * xmlSaveFormatFile:
+ * @filename:  the filename (or URL)
+ * @cur:  the document
+ * @format:  should formatting spaces been added
+ *
+ * Dump an XML document to a file. Will use compression if
+ * compiled in and enabled. If @filename is "-" the stdout file is
+ * used. If @format is set then the document will be indented on output.
+ * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
+ * or xmlKeepBlanksDefault(0) was called
+ *
+ * returns: the number of bytes written or -1 in case of failure.
+ */
+int
+xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
+    return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
+}
+
+/**
+ * xmlSaveFile:
+ * @filename:  the filename (or URL)
+ * @cur:  the document
+ *
+ * Dump an XML document to a file. Will use compression if
+ * compiled in and enabled. If @filename is "-" the stdout file is
+ * used.
+ * returns: the number of bytes written or -1 in case of failure.
+ */
+int
+xmlSaveFile(const char *filename, xmlDocPtr cur) {
+    return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
+}
+
+#endif /* LIBXML_OUTPUT_ENABLED */
+