| /* |
| * 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> |
| |
| #define MAX_INDENT 60 |
| |
| /************************************************************************ |
| * * |
| * 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 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); |
| 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)) { |
| xmlChar *buffer; |
| |
| if (ctxt->encoding == NULL) |
| buffer = xmlEncodeEntitiesReentrant(ctxt->doc, |
| cur->content); |
| else |
| buffer = xmlEncodeSpecialChars(ctxt->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(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)) { |
| xmlChar *buffer; |
| |
| if (ctxt->encoding == NULL) |
| buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content); |
| else |
| buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content); |
| if (buffer != NULL) { |
| xmlOutputBufferWriteString(buf, (const char *)buffer); |
| xmlFree(buffer); |
| } |
| } |
| 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)) { |
| xmlChar *buffer; |
| |
| if (ctxt->encoding == NULL) |
| buffer = xmlEncodeEntitiesReentrant(ctxt->doc, |
| cur->content); |
| else |
| buffer = xmlEncodeSpecialChars(ctxt->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; |
| } |
| |
| 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)) { |
| xmlChar *buffer; |
| |
| if (ctxt->encoding == NULL) |
| buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content); |
| else |
| buffer = xmlEncodeSpecialChars(ctxt->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)) { |
| 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) { |
| if (format) xmlOutputBufferWriteString(buf, "\n"); |
| if (ctxt->level >= 0) ctxt->level++; |
| xhtmlNodeListDumpOutput(ctxt, cur->children); |
| if (ctxt->level > 0) ctxt->level--; |
| 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 */ |
| |