/* * 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 #include #include #include #include #ifdef LIBXML_HTML_ENABLED #include /************************************************************************ * * * 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; }; /************************************************************************ * * * Output error handlers * * * ************************************************************************/ /** * xmlSaveErrMemory: * @extra: extra informations * * Handle an out of memory condition */ static void xmlSaveErrMemory(const char *extra) { __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra); } /** * xmlSaveErr: * @code: the error number * @node: the location of the error. * @extra: extra informations * * Handle an out of memory condition */ static void xmlSaveErr(int code, xmlNodePtr node, const char *extra) { const char *msg = NULL; switch(code) { case XML_SAVE_NOT_UTF8: msg = "string is not in UTF-8"; break; case XML_SAVE_CHAR_INVALID: msg = "invalid character value"; break; case XML_SAVE_UNKNOWN_ENCODING: msg = "unknown encoding %s"; break; case XML_SAVE_NO_DOCTYPE: msg = "document has no DOCTYPE"; break; default: msg = "unexpected error number"; } __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra); } /************************************************************************ * * * Allocation and deallocation * * * ************************************************************************/ /** * xmlFreeSaveCtxt: * * Free a saving context, destroying the ouptut in any remaining buffer */ static void xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt) { if (ctxt == NULL) return; if (ctxt->encoding != NULL) xmlFree((char *) ctxt->encoding); xmlFree(ctxt); } /** * xmlNewSaveCtxt: * * Create a new saving context * * Returns the new structure or NULL in case of error */ static xmlSaveCtxtPtr xmlNewSaveCtxt(const char *encoding, int options) { xmlSaveCtxtPtr ret; ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt)); if (ret == NULL) { xmlSaveErrMemory("creating saving context"); return ( NULL ); } memset(ret, 0, sizeof(xmlSaveCtxt)); ret->options = options; if (encoding != NULL) { ret->handler = xmlFindCharEncodingHandler(encoding); if (ret->handler == NULL) { xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding); xmlFreeSaveCtxt(ret); return(NULL); } ret->encoding = xmlStrdup((const xmlChar *)encoding); } return(ret); } /************************************************************************ * * * Dumping XML tree content to a simple buffer * * * ************************************************************************/ /** * xmlAttrSerializeContent: * @buf: the XML buffer output * @doc: the document * @attr: the attribute pointer * * Serialize the attribute in the buffer */ static void xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr) { xmlNodePtr children; children = attr->children; while (children != NULL) { switch (children->type) { case XML_TEXT_NODE: xmlAttrSerializeTxtContent(buf, doc, attr, children->content); break; case XML_ENTITY_REF_NODE: xmlBufferAdd(buf, BAD_CAST "&", 1); xmlBufferAdd(buf, children->name, xmlStrlen(children->name)); xmlBufferAdd(buf, BAD_CAST ";", 1); break; default: /* should not happen unless we have a badly built tree */ break; } children = children->next; } } /************************************************************************ * * * Dumping XML tree content to an I/O output buffer * * * ************************************************************************/ #ifdef LIBXML_HTML_ENABLED static void xhtmlNodeDumpOutput(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, "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) { int i; xmlOutputBufferPtr buf; if (cur == NULL) return; buf = ctxt->buf; while (cur != NULL) { if ((ctxt->format) && (xmlIndentTreeOutput) && (cur->type == XML_ELEMENT_NODE)) for (i = 0;i < ctxt->level;i++) xmlOutputBufferWriteString(buf, xmlTreeIndentString); 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 i, 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, "name); if (cur->content != NULL) { xmlOutputBufferWriteString(buf, " "); xmlOutputBufferWriteString(buf, (const char *)cur->content); } xmlOutputBufferWriteString(buf, "?>"); } else { xmlOutputBufferWriteString(buf, "name); xmlOutputBufferWriteString(buf, "?>"); } return; } if (cur->type == XML_COMMENT_NODE) { if (cur->content != NULL) { 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, ""); start = end; } end++; } if (start != end) { 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)) for (i = 0;i < ctxt->level;i++) xmlOutputBufferWriteString(buf, xmlTreeIndentString); } xmlOutputBufferWriteString(buf, "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, "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) { int i; xmlOutputBufferPtr buf; if (cur == NULL) return; buf = ctxt->buf; while (cur != NULL) { if ((ctxt->format) && (xmlIndentTreeOutput) && (cur->type == XML_ELEMENT_NODE)) for (i = 0;i < ctxt->level;i++) xmlOutputBufferWriteString(buf, xmlTreeIndentString); 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 i, 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, "name); if (cur->content != NULL) { xmlOutputBufferWriteString(buf, " "); xmlOutputBufferWriteString(buf, (const char *)cur->content); } xmlOutputBufferWriteString(buf, "?>"); } else { xmlOutputBufferWriteString(buf, "name); xmlOutputBufferWriteString(buf, "?>"); } return; } if (cur->type == XML_COMMENT_NODE) { if (cur->content != NULL) { 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, ""); start = end; } end++; } if (start != end) { 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, ">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, ""); start = end; } end++; } if (start != end) { 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)) for (i = 0;i < ctxt->level;i++) xmlOutputBufferWriteString(buf, xmlTreeIndentString); } xmlOutputBufferWriteString(buf, "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 * * Returns the number of byte written or -1 in case of error */ long xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc) { long ret = 0; xmlDocContentDumpOutput(ctxt, doc); TODO /* compute ret */ return(ret); } /** * xmlSaveTree: * @ctxt: a document saving context * @node: a document * * Save a subtree starting at the node parameter to a saving context * * Returns the number of byte written or -1 in case of error */ long xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node) { long ret = 0; xmlNodeDumpOutputInternal(ctxt, node); TODO /* compute ret */ 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; #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; 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; 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; 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; 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; 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 */