mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2024-10-26 12:25:09 +03:00
5d1a4d81bf
* xmlIO.c xmlsave.c include/libxml/xmlIO.h: first pass at refactoring the escape on save routines for better performances (less malloc) and more flexibility using the new saving context. Preliminary work, interface will change. Daniel
2024 lines
56 KiB
C
2024 lines
56 KiB
C
/*
|
|
* 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>
|
|
|
|
#define MAX_INDENT 60
|
|
|
|
#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);
|
|
}
|
|
|
|
#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 buf;
|
|
xmlDocPtr doc;
|
|
int options;
|
|
int level;
|
|
int format;
|
|
char indent[MAX_INDENT + 1];
|
|
int indent_nr;
|
|
int indent_size;
|
|
};
|
|
|
|
/************************************************************************
|
|
* *
|
|
* 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 *
|
|
* *
|
|
************************************************************************/
|
|
/**
|
|
* xmlSaveCtxtInit:
|
|
* @ctxt: the saving context
|
|
*
|
|
* Initialize a saving context
|
|
*/
|
|
static void
|
|
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
|
|
{
|
|
int i;
|
|
|
|
if (ctxt == NULL) return;
|
|
if (xmlTreeIndentString == NULL) {
|
|
memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
|
|
} else {
|
|
ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
|
|
ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
|
|
for (i = 0;i < ctxt->indent_nr;i++)
|
|
memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
|
|
ctxt->indent_size);
|
|
ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
if (ctxt->buf != NULL)
|
|
xmlOutputBufferClose(ctxt->buf);
|
|
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);
|
|
}
|
|
xmlSaveCtxtInit(ret);
|
|
|
|
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(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
|
|
#endif
|
|
static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
|
|
static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
|
|
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) 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
|
|
* @dtd: the pointer to the DTD
|
|
*
|
|
* Dump the XML document DTD, if any.
|
|
*/
|
|
static void
|
|
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
|
|
xmlOutputBufferPtr buf;
|
|
int format, level;
|
|
xmlDocPtr doc;
|
|
|
|
if (dtd == NULL) return;
|
|
if ((ctxt == NULL) || (ctxt->buf == NULL))
|
|
return;
|
|
buf = ctxt->buf;
|
|
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");
|
|
format = ctxt->format;
|
|
level = ctxt->level;
|
|
doc = ctxt->doc;
|
|
ctxt->format = 0;
|
|
ctxt->level = -1;
|
|
ctxt->doc = dtd->doc;
|
|
xmlNodeListDumpOutput(ctxt, dtd->children);
|
|
ctxt->format = format;
|
|
ctxt->level = level;
|
|
ctxt->doc = doc;
|
|
xmlOutputBufferWriteString(buf, "]>");
|
|
}
|
|
|
|
/**
|
|
* xmlAttrDumpOutput:
|
|
* @buf: the XML buffer output
|
|
* @cur: the attribute pointer
|
|
*
|
|
* Dump an XML attribute
|
|
*/
|
|
static void
|
|
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
|
|
xmlOutputBufferPtr buf;
|
|
if (cur == NULL) return;
|
|
buf = ctxt->buf;
|
|
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, ctxt->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(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
|
|
if (cur == NULL) return;
|
|
while (cur != NULL) {
|
|
xmlAttrDumpOutput(ctxt, cur);
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* xmlNodeListDumpOutput:
|
|
* @cur: the first node
|
|
*
|
|
* Dump an XML node list, recursive behaviour, children are printed too.
|
|
*/
|
|
static void
|
|
xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
|
|
xmlOutputBufferPtr buf;
|
|
|
|
if (cur == NULL) return;
|
|
buf = ctxt->buf;
|
|
while (cur != NULL) {
|
|
if ((ctxt->format) && (xmlIndentTreeOutput) &&
|
|
(cur->type == XML_ELEMENT_NODE))
|
|
xmlOutputBufferWrite(buf, ctxt->indent_size *
|
|
(ctxt->level > ctxt->indent_nr ?
|
|
ctxt->indent_nr : ctxt->level),
|
|
ctxt->indent);
|
|
xmlNodeDumpOutputInternal(ctxt, cur);
|
|
if (ctxt->format) {
|
|
xmlOutputBufferWriteString(buf, "\n");
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlNodeDumpOutputInternal:
|
|
* @cur: the current node
|
|
*
|
|
* Dump an XML node, recursive behaviour, children are printed too.
|
|
*/
|
|
static void
|
|
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
|
|
int format;
|
|
xmlNodePtr tmp;
|
|
xmlChar *start, *end;
|
|
xmlOutputBufferPtr buf;
|
|
|
|
if (cur == NULL) return;
|
|
buf = ctxt->buf;
|
|
if (cur->type == XML_XINCLUDE_START)
|
|
return;
|
|
if (cur->type == XML_XINCLUDE_END)
|
|
return;
|
|
if (cur->type == XML_DTD_NODE) {
|
|
xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
|
|
return;
|
|
}
|
|
if (cur->type == XML_DOCUMENT_FRAG_NODE) {
|
|
xmlNodeListDumpOutput(ctxt, cur->children);
|
|
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)) {
|
|
|
|
if (ctxt->encoding == NULL) {
|
|
xmlChar *buffer;
|
|
|
|
buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
|
|
cur->content);
|
|
if (buffer != NULL) {
|
|
xmlOutputBufferWriteString(buf, (const char *)buffer);
|
|
xmlFree(buffer);
|
|
}
|
|
} else {
|
|
xmlOutputBufferWriteEscape(buf, cur->content);
|
|
}
|
|
} 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(ctxt, (xmlAttrPtr) cur);
|
|
return;
|
|
}
|
|
if (cur->type == XML_NAMESPACE_DECL) {
|
|
xmlNsDumpOutput(buf, (xmlNsPtr) cur);
|
|
return;
|
|
}
|
|
|
|
format = ctxt->format;
|
|
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)) {
|
|
ctxt->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(ctxt, cur->properties);
|
|
|
|
if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
|
|
(cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
|
|
xmlOutputBufferWriteString(buf, "/>");
|
|
ctxt->format = format;
|
|
return;
|
|
}
|
|
xmlOutputBufferWriteString(buf, ">");
|
|
if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
|
|
if (ctxt->encoding == NULL) {
|
|
xmlChar *buffer;
|
|
|
|
buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
|
|
cur->content);
|
|
if (buffer != NULL) {
|
|
xmlOutputBufferWriteString(buf, (const char *)buffer);
|
|
xmlFree(buffer);
|
|
}
|
|
} else {
|
|
xmlOutputBufferWriteEscape(buf, cur->content);
|
|
}
|
|
}
|
|
if (cur->children != NULL) {
|
|
if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
|
|
if (ctxt->level >= 0) ctxt->level++;
|
|
xmlNodeListDumpOutput(ctxt, cur->children);
|
|
if (ctxt->level > 0) ctxt->level--;
|
|
if ((xmlIndentTreeOutput) && (ctxt->format))
|
|
xmlOutputBufferWrite(buf, ctxt->indent_size *
|
|
(ctxt->level > ctxt->indent_nr ?
|
|
ctxt->indent_nr : ctxt->level),
|
|
ctxt->indent);
|
|
}
|
|
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, ">");
|
|
ctxt->format = format;
|
|
}
|
|
|
|
/**
|
|
* xmlDocContentDumpOutput:
|
|
* @cur: the document
|
|
*
|
|
* Dump an XML document.
|
|
*/
|
|
static void
|
|
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
|
|
#ifdef LIBXML_HTML_ENABLED
|
|
xmlDtdPtr dtd;
|
|
int is_xhtml = 0;
|
|
#endif
|
|
const xmlChar *oldenc = cur->encoding;
|
|
const xmlChar *encoding = ctxt->encoding;
|
|
xmlOutputBufferPtr buf;
|
|
|
|
xmlInitParser();
|
|
|
|
if (ctxt->encoding != NULL)
|
|
cur->encoding = BAD_CAST ctxt->encoding;
|
|
|
|
buf = ctxt->buf;
|
|
xmlOutputBufferWriteString(buf, "<?xml version=");
|
|
if (cur->version != NULL)
|
|
xmlBufferWriteQuotedString(buf->buffer, cur->version);
|
|
else
|
|
xmlOutputBufferWriteString(buf, "\"1.0\"");
|
|
if (ctxt->encoding == NULL) {
|
|
if (cur->encoding != NULL)
|
|
encoding = cur->encoding;
|
|
else if (cur->charset != XML_CHAR_ENCODING_UTF8)
|
|
encoding = (const xmlChar *)
|
|
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 *) ctxt->encoding);
|
|
else
|
|
htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
|
|
}
|
|
#endif
|
|
if (cur->children != NULL) {
|
|
xmlNodePtr child = cur->children;
|
|
|
|
while (child != NULL) {
|
|
ctxt->level = 0;
|
|
#ifdef LIBXML_HTML_ENABLED
|
|
if (is_xhtml)
|
|
xhtmlNodeDumpOutput(ctxt, child);
|
|
else
|
|
#endif
|
|
xmlNodeDumpOutputInternal(ctxt, child);
|
|
xmlOutputBufferWriteString(buf, "\n");
|
|
child = child->next;
|
|
}
|
|
}
|
|
if (ctxt->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:
|
|
* @cur: the first attribute pointer
|
|
*
|
|
* Dump a list of XML attributes
|
|
*/
|
|
static void
|
|
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
|
|
xmlAttrPtr xml_lang = NULL;
|
|
xmlAttrPtr lang = NULL;
|
|
xmlAttrPtr name = NULL;
|
|
xmlAttrPtr id = NULL;
|
|
xmlNodePtr parent;
|
|
xmlOutputBufferPtr buf;
|
|
|
|
if (cur == NULL) return;
|
|
buf = ctxt->buf;
|
|
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(ctxt, cur);
|
|
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, ctxt->doc, name);
|
|
xmlOutputBufferWriteString(buf, "\"");
|
|
}
|
|
}
|
|
/*
|
|
* C.7.
|
|
*/
|
|
if ((lang != NULL) && (xml_lang == NULL)) {
|
|
xmlOutputBufferWriteString(buf, " xml:lang=\"");
|
|
xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
|
|
xmlOutputBufferWriteString(buf, "\"");
|
|
} else
|
|
if ((xml_lang != NULL) && (lang == NULL)) {
|
|
xmlOutputBufferWriteString(buf, " lang=\"");
|
|
xmlAttrSerializeContent(buf->buffer, ctxt->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(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
|
|
xmlOutputBufferPtr buf;
|
|
|
|
if (cur == NULL) return;
|
|
buf = ctxt->buf;
|
|
while (cur != NULL) {
|
|
if ((ctxt->format) && (xmlIndentTreeOutput) &&
|
|
(cur->type == XML_ELEMENT_NODE))
|
|
xmlOutputBufferWrite(buf, ctxt->indent_size *
|
|
(ctxt->level > ctxt->indent_nr ?
|
|
ctxt->indent_nr : ctxt->level),
|
|
ctxt->indent);
|
|
xhtmlNodeDumpOutput(ctxt, cur);
|
|
if (ctxt->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.
|
|
*/
|
|
static void
|
|
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
|
|
int format;
|
|
xmlNodePtr tmp;
|
|
xmlChar *start, *end;
|
|
xmlOutputBufferPtr buf;
|
|
|
|
if (cur == NULL) return;
|
|
if (cur->type == XML_XINCLUDE_START)
|
|
return;
|
|
if (cur->type == XML_XINCLUDE_END)
|
|
return;
|
|
if (cur->type == XML_DTD_NODE) {
|
|
xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
|
|
return;
|
|
}
|
|
buf = ctxt->buf;
|
|
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)) {
|
|
|
|
if (ctxt->encoding == NULL) {
|
|
xmlChar *buffer;
|
|
|
|
buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
|
|
cur->content);
|
|
if (buffer != NULL) {
|
|
xmlOutputBufferWriteString(buf, (const char *)buffer);
|
|
xmlFree(buffer);
|
|
}
|
|
} else {
|
|
xmlOutputBufferWriteEscape(buf, cur->content);
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
format = ctxt->format;
|
|
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(ctxt, cur->properties);
|
|
|
|
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)) {
|
|
if (ctxt->encoding == NULL) {
|
|
xmlChar *buffer;
|
|
|
|
buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
|
|
cur->content);
|
|
if (buffer != NULL) {
|
|
xmlOutputBufferWriteString(buf, (const char *)buffer);
|
|
xmlFree(buffer);
|
|
}
|
|
} else {
|
|
xmlOutputBufferWriteEscape(buf, cur->content);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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)) {
|
|
int level = ctxt->level;
|
|
int indent = ctxt->format;
|
|
|
|
ctxt->level = 0;
|
|
ctxt->format = 0;
|
|
xhtmlNodeDumpOutput(ctxt, child);
|
|
ctxt->level = level;
|
|
ctxt->format = indent;
|
|
} 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 {
|
|
int level = ctxt->level;
|
|
int indent = ctxt->format;
|
|
|
|
ctxt->level = 0;
|
|
ctxt->format = 0;
|
|
xhtmlNodeDumpOutput(ctxt, child);
|
|
ctxt->level = level;
|
|
ctxt->format = indent;
|
|
}
|
|
child = child->next;
|
|
}
|
|
} else if (cur->children != NULL) {
|
|
int indent = ctxt->format;
|
|
|
|
if (format) xmlOutputBufferWriteString(buf, "\n");
|
|
if (ctxt->level >= 0) ctxt->level++;
|
|
ctxt->format = format;
|
|
xhtmlNodeListDumpOutput(ctxt, cur->children);
|
|
if (ctxt->level > 0) ctxt->level--;
|
|
ctxt->format = indent;
|
|
if ((xmlIndentTreeOutput) && (format))
|
|
xmlOutputBufferWrite(buf, ctxt->indent_size *
|
|
(ctxt->level > ctxt->indent_nr ?
|
|
ctxt->indent_nr : ctxt->level),
|
|
ctxt->indent);
|
|
}
|
|
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->buf = xmlOutputBufferCreateFd(fd, ret->handler);
|
|
if (ret->buf == 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->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
|
|
compression);
|
|
if (ret->buf == 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->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
|
|
if (ret->buf == NULL) {
|
|
xmlFreeSaveCtxt(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlSaveDoc:
|
|
* @ctxt: a document saving context
|
|
* @doc: a document
|
|
*
|
|
* Save a full document to a saving context
|
|
* TODO: The function is not fully implemented yet as it does not return the
|
|
* byte count but 0 instead
|
|
*
|
|
* Returns the number of byte written or -1 in case of error
|
|
*/
|
|
long
|
|
xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
|
|
{
|
|
long ret = 0;
|
|
|
|
xmlDocContentDumpOutput(ctxt, doc);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlSaveTree:
|
|
* @ctxt: a document saving context
|
|
* @node: a document
|
|
*
|
|
* Save a subtree starting at the node parameter to a saving context
|
|
* TODO: The function is not fully implemented yet as it does not return the
|
|
* byte count but 0 instead
|
|
*
|
|
* Returns the number of byte written or -1 in case of error
|
|
*/
|
|
long
|
|
xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
|
|
{
|
|
long ret = 0;
|
|
|
|
xmlNodeDumpOutputInternal(ctxt, node);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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->buf == NULL) return(-1);
|
|
return(xmlOutputBufferFlush(ctxt->buf));
|
|
}
|
|
|
|
/**
|
|
* 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 " ", 5);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '\r') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST " ", 5);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '\t') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST "	", 4);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '"') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST """, 6);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '<') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST "<", 4);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '>') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST ">", 4);
|
|
cur++;
|
|
base = cur;
|
|
} else if (*cur == '&') {
|
|
if (base != cur)
|
|
xmlBufferAdd(buf, base, cur - base);
|
|
xmlBufferAdd(buf, BAD_CAST "&", 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)
|
|
{
|
|
xmlSaveCtxt ctxt;
|
|
#ifdef LIBXML_HTML_ENABLED
|
|
xmlDtdPtr dtd;
|
|
int is_xhtml = 0;
|
|
#endif
|
|
|
|
xmlInitParser();
|
|
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = doc;
|
|
ctxt.buf = buf;
|
|
ctxt.level = level;
|
|
ctxt.format = format;
|
|
ctxt.encoding = (const xmlChar *) encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
|
|
#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(&ctxt, cur);
|
|
else
|
|
#endif
|
|
xmlNodeDumpOutputInternal(&ctxt, cur);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlSaveCtxt ctxt;
|
|
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;
|
|
}
|
|
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = out_doc;
|
|
ctxt.buf = out_buff;
|
|
ctxt.level = 0;
|
|
ctxt.format = format;
|
|
ctxt.encoding = (const xmlChar *) txt_encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
xmlDocContentDumpOutput(&ctxt, out_doc);
|
|
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) {
|
|
xmlSaveCtxt ctxt;
|
|
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);
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = cur;
|
|
ctxt.buf = buf;
|
|
ctxt.level = 0;
|
|
ctxt.format = format;
|
|
ctxt.encoding = (const xmlChar *) encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
xmlDocContentDumpOutput(&ctxt, cur);
|
|
|
|
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) {
|
|
xmlSaveCtxt ctxt;
|
|
int ret;
|
|
|
|
if (buf == NULL) return(0);
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = cur;
|
|
ctxt.buf = buf;
|
|
ctxt.level = 0;
|
|
ctxt.format = 0;
|
|
ctxt.encoding = (const xmlChar *) encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
xmlDocContentDumpOutput(&ctxt, cur);
|
|
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.
|
|
* NOTE: the I/O buffer is closed as part of the call.
|
|
*
|
|
* returns: the number of bytes written or -1 in case of failure.
|
|
*/
|
|
int
|
|
xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
|
|
const char *encoding, int format)
|
|
{
|
|
xmlSaveCtxt ctxt;
|
|
int ret;
|
|
|
|
if (buf == NULL)
|
|
return (0);
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = cur;
|
|
ctxt.buf = buf;
|
|
ctxt.level = 0;
|
|
ctxt.format = format;
|
|
ctxt.encoding = (const xmlChar *) encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
xmlDocContentDumpOutput(&ctxt, cur);
|
|
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 ) {
|
|
xmlSaveCtxt ctxt;
|
|
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);
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.doc = cur;
|
|
ctxt.buf = buf;
|
|
ctxt.level = 0;
|
|
ctxt.format = format;
|
|
ctxt.encoding = (const xmlChar *) encoding;
|
|
xmlSaveCtxtInit(&ctxt);
|
|
|
|
xmlDocContentDumpOutput(&ctxt, cur);
|
|
|
|
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 */
|
|
|