diff -r 000000000000 -r e35f40988205 xml/libxml2libs/src/libxml2/libxml2_xmlsave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xml/libxml2libs/src/libxml2/libxml2_xmlsave.c Thu Dec 17 09:29:21 2009 +0200 @@ -0,0 +1,2385 @@ +/* + * libxml2_xmlsave.c: Implementation of the document serializer + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. + */ + +#define IN_LIBXML + +#include "xmlenglibxml.h" + +#include + +#include +#include +#include "libxml2_xmlerror2.h" +#include + +#ifdef LIBXML_HTML_ENABLED +#include "libxml2_htmltree.h" +#endif +/************************************************************************ + * * + * 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" + +// Constansts for results that are returned by callbacks of node filter during serialization +const int KFilterAccept = 1; +const int KFilterReject = 2; +const int KFilterSkip = 3; +const int KFilterSkipElementContents = 4; + +/** + * xmlIsXHTML: + * @param systemID the system identifier + * @param 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 + */ +XMLPUBFUNEXPORT int +xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) { + if ((systemID == NULL) && (publicID == NULL)) + return(-1); + if (publicID != NULL) { + if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1); + if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1); + if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1); + } + if (systemID != NULL) { + if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1); + if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1); + if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1); + } + return(0); +} + +#ifdef LIBXML_OUTPUT_ENABLED + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + + + +/************************************************************************ + * * + * Output error handlers * + * * + ************************************************************************/ +/** + * xmlSaveErrMemory: + * @param 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: + * @param code the error number + * @param node the location of the error. + * @param 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 = EMBED_ERRTXT("string is not in UTF-8"); + break; + case XML_SAVE_CHAR_INVALID: + msg = EMBED_ERRTXT("invalid character value"); + break; + case XML_SAVE_UNKNOWN_ENCODING: + msg = EMBED_ERRTXT("unknown encoding %s"); + break; + case XML_SAVE_NO_DOCTYPE: + msg = EMBED_ERRTXT("document has no DOCTYPE"); + break; + default: + msg = EMBED_ERRTXT("unexpected error number"); + } + __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra); +} + + + + + +/************************************************************************ + * * + * Special escaping routines * + * * + ************************************************************************/ +static unsigned char * +xmlSerializeHexCharRef(unsigned char *out, int val) { + unsigned char *ptr; + + *out++ = '&'; + *out++ = '#'; + *out++ = 'x'; + if (val < 0x10) ptr = out; + else if (val < 0x100) ptr = out + 1; + else if (val < 0x1000) ptr = out + 2; + else if (val < 0x10000) ptr = out + 3; + else if (val < 0x100000) ptr = out + 4; + else ptr = out + 5; + out = ptr + 1; + while (val > 0) { + // OPTIMIZE: Isn't that + // bval = (val & 0xF) + // *ptr-- = bval + (bval < 10 ? '0'; 'A'-10) + // much more efficient??? + switch (val & 0xF) { + case 0: *ptr-- = '0'; break; + case 1: *ptr-- = '1'; break; + case 2: *ptr-- = '2'; break; + case 3: *ptr-- = '3'; break; + case 4: *ptr-- = '4'; break; + case 5: *ptr-- = '5'; break; + case 6: *ptr-- = '6'; break; + case 7: *ptr-- = '7'; break; + case 8: *ptr-- = '8'; break; + case 9: *ptr-- = '9'; break; + case 0xA: *ptr-- = 'A'; break; + case 0xB: *ptr-- = 'B'; break; + case 0xC: *ptr-- = 'C'; break; + case 0xD: *ptr-- = 'D'; break; + case 0xE: *ptr-- = 'E'; break; + case 0xF: *ptr-- = 'F'; break; + default: *ptr-- = '0'; break; + } + val >>= 4; + } + *out++ = ';'; + *out = 0; + return(out); +} + + +/** + * xmlAttrSerializeTxtContent: + * @param buf the XML buffer output + * @param doc the document + * @param attr the attribute node + * @param string the text content + * + * Serialize text attribute values to an xml simple buffer + * + * (Previously this function was in xmlsave.c) + */ +XMLPUBFUNEXPORT 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. + */ + unsigned 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"); + xmlSerializeHexCharRef(tmp, *cur); + 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"); + + xmlSerializeHexCharRef(tmp, *cur); + xmlBufferAdd(buf, (xmlChar *) tmp, -1); + cur++; + base = cur; + continue; + } + /* + * We could do multiple things here. Just save + * as a char ref + */ + xmlSerializeHexCharRef(tmp, val); + xmlBufferAdd(buf, (xmlChar *) tmp, -1); + cur += l; + base = cur; + } else { + cur++; + } + } + if (base != cur) + xmlBufferAdd(buf, base, cur - base); +} + +/** + * xmlEscapeEntities: + * @param out a pointer to an array of bytes to store the result + * @param outlen the length of out + * @param in a pointer to an array of unescaped UTF-8 bytes + * @param inlen the length of in + * + * Take a block of UTF-8 chars in and escape them. Used when there is no + * encoding specified. + * + * Returns 0 if success, or -1 otherwise + * The value of inlen after return is the number of octets consumed + * if the return value is positive, else unpredictable. + * The value of outlen after return is the number of octets consumed. + */ +static int +xmlEscapeEntities(unsigned char* out, int *outlen, + const xmlChar* in, int *inlen) { + unsigned char* outstart = out; + const unsigned char* base = in; + unsigned char* outend = out + *outlen; + const unsigned char* inend; + int val; + + inend = in + (*inlen); + + while ((in < inend) && (out < outend)) { + if (*in == '<') { + if (outend - out < 4) break; + *out++ = '&'; + *out++ = 'l'; + *out++ = 't'; + *out++ = ';'; + in++; + continue; + } else if (*in == '>') { + if (outend - out < 4) break; + *out++ = '&'; + *out++ = 'g'; + *out++ = 't'; + *out++ = ';'; + in++; + continue; + } else if (*in == '&') { + if (outend - out < 5) break; + *out++ = '&'; + *out++ = 'a'; + *out++ = 'm'; + *out++ = 'p'; + *out++ = ';'; + in++; + continue; + } else if (((*in >= 0x20) && (*in < 0x80)) || + (*in == '\n') || (*in == '\t')) { + /* + * default case, just copy ! + */ + *out++ = *in++; + continue; + } else if (*in >= 0x80) { + /* + * We assume we have UTF-8 input. + */ + if (outend - out < 10) break; + + if (*in < 0xC0) { + xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL); + in++; + goto error; + } else if (*in < 0xE0) { + if (inend - in < 2) break; + val = (in[0]) & 0x1F; + val <<= 6; + val |= (in[1]) & 0x3F; + in += 2; + } else if (*in < 0xF0) { + if (inend - in < 3) break; + val = (in[0]) & 0x0F; + val <<= 6; + val |= (in[1]) & 0x3F; + val <<= 6; + val |= (in[2]) & 0x3F; + in += 3; + } else if (*in < 0xF8) { + if (inend - in < 4) break; + val = (in[0]) & 0x07; + val <<= 6; + val |= (in[1]) & 0x3F; + val <<= 6; + val |= (in[2]) & 0x3F; + val <<= 6; + val |= (in[3]) & 0x3F; + in += 4; + } else { + xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); + in++; + goto error; + } + if (!IS_CHAR(val)) { + xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); + in++; + goto error; + } + + /* + * We could do multiple things here. Just save as a char ref + */ + out = xmlSerializeHexCharRef(out, val); + } else if (IS_BYTE_CHAR(*in)) { + if (outend - out < 6) break; + out = xmlSerializeHexCharRef(out, *in++); + } else { + xmlGenericError(xmlGenericErrorContext, + "xmlEscapeEntities : char out of range\n"); + in++; + goto error; + } + } + *outlen = out - outstart; + *inlen = in - base; + return(0); +error: + *outlen = out - outstart; + *inlen = in - base; + return(-1); +} + +/************************************************************************ + * * + * Allocation and deallocation * + * * + ************************************************************************/ +/** + * xmlSaveCtxtInit: + * @param ctxt the saving context + * + * Initialize a saving context + * + * OOM: never + */ +//static + +XMLPUBFUNEXPORT void +xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt) +{ + int i; + + if (ctxt == NULL) return; + if ((ctxt->encoding == NULL) && (ctxt->escape == NULL)) + ctxt->escape = xmlEscapeEntities; + if (xmlTreeIndentString == NULL) { + memset(&ctxt->indent[0], 0, MAX_INDENT + 1); + } else { + ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString); + ctxt->indent_nr = MAX_INDENT / ctxt->indent_size; + for (i = 0;i < ctxt->indent_nr;i++) + memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString, + ctxt->indent_size); + ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0; + } +} + +/** + * xmlFreeSaveCtxt: + * + * Free a saving context, destroying the ouptut in any remaining buffer + */ +static void +xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt) +{ + if (ctxt == NULL) return; + if (ctxt->encoding != NULL) + xmlFree((char *) ctxt->encoding); + if (ctxt->buf != NULL) + xmlOutputBufferClose(ctxt->buf); + xmlFree(ctxt); +} + +/** + * xmlNewSaveCtxt: + * + * Create a new saving context + * + * Returns the new structure or NULL in case of error + */ +static xmlSaveCtxtPtr +xmlNewSaveCtxt(const char *encoding, int options) +{ + xmlSaveCtxtPtr ret; + + ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt)); + if (ret == NULL) { + xmlSaveErrMemory("creating saving context"); + return ( NULL ); + } + memset(ret, 0, sizeof(xmlSaveCtxt)); + ret->options = options; + if (encoding != NULL) { + ret->handler = xmlFindCharEncodingHandler(encoding); + if (ret->handler == NULL) { + xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding); + xmlFreeSaveCtxt(ret); + return(NULL); + } + ret->encoding = xmlStrdup((const xmlChar *)encoding); + ret->escape = xmlEscapeEntities; + } + xmlSaveCtxtInit(ret); + + return(ret); +} + +/************************************************************************ + * * + * Dumping XML tree content to a simple buffer * + * * + ************************************************************************/ +/** + * xmlAttrSerializeContent: + * @param buf the XML buffer output + * @param doc the document + * @param attr the attribute pointer + * + * Serialize the attribute in the buffer + */ +static void +xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr) +{ + xmlNodePtr children; + + children = attr->children; + while (children != NULL) { + switch (children->type) { + case XML_TEXT_NODE: + xmlAttrSerializeTxtContent(buf->buffer, attr->doc, + attr, children->content); + break; + case XML_ENTITY_REF_NODE: + xmlBufferAdd(buf->buffer, BAD_CAST "&", 1); + xmlBufferAdd(buf->buffer, children->name, + xmlStrlen(children->name)); + xmlBufferAdd(buf->buffer, 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); + +//were static, now exported +//void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur); +//void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur); + +/** + * xmlNsDumpOutput: + * @param buf the XML buffer output + * @param 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) { + xmlOutputBufferWrite(buf, 7, " xmlns:"); + xmlOutputBufferWriteString(buf, (const char *)cur->prefix); + }else{ + xmlOutputBufferWrite(buf, 6, " xmlns"); + } + xmlOutputBufferWrite(buf, 1, "="); + xmlBufferWriteQuotedString(buf->buffer, cur->href); + } +} + +/** + * xmlNsListDumpOutput: + * @param buf the XML buffer output + * @param 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: + * @param buf the XML buffer output + * @param 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 || !ctxt || !ctxt->buf) + return; + + buf = ctxt->buf; + xmlOutputBufferWrite(buf, 10, "name); + if (dtd->ExternalID) + { + xmlOutputBufferWrite(buf, 8, " PUBLIC "); + xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID); + xmlOutputBufferWrite(buf, 1, " "); + xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID); + } + else if (dtd->SystemID) + { + xmlOutputBufferWrite(buf, 8, " SYSTEM "); + xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID); + } + if (!dtd->entities && !dtd->elements && + !dtd->attributes && !dtd->notations && + !dtd->pentities) + { + xmlOutputBufferWrite(buf, 1, ">"); + return; + } + xmlOutputBufferWrite(buf, 3, " [\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; + xmlOutputBufferWrite(buf, 2, "]>"); +} + +/** + * xmlAttrDumpOutput: + * @param buf the XML buffer output + * @param cur the attribute pointer + * + * Dump an XML attribute + */ +static void +xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { + xmlOutputBufferPtr buf; + + if (cur == NULL) return; + buf = ctxt->buf; + xmlOutputBufferWrite(buf, 1, " "); + if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { + xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + xmlOutputBufferWriteString(buf, (const char *)cur->name); + xmlOutputBufferWrite(buf, 2, "=\""); + xmlAttrSerializeContent(buf, cur); + xmlOutputBufferWrite(buf, 1, "\""); +} + +/** + * xmlAttrListDumpOutput: + * @param buf the XML buffer output + * @param doc the document + * @param cur the first attribute pointer + * @param 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: + * @param cur the first node + * + * Dump an XML node list, recursive behaviour, children are printed too. + */ +static void +xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) +{ + xmlOutputBufferPtr buf; + LOAD_GS_SAFE_NODE(cur) + + 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) { + xmlOutputBufferWrite(buf, 1, "\n"); + } + cur = cur->next; + } +} + +/** + * xmlNodeDumpOutputInternal: + * @param cur the current node + * + * Dump an XML node, recursive behaviour, children are printed too. + */ +//static // it was made exported +XMLPUBFUNEXPORT void +xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) +{ + int format; +// int ret; + xmlNodePtr tmp; + xmlChar *start, *end;// *dataStub; + xmlOutputBufferPtr buf; + +#ifdef XMLENGINE_NODEFILTER + xmlNodeFilterData* filterData = ctxt->nodeFilter; + int skipMode = 0; // normal mode + LOAD_GS_SAFE_NODE(cur) + if(filterData) + { + switch(filterData->proxyFn(filterData->fn, cur)) + { + + // Use constants. + case 2 /* KFilterReject */: + return; + case 3 /* KFilterSkip */: + skipMode = 1; // Skip node + break; + case 4 /* KFilterSkipElementContents */: + skipMode = (cur->type == XML_ELEMENT_NODE) ? 2 : 0; + break; + default: ; + // DO NOTHING, though the result is bogus + } + } +#else + LOAD_GS_SAFE_NODE(cur) +#endif + if (!cur) + return; + + buf = ctxt->buf; + + // DONE: OPTIMIZATION: quicker selection of code flow + if(cur->type != XML_ELEMENT_NODE) + { + + /* Disabled: + if (cur->type == XML_XINCLUDE_START) + return; + if (cur->type == XML_XINCLUDE_END) + return; + */ + if (cur->type == XML_ATTRIBUTE_NODE) { + xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur); + return; + } + if (cur->type == XML_NAMESPACE_DECL) { + xmlNsDumpOutput(buf, (xmlNsPtr) cur); + return; + } + if (cur->type == XML_TEXT_NODE) { + if (cur->content) { + if (TEXT_IS_DATA(cur) ){ + if( ctxt->textNodeCallback){ + xmlChar* output = NULL; + int written = 0; + if (buf->writecallback) { + /* + * second write the stuff to the I/O channel + */ + if (buf->encoder != NULL) { + written = buf->writecallback(buf->context, + (const char *)buf->conv->content, buf->conv->use); + if (written >= 0) + xmlBufferShrink(buf->conv, written); + } else { + written = buf->writecallback(buf->context, + (const char *)buf->buffer->content, buf->buffer->use); + if (written >= 0) + xmlBufferShrink(buf->buffer, written); + } + if (written < 0) { + __xmlIOErr(XML_FROM_IO, XML_IO_WRITE, NULL); + buf->error = written; + return; + } + buf->written += written; + } + output = ctxt->textNodeCallback(ctxt->context, cur, &written); + if(written) + xmlOutputBufferWrite(buf, written, (const char *) output); + } + } + else{ + if (cur->name != xmlStringTextNoenc){ + xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); + } + else{ + /* + * Disable escaping, needed for XSLT + */ + xmlOutputBufferWriteString(buf, (const char *) cur->content); + } + } + } + return; + } + if (cur->type == XML_CDATA_SECTION_NODE) { + start = end = cur->content; + while (*end != '\0') { + if ((*end == ']') && + (*(end + 1) == ']') && + (*(end + 2) == '>')) + { + end = end + 2; + xmlOutputBufferWrite(buf, 9, ""); + start = end; + } + end++; + } + if (start != end) { + xmlOutputBufferWrite(buf, 9, ""); + } + return; + } + if (cur->type == XML_PI_NODE) { + // DONE: correct program flow / exclude redundant IF check + xmlOutputBufferWrite(buf, 2, "name); + if (cur->content) { + xmlOutputBufferWrite(buf, 1, " "); + xmlOutputBufferWriteString(buf, (const char*)cur->content); + } + xmlOutputBufferWrite(buf, 2, "?>"); + return; + } + if (cur->type == XML_COMMENT_NODE) { + if (cur->content) { + xmlOutputBufferWrite(buf, 4, ""); + } + return; + } + if (cur->type == XML_ENTITY_REF_NODE) { + xmlOutputBufferWrite(buf, 1, "&"); + xmlOutputBufferWriteString(buf, (const char *)cur->name); + xmlOutputBufferWrite(buf, 1, ";"); + return; + } + if (cur->type == XML_DOCUMENT_NODE || + cur->type == XML_DOCUMENT_FRAG_NODE) + { + xmlNodeListDumpOutput(ctxt, cur->children); + return; + } + if (cur->type == XML_DTD_NODE) { + xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur); + 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; + } + return; // defaulted action for other types of nodes + } // end of "if not element node" + // + //XE_ASSERT_DEBUG(cur->type == XML_ELEMENT_NODE); + // + format = ctxt->format; + if (format == 1) { + tmp = cur->children; + while (tmp) { + 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; + } + } + +#ifdef XMLENGINE_NODEFILTER + if (skipMode != 1 /* skip element */) + { +#endif + xmlOutputBufferWrite(buf, 1, "<"); + if (cur->ns && cur->ns->prefix) { + xmlOutputBufferWriteString(buf, (const char*)cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + + xmlOutputBufferWriteString(buf, (const char*)cur->name); + if (cur->nsDef) + xmlNsListDumpOutput(buf, cur->nsDef); + if (cur->properties) + xmlAttrListDumpOutput(ctxt, cur->properties); +#ifdef XMLENGINE_NODEFILTER + } +#endif + +#ifdef XMLENGINE_NODEFILTER + if(skipMode != 1) + { + // XMLENGINE: BEGIN REPLACE CODE + // skipMode check is inserted + if ( (skipMode == 2 /* skip contents */ + || + ( + !cur->content + && + !cur->children + ) + ) + && !xmlSaveNoEmptyTags + ) +#else // OLD CODE: + if (!cur->content && + !cur->children && + !xmlSaveNoEmptyTags) +#endif + { + xmlOutputBufferWrite(buf, 2, "/>"); + ctxt->format = format; + return; + } + xmlOutputBufferWrite(buf, 1, ">"); +#ifdef XMLENGINE_NODEFILTER + } // if(skipMode != 1) +#endif + +#ifdef XMLENGINE_NODEFILTER + if(skipMode != 2){ +#endif + + if (cur->type != XML_ELEMENT_NODE && + cur->content) + { + xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); + } + + if (cur->children) { + if (ctxt->format) + xmlOutputBufferWrite(buf, 1, "\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 + ); + } + } +#ifdef XMLENGINE_NODEFILTER + } // if(skipMode != 2) // not skipping children & contents +#endif + +#ifdef XMLENGINE_NODEFILTER + if(skipMode != 1){ +#endif + xmlOutputBufferWrite(buf, 2, "ns && cur->ns->prefix) { + xmlOutputBufferWriteString(buf, (const char*) cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + + xmlOutputBufferWriteString(buf, (const char*) cur->name); + xmlOutputBufferWrite(buf, 1, ">"); +#ifdef XMLENGINE_NODEFILTER + } //if(skipMode != 1) +#endif + + ctxt->format = format; +} + +/** + * xmlDocContentDumpOutput: + * @param cur the document + * + * Dump an XML document. + */ +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; + LOAD_GS_SAFE_DOC(cur) + + xmlInitParser(); + if(OOM_FLAG) + return; + + if (ctxt->encoding) + cur->encoding = BAD_CAST ctxt->encoding; + + buf = ctxt->buf; + xmlOutputBufferWrite(buf, 14, "version != NULL) + xmlBufferWriteQuotedString(buf->buffer, cur->version); + else + xmlOutputBufferWrite(buf, 5, "\"1.0\""); + + if (!ctxt->encoding) { + if (cur->encoding) + encoding = cur->encoding; + else if (cur->charset != XML_CHAR_ENCODING_UTF8) + encoding = (const xmlChar *) + xmlGetCharEncodingName((xmlCharEncoding) cur->charset); + } + if (encoding) { + xmlOutputBufferWrite(buf, 10, " encoding="); + xmlBufferWriteQuotedString(buf->buffer, (xmlChar*) encoding); + } + switch (cur->standalone) { + case 0: + xmlOutputBufferWrite(buf, 16, " standalone=\"no\""); + break; + case 1: + xmlOutputBufferWrite(buf, 17, " standalone=\"yes\""); + break; + } + xmlOutputBufferWrite(buf, 3, "?>\n"); + +#ifdef LIBXML_HTML_ENABLED + dtd = xmlGetIntSubset(cur); + if (dtd) { + is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID); + if (is_xhtml < 0) is_xhtml = 0; + } + if (is_xhtml) { + if (encoding) + htmlSetMetaEncoding(cur, (const xmlChar*) ctxt->encoding); + else + htmlSetMetaEncoding(cur, BAD_CAST "UTF-8"); + } +#endif + if (cur->children) { + xmlNodePtr child = cur->children; + + while (child) { + ctxt->level = 0; +#ifdef LIBXML_HTML_ENABLED + if (is_xhtml){ + xhtmlNodeDumpOutput(ctxt, child); + } else +#endif + { + xmlNodeDumpOutputInternal(ctxt, child); + } + xmlOutputBufferWrite(buf, 1, "\n"); + child = child->next; + } + } + if (ctxt->encoding) + cur->encoding = oldenc; +} + +#ifdef LIBXML_HTML_ENABLED +/************************************************************************ + * * + * Functions specific to XHTML serialization * + * * + ************************************************************************/ + +/** + * xhtmlIsEmpty: + * @param 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: + * @param 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")))) { + xmlOutputBufferWrite(buf, 5, " id=\""); + xmlAttrSerializeContent(buf, name); + xmlOutputBufferWrite(buf, 1, "\""); + } + } + /* + * C.7. + */ + if ((lang != NULL) && (xml_lang == NULL)) { + xmlOutputBufferWrite(buf, 11, " xml:lang=\""); + xmlAttrSerializeContent(buf, lang); + xmlOutputBufferWrite(buf, 1, "\""); + } else + if ((xml_lang != NULL) && (lang == NULL)) { + xmlOutputBufferWrite(buf, 7, " lang=\""); + xmlAttrSerializeContent(buf, xml_lang); + xmlOutputBufferWrite(buf, 1, "\""); + } +} + +/** + * xhtmlNodeListDumpOutput: + * @param buf the XML buffer output + * @param doc the XHTML document + * @param cur the first node + * @param level the imbrication level for indenting + * @param format is formatting allowed + * @param 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) { + xmlOutputBufferWrite(buf, 1, "\n"); + } + cur = cur->next; + } +} + +/** + * xhtmlNodeDumpOutput: + * @param buf the XML buffer output + * @param doc the XHTML document + * @param cur the current node + * @param level the imbrication level for indenting + * @param format is formatting allowed + * @param 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)) { + xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); + } else { + /* + * Disable escaping, needed for XSLT + */ + xmlOutputBufferWriteString(buf, (const char *) cur->content); + } + } + + return; + } + if (cur->type == XML_PI_NODE) { + if (cur->content != NULL) { + xmlOutputBufferWrite(buf, 2, "name); + if (cur->content != NULL) { + xmlOutputBufferWrite(buf, 1, " "); + xmlOutputBufferWriteString(buf, (const char *)cur->content); + } + xmlOutputBufferWrite(buf, 2, "?>"); + } else { + xmlOutputBufferWrite(buf, 2, "name); + xmlOutputBufferWrite(buf, 2, "?>"); + } + return; + } + if (cur->type == XML_COMMENT_NODE) { + if (cur->content != NULL) { + xmlOutputBufferWrite(buf, 4, ""); + } + return; + } + if (cur->type == XML_ENTITY_REF_NODE) { + xmlOutputBufferWrite(buf, 1, "&"); + xmlOutputBufferWriteString(buf, (const char *)cur->name); + xmlOutputBufferWrite(buf, 1, ";"); + return; + } + if (cur->type == XML_CDATA_SECTION_NODE) { + start = end = cur->content; + while (*end != '\0') { + if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') { + end = end + 2; + xmlOutputBufferWrite(buf, 9, ""); + start = end; + } + end++; + } + if (start != end) { + xmlOutputBufferWrite(buf, 9, ""); + } + 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; + } + } + xmlOutputBufferWrite(buf, 1, "<"); + if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { + xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + + 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 + */ + xmlOutputBufferWrite(buf, 3, " />"); + } else { + /* + * C.3. Element Minimization and Empty Element Content + */ + xmlOutputBufferWrite(buf, 3, ">ns != NULL) && (cur->ns->prefix != NULL)) { + xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + xmlOutputBufferWriteString(buf, (const char *)cur->name); + xmlOutputBufferWrite(buf, 1, ">"); + } + return; + } + xmlOutputBufferWrite(buf, 1, ">"); + if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { + xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); + } + + /* + * 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; + xmlOutputBufferWrite(buf, 9, ""); + start = end; + } + end++; + } + if (start != end) { + xmlOutputBufferWrite(buf, 9, ""); + } + } + } else { + int level = ctxt->level; + int indent = ctxt->format; + + ctxt->level = 0; + ctxt->format = 0; + xhtmlNodeDumpOutput(ctxt, child); + ctxt->level = level; + ctxt->format = indent; + } + child = child->next; + } + } else if (cur->children != NULL) { + int indent = ctxt->format; + + if (format) xmlOutputBufferWrite(buf, 1, "\n"); + if (ctxt->level >= 0) ctxt->level++; + ctxt->format = format; + xhtmlNodeListDumpOutput(ctxt, cur->children); + if (ctxt->level > 0) ctxt->level--; + ctxt->format = indent; + if ((xmlIndentTreeOutput) && (format)) + xmlOutputBufferWrite(buf, ctxt->indent_size * + (ctxt->level > ctxt->indent_nr ? + ctxt->indent_nr : ctxt->level), + ctxt->indent); + } + xmlOutputBufferWrite(buf, 2, "ns != NULL) && (cur->ns->prefix != NULL)) { + xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + + xmlOutputBufferWriteString(buf, (const char *)cur->name); + xmlOutputBufferWrite(buf, 1, ">"); +} +#endif + +/************************************************************************ + * * + * Public entry points * + * * + ************************************************************************/ + +/** + * xmlSaveToFd: + * @param fd a file descriptor number + * @param encoding the encoding name to use or NULL + * @param 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. + */ +XMLPUBFUNEXPORT xmlSaveCtxtPtr +xmlSaveToFd(int fd, const char* encoding, int options) +{ + xmlSaveCtxtPtr ret; + + ret = xmlNewSaveCtxt(encoding, options); + if (!ret) + return ret; + ret->buf = xmlOutputBufferCreateFd(fd, ret->handler); + if (!ret->buf) { + xmlFreeSaveCtxt(ret); + return(NULL); + } + return(ret); +} + +/** + * xmlSaveToFilename: + * @param filename a file name or an URL + * @param encoding the encoding name to use or NULL + * @param 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. + */ +XMLPUBFUNEXPORT xmlSaveCtxtPtr +xmlSaveToFilename(const char* filename, const char* encoding, int options) +{ + xmlSaveCtxtPtr ret; + int compression = 0; + + ret = xmlNewSaveCtxt(encoding, options); + if (!ret) + return ret /* NULL */; + ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler, compression); + if (!ret->buf) { + xmlFreeSaveCtxt(ret); + return(NULL); + } + return(ret); +} + +#if 0 +/** + * xmlSaveToBuffer: + * @param buffer a buffer + * @param encoding the encoding name to use or NULL + * @param 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: + * @param iowrite an I/O write function + * @param ioclose an I/O close function + * @param ioctx an I/O handler + * @param encoding the encoding name to use or NULL + * @param 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. + */ +XMLPUBFUNEXPORT xmlSaveCtxtPtr +xmlSaveToIO(xmlOutputWriteCallback iowrite, + xmlOutputCloseCallback ioclose, + void* ioctx, const char* encoding, int options) +{ + xmlSaveCtxtPtr ret; + + ret = xmlNewSaveCtxt(encoding, options); + if (ret) + { + ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler); + if (!ret->buf) { + xmlFreeSaveCtxt(ret); + ret = NULL; + } + } + return(ret); +} + +/** + * xmlSaveDoc: + * @param ctxt a document saving context + * @param doc a document + * + * Save a full document to a saving context + * 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 + */ +XMLPUBFUNEXPORT long +xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc) +{ + long ret = 0; + + xmlDocContentDumpOutput(ctxt, doc); + return(ret); +} + +/** + * xmlSaveTree: + * @param ctxt a document saving context + * @param node a document + * + * Save a subtree starting at the node parameter to a saving context + * 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 + */ +XMLPUBFUNEXPORT long +xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node) +{ + long ret = 0; + + xmlNodeDumpOutputInternal(ctxt, node); + return(ret); +} + +/** + * xmlSaveFlush: + * @param 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. + */ +XMLPUBFUNEXPORT int +xmlSaveFlush(xmlSaveCtxtPtr ctxt) +{ + if (ctxt == NULL) return(-1); + if (ctxt->buf == NULL) return(-1); + return(xmlOutputBufferFlush(ctxt->buf)); +} + +/** + * xmlSaveClose: + * @param 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. + */ +XMLPUBFUNEXPORT int +xmlSaveClose(xmlSaveCtxtPtr ctxt) +{ + int ret; + + if (ctxt == NULL) return(-1); + ret = xmlSaveFlush(ctxt); + xmlFreeSaveCtxt(ctxt); + return(ret); +} + +/** + * xmlSaveSetEscape: + * @param ctxt a document saving context + * @param escape the escaping function + * + * Set a custom escaping function to be used for text in element content + * + * Returns 0 if successful or -1 in case of error. + */ +XMLPUBFUNEXPORT int +xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape) +{ + if (ctxt == NULL) return(-1); + ctxt->escape = escape; + return(0); +} + +/** + * xmlSaveSetAttrEscape: + * @param ctxt a document saving context + * @param escape the escaping function + * + * Set a custom escaping function to be used for text in attribute content + * + * Returns 0 if successful or -1 in case of error. + */ +XMLPUBFUNEXPORT int +xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape) +{ + if (ctxt == NULL) return(-1); + ctxt->escapeAttr = escape; + return(0); +} + +/************************************************************************ + * * + * Public entry points based on buffers * + * * + ************************************************************************/ +/** + * xmlAttrSerializeTxtContent: + * @param buf the XML buffer output + * @param doc the document + * @param attr the attribute node + * @param string the text content + * + * Serialize text attribute values to an xml simple buffer + * + * (moved to tree.c) + */ + + + +/** + * xmlNodeDump: + * @param buf the XML buffer output + * @param doc the document + * @param cur the current node + * @param level the imbrication level for indenting + * @param 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 + */ +XMLPUBFUNEXPORT 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: + * @param f the FILE * for the output + * @param doc the document + * @param 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 = (xmlOutputBufferPtr)xmlOutputBufferCreateFile(f, NULL); + if (!outbuf) + { // OOM!!! + 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: + * @param buf the XML buffer output + * @param doc the document + * @param cur the current node + * @param level the imbrication level for indenting + * @param format is formatting allowed + * @param 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 + */ +XMLPUBFUNEXPORT 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: + * @param out_doc Document to generate XML text from + * @param doc_txt_ptr Memory pointer for allocated XML text + * @param doc_txt_len Length of the generated XML text + * @param txt_encoding Character encoding to use when generating XML text + * @param 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 + */ + +XMLPUBFUNEXPORT 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: + * @param cur the document + * @param mem OUT: the memory pointer + * @param 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. + */ +XMLPUBFUNEXPORT void +xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) { + xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0); +} + +/** + * xmlDocDumpFormatMemory: + * @param cur the document + * @param mem OUT: the memory pointer + * @param size OUT: the memory length + * @param 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 + */ +XMLPUBFUNEXPORT void +xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) { + xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format); +} + +/** + * xmlDocDumpMemoryEnc: + * @param out_doc Document to generate XML text from + * @param doc_txt_ptr Memory pointer for allocated XML text + * @param doc_txt_len Length of the generated XML text + * @param 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(). + */ + +XMLPUBFUNEXPORT 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: + * @param f the FILE* + * @param cur the document + * @param 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 = (xmlOutputBufferPtr)xmlOutputBufferCreateFile(f, handler); + + if (buf == NULL) + { // OOM!!! + 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: + * @param f the FILE* + * @param cur the document + * + * Dump an XML document to an open FILE. + * + * @return the number of bytes written or -1 in case of failure + * (OOM, I/O error or invalid arguments) + */ +int +xmlDocDump(FILE *f, xmlDocPtr cur) { + return(xmlDocFormatDump (f, cur, 0)); +} + +/** + * xmlSaveFileTo: + * @param buf an output I/O buffer + * @param cur the document + * @param 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. + */ +XMLPUBFUNEXPORT 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: + * @param buf an output I/O buffer + * @param cur the document + * @param encoding the encoding if any assuming the I/O layer handles the trancoding + * @param 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. + */ +XMLPUBFUNEXPORT 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: + * @param filename the filename or URL to output + * @param cur the document being saved + * @param encoding the name of the encoding to use or NULL. + * @param 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 + */ +XMLPUBFUNEXPORT int +xmlSaveFormatFileEnc( const char* filename, xmlDocPtr cur, + const char* encoding, int format ) { + xmlSaveCtxt ctxt; + xmlOutputBufferPtr buf; + xmlCharEncodingHandlerPtr handler = NULL; + int ret; + + if (!cur) + return(-1); + + if (!encoding) + encoding = (const char*) cur->encoding; + + if (encoding) { + handler = xmlFindCharEncodingHandler(encoding); + if (!handler) + 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) + return(-1); + memset(&ctxt, 0, sizeof(ctxt)); + + ctxt.doc = cur; + ctxt.buf = buf; + //ctxt.level = 0; // already zeroed by memset + ctxt.format = format; + ctxt.encoding = (const xmlChar*) encoding; + xmlSaveCtxtInit(&ctxt); + xmlDocContentDumpOutput(&ctxt, cur); + + ret = xmlOutputBufferClose(buf); + return(ret); +} + + +/** + * xmlSaveFileEnc: + * @param filename the filename (or URL) + * @param cur the document + * @param 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. + */ +XMLPUBFUNEXPORT int +xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) { + return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) ); +} + +/** + * xmlSaveFormatFile: + * @param filename the filename (or URL) + * @param cur the document + * @param 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. + */ +XMLPUBFUNEXPORT int +xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) { + return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) ); +} + +/** + * xmlSaveFile: + * @param filename the filename (or URL) + * @param 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. + */ +XMLPUBFUNEXPORT int +xmlSaveFile(const char *filename, xmlDocPtr cur) { + return(xmlSaveFormatFileEnc(filename, cur, NULL, 0)); +} + +#endif /* LIBXML_OUTPUT_ENABLED */