Home | History | Annotate | Download | only in libxml2
      1 /*
      2  * xmlsave.c: Implemetation of the document serializer
      3  *
      4  * See Copyright for the status of this software.
      5  *
      6  * daniel (at) veillard.com
      7  */
      8 
      9 #define IN_LIBXML
     10 #include "libxml.h"
     11 
     12 #include <string.h>
     13 #include <libxml/xmlmemory.h>
     14 #include <libxml/parserInternals.h>
     15 #include <libxml/tree.h>
     16 #include <libxml/xmlsave.h>
     17 
     18 #define MAX_INDENT 60
     19 
     20 #include <libxml/HTMLtree.h>
     21 
     22 #include "buf.h"
     23 #include "enc.h"
     24 #include "save.h"
     25 
     26 /************************************************************************
     27  *									*
     28  *			XHTML detection					*
     29  *									*
     30  ************************************************************************/
     31 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
     32    "-//W3C//DTD XHTML 1.0 Strict//EN"
     33 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
     34    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
     35 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
     36    "-//W3C//DTD XHTML 1.0 Frameset//EN"
     37 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
     38    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
     39 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
     40    "-//W3C//DTD XHTML 1.0 Transitional//EN"
     41 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
     42    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
     43 
     44 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
     45 /**
     46  * xmlIsXHTML:
     47  * @systemID:  the system identifier
     48  * @publicID:  the public identifier
     49  *
     50  * Try to find if the document correspond to an XHTML DTD
     51  *
     52  * Returns 1 if true, 0 if not and -1 in case of error
     53  */
     54 int
     55 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
     56     if ((systemID == NULL) && (publicID == NULL))
     57 	return(-1);
     58     if (publicID != NULL) {
     59 	if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
     60 	if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
     61 	if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
     62     }
     63     if (systemID != NULL) {
     64 	if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
     65 	if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
     66 	if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
     67     }
     68     return(0);
     69 }
     70 
     71 #ifdef LIBXML_OUTPUT_ENABLED
     72 
     73 #define TODO								\
     74     xmlGenericError(xmlGenericErrorContext,				\
     75 	    "Unimplemented block at %s:%d\n",				\
     76             __FILE__, __LINE__);
     77 
     78 struct _xmlSaveCtxt {
     79     void *_private;
     80     int type;
     81     int fd;
     82     const xmlChar *filename;
     83     const xmlChar *encoding;
     84     xmlCharEncodingHandlerPtr handler;
     85     xmlOutputBufferPtr buf;
     86     xmlDocPtr doc;
     87     int options;
     88     int level;
     89     int format;
     90     char indent[MAX_INDENT + 1];	/* array for indenting output */
     91     int indent_nr;
     92     int indent_size;
     93     xmlCharEncodingOutputFunc escape;	/* used for element content */
     94     xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
     95 };
     96 
     97 /************************************************************************
     98  *									*
     99  *			Output error handlers				*
    100  *									*
    101  ************************************************************************/
    102 /**
    103  * xmlSaveErrMemory:
    104  * @extra:  extra informations
    105  *
    106  * Handle an out of memory condition
    107  */
    108 static void
    109 xmlSaveErrMemory(const char *extra)
    110 {
    111     __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
    112 }
    113 
    114 /**
    115  * xmlSaveErr:
    116  * @code:  the error number
    117  * @node:  the location of the error.
    118  * @extra:  extra informations
    119  *
    120  * Handle an out of memory condition
    121  */
    122 static void
    123 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
    124 {
    125     const char *msg = NULL;
    126 
    127     switch(code) {
    128         case XML_SAVE_NOT_UTF8:
    129 	    msg = "string is not in UTF-8\n";
    130 	    break;
    131 	case XML_SAVE_CHAR_INVALID:
    132 	    msg = "invalid character value\n";
    133 	    break;
    134 	case XML_SAVE_UNKNOWN_ENCODING:
    135 	    msg = "unknown encoding %s\n";
    136 	    break;
    137 	case XML_SAVE_NO_DOCTYPE:
    138 	    msg = "document has no DOCTYPE\n";
    139 	    break;
    140 	default:
    141 	    msg = "unexpected error number\n";
    142     }
    143     __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
    144 }
    145 
    146 /************************************************************************
    147  *									*
    148  *			Special escaping routines			*
    149  *									*
    150  ************************************************************************/
    151 static unsigned char *
    152 xmlSerializeHexCharRef(unsigned char *out, int val) {
    153     unsigned char *ptr;
    154 
    155     *out++ = '&';
    156     *out++ = '#';
    157     *out++ = 'x';
    158     if (val < 0x10) ptr = out;
    159     else if (val < 0x100) ptr = out + 1;
    160     else if (val < 0x1000) ptr = out + 2;
    161     else if (val < 0x10000) ptr = out + 3;
    162     else if (val < 0x100000) ptr = out + 4;
    163     else ptr = out + 5;
    164     out = ptr + 1;
    165     while (val > 0) {
    166 	switch (val & 0xF) {
    167 	    case 0: *ptr-- = '0'; break;
    168 	    case 1: *ptr-- = '1'; break;
    169 	    case 2: *ptr-- = '2'; break;
    170 	    case 3: *ptr-- = '3'; break;
    171 	    case 4: *ptr-- = '4'; break;
    172 	    case 5: *ptr-- = '5'; break;
    173 	    case 6: *ptr-- = '6'; break;
    174 	    case 7: *ptr-- = '7'; break;
    175 	    case 8: *ptr-- = '8'; break;
    176 	    case 9: *ptr-- = '9'; break;
    177 	    case 0xA: *ptr-- = 'A'; break;
    178 	    case 0xB: *ptr-- = 'B'; break;
    179 	    case 0xC: *ptr-- = 'C'; break;
    180 	    case 0xD: *ptr-- = 'D'; break;
    181 	    case 0xE: *ptr-- = 'E'; break;
    182 	    case 0xF: *ptr-- = 'F'; break;
    183 	    default: *ptr-- = '0'; break;
    184 	}
    185 	val >>= 4;
    186     }
    187     *out++ = ';';
    188     *out = 0;
    189     return(out);
    190 }
    191 
    192 /**
    193  * xmlEscapeEntities:
    194  * @out:  a pointer to an array of bytes to store the result
    195  * @outlen:  the length of @out
    196  * @in:  a pointer to an array of unescaped UTF-8 bytes
    197  * @inlen:  the length of @in
    198  *
    199  * Take a block of UTF-8 chars in and escape them. Used when there is no
    200  * encoding specified.
    201  *
    202  * Returns 0 if success, or -1 otherwise
    203  * The value of @inlen after return is the number of octets consumed
    204  *     if the return value is positive, else unpredictable.
    205  * The value of @outlen after return is the number of octets consumed.
    206  */
    207 static int
    208 xmlEscapeEntities(unsigned char* out, int *outlen,
    209                  const xmlChar* in, int *inlen) {
    210     unsigned char* outstart = out;
    211     const unsigned char* base = in;
    212     unsigned char* outend = out + *outlen;
    213     const unsigned char* inend;
    214     int val;
    215 
    216     inend = in + (*inlen);
    217 
    218     while ((in < inend) && (out < outend)) {
    219 	if (*in == '<') {
    220 	    if (outend - out < 4) break;
    221 	    *out++ = '&';
    222 	    *out++ = 'l';
    223 	    *out++ = 't';
    224 	    *out++ = ';';
    225 	    in++;
    226 	    continue;
    227 	} else if (*in == '>') {
    228 	    if (outend - out < 4) break;
    229 	    *out++ = '&';
    230 	    *out++ = 'g';
    231 	    *out++ = 't';
    232 	    *out++ = ';';
    233 	    in++;
    234 	    continue;
    235 	} else if (*in == '&') {
    236 	    if (outend - out < 5) break;
    237 	    *out++ = '&';
    238 	    *out++ = 'a';
    239 	    *out++ = 'm';
    240 	    *out++ = 'p';
    241 	    *out++ = ';';
    242 	    in++;
    243 	    continue;
    244 	} else if (((*in >= 0x20) && (*in < 0x80)) ||
    245 	           (*in == '\n') || (*in == '\t')) {
    246 	    /*
    247 	     * default case, just copy !
    248 	     */
    249 	    *out++ = *in++;
    250 	    continue;
    251 	} else if (*in >= 0x80) {
    252 	    /*
    253 	     * We assume we have UTF-8 input.
    254 	     */
    255 	    if (outend - out < 11) break;
    256 
    257 	    if (*in < 0xC0) {
    258 		xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
    259 		in++;
    260 		goto error;
    261 	    } else if (*in < 0xE0) {
    262 		if (inend - in < 2) break;
    263 		val = (in[0]) & 0x1F;
    264 		val <<= 6;
    265 		val |= (in[1]) & 0x3F;
    266 		in += 2;
    267 	    } else if (*in < 0xF0) {
    268 		if (inend - in < 3) break;
    269 		val = (in[0]) & 0x0F;
    270 		val <<= 6;
    271 		val |= (in[1]) & 0x3F;
    272 		val <<= 6;
    273 		val |= (in[2]) & 0x3F;
    274 		in += 3;
    275 	    } else if (*in < 0xF8) {
    276 		if (inend - in < 4) break;
    277 		val = (in[0]) & 0x07;
    278 		val <<= 6;
    279 		val |= (in[1]) & 0x3F;
    280 		val <<= 6;
    281 		val |= (in[2]) & 0x3F;
    282 		val <<= 6;
    283 		val |= (in[3]) & 0x3F;
    284 		in += 4;
    285 	    } else {
    286 		xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
    287 		in++;
    288 		goto error;
    289 	    }
    290 	    if (!IS_CHAR(val)) {
    291 		xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
    292 		in++;
    293 		goto error;
    294 	    }
    295 
    296 	    /*
    297 	     * We could do multiple things here. Just save as a char ref
    298 	     */
    299 	    out = xmlSerializeHexCharRef(out, val);
    300 	} else if (IS_BYTE_CHAR(*in)) {
    301 	    if (outend - out < 6) break;
    302 	    out = xmlSerializeHexCharRef(out, *in++);
    303 	} else {
    304 	    xmlGenericError(xmlGenericErrorContext,
    305 		"xmlEscapeEntities : char out of range\n");
    306 	    in++;
    307 	    goto error;
    308 	}
    309     }
    310     *outlen = out - outstart;
    311     *inlen = in - base;
    312     return(0);
    313 error:
    314     *outlen = out - outstart;
    315     *inlen = in - base;
    316     return(-1);
    317 }
    318 
    319 /************************************************************************
    320  *									*
    321  *			Allocation and deallocation			*
    322  *									*
    323  ************************************************************************/
    324 /**
    325  * xmlSaveCtxtInit:
    326  * @ctxt: the saving context
    327  *
    328  * Initialize a saving context
    329  */
    330 static void
    331 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
    332 {
    333     int i;
    334     int len;
    335 
    336     if (ctxt == NULL) return;
    337     if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
    338         ctxt->escape = xmlEscapeEntities;
    339     len = xmlStrlen((xmlChar *)xmlTreeIndentString);
    340     if ((xmlTreeIndentString == NULL) || (len == 0)) {
    341         memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
    342     } else {
    343 	ctxt->indent_size = len;
    344 	ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
    345 	for (i = 0;i < ctxt->indent_nr;i++)
    346 	    memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
    347 		   ctxt->indent_size);
    348         ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
    349     }
    350 
    351     if (xmlSaveNoEmptyTags) {
    352 	ctxt->options |= XML_SAVE_NO_EMPTY;
    353     }
    354 }
    355 
    356 /**
    357  * xmlFreeSaveCtxt:
    358  *
    359  * Free a saving context, destroying the ouptut in any remaining buffer
    360  */
    361 static void
    362 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
    363 {
    364     if (ctxt == NULL) return;
    365     if (ctxt->encoding != NULL)
    366         xmlFree((char *) ctxt->encoding);
    367     if (ctxt->buf != NULL)
    368         xmlOutputBufferClose(ctxt->buf);
    369     xmlFree(ctxt);
    370 }
    371 
    372 /**
    373  * xmlNewSaveCtxt:
    374  *
    375  * Create a new saving context
    376  *
    377  * Returns the new structure or NULL in case of error
    378  */
    379 static xmlSaveCtxtPtr
    380 xmlNewSaveCtxt(const char *encoding, int options)
    381 {
    382     xmlSaveCtxtPtr ret;
    383 
    384     ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
    385     if (ret == NULL) {
    386 	xmlSaveErrMemory("creating saving context");
    387 	return ( NULL );
    388     }
    389     memset(ret, 0, sizeof(xmlSaveCtxt));
    390 
    391     if (encoding != NULL) {
    392         ret->handler = xmlFindCharEncodingHandler(encoding);
    393 	if (ret->handler == NULL) {
    394 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
    395             xmlFreeSaveCtxt(ret);
    396 	    return(NULL);
    397 	}
    398         ret->encoding = xmlStrdup((const xmlChar *)encoding);
    399 	ret->escape = NULL;
    400     }
    401     xmlSaveCtxtInit(ret);
    402 
    403     /*
    404      * Use the options
    405      */
    406 
    407     /* Re-check this option as it may already have been set */
    408     if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
    409 	options |= XML_SAVE_NO_EMPTY;
    410     }
    411 
    412     ret->options = options;
    413     if (options & XML_SAVE_FORMAT)
    414         ret->format = 1;
    415     else if (options & XML_SAVE_WSNONSIG)
    416         ret->format = 2;
    417 
    418     return(ret);
    419 }
    420 
    421 /************************************************************************
    422  *									*
    423  *		Dumping XML tree content to a simple buffer		*
    424  *									*
    425  ************************************************************************/
    426 /**
    427  * xmlAttrSerializeContent:
    428  * @buf:  the XML buffer output
    429  * @doc:  the document
    430  * @attr:  the attribute pointer
    431  *
    432  * Serialize the attribute in the buffer
    433  */
    434 static void
    435 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
    436 {
    437     xmlNodePtr children;
    438 
    439     children = attr->children;
    440     while (children != NULL) {
    441         switch (children->type) {
    442             case XML_TEXT_NODE:
    443 	        xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
    444 		                              attr, children->content);
    445 		break;
    446             case XML_ENTITY_REF_NODE:
    447                 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
    448                 xmlBufAdd(buf->buffer, children->name,
    449                              xmlStrlen(children->name));
    450                 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
    451                 break;
    452             default:
    453                 /* should not happen unless we have a badly built tree */
    454                 break;
    455         }
    456         children = children->next;
    457     }
    458 }
    459 
    460 /**
    461  * xmlBufDumpNotationTable:
    462  * @buf:  an xmlBufPtr output
    463  * @table:  A notation table
    464  *
    465  * This will dump the content of the notation table as an XML DTD definition
    466  */
    467 void
    468 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
    469     xmlBufferPtr buffer;
    470 
    471     buffer = xmlBufferCreate();
    472     if (buffer == NULL) {
    473         /*
    474          * TODO set the error in buf
    475          */
    476         return;
    477     }
    478     xmlDumpNotationTable(buffer, table);
    479     xmlBufMergeBuffer(buf, buffer);
    480 }
    481 
    482 /**
    483  * xmlBufDumpElementDecl:
    484  * @buf:  an xmlBufPtr output
    485  * @elem:  An element table
    486  *
    487  * This will dump the content of the element declaration as an XML
    488  * DTD definition
    489  */
    490 void
    491 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
    492     xmlBufferPtr buffer;
    493 
    494     buffer = xmlBufferCreate();
    495     if (buffer == NULL) {
    496         /*
    497          * TODO set the error in buf
    498          */
    499         return;
    500     }
    501     xmlDumpElementDecl(buffer, elem);
    502     xmlBufMergeBuffer(buf, buffer);
    503 }
    504 
    505 /**
    506  * xmlBufDumpAttributeDecl:
    507  * @buf:  an xmlBufPtr output
    508  * @attr:  An attribute declaration
    509  *
    510  * This will dump the content of the attribute declaration as an XML
    511  * DTD definition
    512  */
    513 void
    514 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
    515     xmlBufferPtr buffer;
    516 
    517     buffer = xmlBufferCreate();
    518     if (buffer == NULL) {
    519         /*
    520          * TODO set the error in buf
    521          */
    522         return;
    523     }
    524     xmlDumpAttributeDecl(buffer, attr);
    525     xmlBufMergeBuffer(buf, buffer);
    526 }
    527 
    528 /**
    529  * xmlBufDumpEntityDecl:
    530  * @buf:  an xmlBufPtr output
    531  * @ent:  An entity table
    532  *
    533  * This will dump the content of the entity table as an XML DTD definition
    534  */
    535 void
    536 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
    537     xmlBufferPtr buffer;
    538 
    539     buffer = xmlBufferCreate();
    540     if (buffer == NULL) {
    541         /*
    542          * TODO set the error in buf
    543          */
    544         return;
    545     }
    546     xmlDumpEntityDecl(buffer, ent);
    547     xmlBufMergeBuffer(buf, buffer);
    548 }
    549 
    550 /************************************************************************
    551  *									*
    552  *		Dumping XML tree content to an I/O output buffer	*
    553  *									*
    554  ************************************************************************/
    555 
    556 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
    557     xmlOutputBufferPtr buf = ctxt->buf;
    558 
    559     if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
    560 	buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
    561 	if (buf->encoder == NULL) {
    562 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
    563 		       (const char *)encoding);
    564 	    return(-1);
    565 	}
    566 	buf->conv = xmlBufCreate();
    567 	if (buf->conv == NULL) {
    568 	    xmlCharEncCloseFunc(buf->encoder);
    569 	    xmlSaveErrMemory("creating encoding buffer");
    570 	    return(-1);
    571 	}
    572 	/*
    573 	 * initialize the state, e.g. if outputting a BOM
    574 	 */
    575         xmlCharEncOutput(buf, 1);
    576     }
    577     return(0);
    578 }
    579 
    580 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
    581     xmlOutputBufferPtr buf = ctxt->buf;
    582     xmlOutputBufferFlush(buf);
    583     xmlCharEncCloseFunc(buf->encoder);
    584     xmlBufFree(buf->conv);
    585     buf->encoder = NULL;
    586     buf->conv = NULL;
    587     return(0);
    588 }
    589 
    590 #ifdef LIBXML_HTML_ENABLED
    591 static void
    592 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
    593 #endif
    594 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
    595 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
    596 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
    597 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
    598 
    599 /**
    600  * xmlOutputBufferWriteWSNonSig:
    601  * @ctxt:  The save context
    602  * @extra: Number of extra indents to apply to ctxt->level
    603  *
    604  * Write out formatting for non-significant whitespace output.
    605  */
    606 static void
    607 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
    608 {
    609     int i;
    610     if ((ctxt == NULL) || (ctxt->buf == NULL))
    611         return;
    612     xmlOutputBufferWrite(ctxt->buf, 1, "\n");
    613     for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
    614         xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
    615                 ((ctxt->level + extra - i) > ctxt->indent_nr ?
    616                  ctxt->indent_nr : (ctxt->level + extra - i)),
    617                 ctxt->indent);
    618     }
    619 }
    620 
    621 /**
    622  * xmlNsDumpOutput:
    623  * @buf:  the XML buffer output
    624  * @cur:  a namespace
    625  * @ctxt: the output save context. Optional.
    626  *
    627  * Dump a local Namespace definition.
    628  * Should be called in the context of attributes dumps.
    629  * If @ctxt is supplied, @buf should be its buffer.
    630  */
    631 static void
    632 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
    633     if ((cur == NULL) || (buf == NULL)) return;
    634     if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
    635 	if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
    636 	    return;
    637 
    638 	if (ctxt != NULL && ctxt->format == 2)
    639 	    xmlOutputBufferWriteWSNonSig(ctxt, 2);
    640 	else
    641 	    xmlOutputBufferWrite(buf, 1, " ");
    642 
    643         /* Within the context of an element attributes */
    644 	if (cur->prefix != NULL) {
    645 	    xmlOutputBufferWrite(buf, 6, "xmlns:");
    646 	    xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
    647 	} else
    648 	    xmlOutputBufferWrite(buf, 5, "xmlns");
    649 	xmlOutputBufferWrite(buf, 1, "=");
    650 	xmlBufWriteQuotedString(buf->buffer, cur->href);
    651     }
    652 }
    653 
    654 /**
    655  * xmlNsDumpOutputCtxt
    656  * @ctxt: the save context
    657  * @cur:  a namespace
    658  *
    659  * Dump a local Namespace definition to a save context.
    660  * Should be called in the context of attribute dumps.
    661  */
    662 static void
    663 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
    664     xmlNsDumpOutput(ctxt->buf, cur, ctxt);
    665 }
    666 
    667 /**
    668  * xmlNsListDumpOutputCtxt
    669  * @ctxt: the save context
    670  * @cur:  the first namespace
    671  *
    672  * Dump a list of local namespace definitions to a save context.
    673  * Should be called in the context of attribute dumps.
    674  */
    675 static void
    676 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
    677     while (cur != NULL) {
    678         xmlNsDumpOutput(ctxt->buf, cur, ctxt);
    679 	cur = cur->next;
    680     }
    681 }
    682 
    683 /**
    684  * xmlNsListDumpOutput:
    685  * @buf:  the XML buffer output
    686  * @cur:  the first namespace
    687  *
    688  * Dump a list of local Namespace definitions.
    689  * Should be called in the context of attributes dumps.
    690  */
    691 void
    692 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
    693     while (cur != NULL) {
    694         xmlNsDumpOutput(buf, cur, NULL);
    695 	cur = cur->next;
    696     }
    697 }
    698 
    699 /**
    700  * xmlDtdDumpOutput:
    701  * @buf:  the XML buffer output
    702  * @dtd:  the pointer to the DTD
    703  *
    704  * Dump the XML document DTD, if any.
    705  */
    706 static void
    707 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
    708     xmlOutputBufferPtr buf;
    709     int format, level;
    710     xmlDocPtr doc;
    711 
    712     if (dtd == NULL) return;
    713     if ((ctxt == NULL) || (ctxt->buf == NULL))
    714         return;
    715     buf = ctxt->buf;
    716     xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
    717     xmlOutputBufferWriteString(buf, (const char *)dtd->name);
    718     if (dtd->ExternalID != NULL) {
    719 	xmlOutputBufferWrite(buf, 8, " PUBLIC ");
    720 	xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
    721 	xmlOutputBufferWrite(buf, 1, " ");
    722 	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
    723     }  else if (dtd->SystemID != NULL) {
    724 	xmlOutputBufferWrite(buf, 8, " SYSTEM ");
    725 	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
    726     }
    727     if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
    728         (dtd->attributes == NULL) && (dtd->notations == NULL) &&
    729 	(dtd->pentities == NULL)) {
    730 	xmlOutputBufferWrite(buf, 1, ">");
    731 	return;
    732     }
    733     xmlOutputBufferWrite(buf, 3, " [\n");
    734     /*
    735      * Dump the notations first they are not in the DTD children list
    736      * Do this only on a standalone DTD or on the internal subset though.
    737      */
    738     if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
    739         (dtd->doc->intSubset == dtd))) {
    740         xmlBufDumpNotationTable(buf->buffer,
    741                                 (xmlNotationTablePtr) dtd->notations);
    742     }
    743     format = ctxt->format;
    744     level = ctxt->level;
    745     doc = ctxt->doc;
    746     ctxt->format = 0;
    747     ctxt->level = -1;
    748     ctxt->doc = dtd->doc;
    749     xmlNodeListDumpOutput(ctxt, dtd->children);
    750     ctxt->format = format;
    751     ctxt->level = level;
    752     ctxt->doc = doc;
    753     xmlOutputBufferWrite(buf, 2, "]>");
    754 }
    755 
    756 /**
    757  * xmlAttrDumpOutput:
    758  * @buf:  the XML buffer output
    759  * @cur:  the attribute pointer
    760  *
    761  * Dump an XML attribute
    762  */
    763 static void
    764 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
    765     xmlOutputBufferPtr buf;
    766 
    767     if (cur == NULL) return;
    768     buf = ctxt->buf;
    769     if (buf == NULL) return;
    770     if (ctxt->format == 2)
    771         xmlOutputBufferWriteWSNonSig(ctxt, 2);
    772     else
    773         xmlOutputBufferWrite(buf, 1, " ");
    774     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
    775         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
    776 	xmlOutputBufferWrite(buf, 1, ":");
    777     }
    778     xmlOutputBufferWriteString(buf, (const char *)cur->name);
    779     xmlOutputBufferWrite(buf, 2, "=\"");
    780     xmlAttrSerializeContent(buf, cur);
    781     xmlOutputBufferWrite(buf, 1, "\"");
    782 }
    783 
    784 /**
    785  * xmlAttrListDumpOutput:
    786  * @buf:  the XML buffer output
    787  * @doc:  the document
    788  * @cur:  the first attribute pointer
    789  * @encoding:  an optional encoding string
    790  *
    791  * Dump a list of XML attributes
    792  */
    793 static void
    794 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
    795     if (cur == NULL) return;
    796     while (cur != NULL) {
    797         xmlAttrDumpOutput(ctxt, cur);
    798 	cur = cur->next;
    799     }
    800 }
    801 
    802 
    803 
    804 /**
    805  * xmlNodeListDumpOutput:
    806  * @cur:  the first node
    807  *
    808  * Dump an XML node list, recursive behaviour, children are printed too.
    809  */
    810 static void
    811 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
    812     xmlOutputBufferPtr buf;
    813 
    814     if (cur == NULL) return;
    815     buf = ctxt->buf;
    816     while (cur != NULL) {
    817 	if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
    818 	    ((cur->type == XML_ELEMENT_NODE) ||
    819 	     (cur->type == XML_COMMENT_NODE) ||
    820 	     (cur->type == XML_PI_NODE)))
    821 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
    822 	                         (ctxt->level > ctxt->indent_nr ?
    823 				  ctxt->indent_nr : ctxt->level),
    824 				 ctxt->indent);
    825         xmlNodeDumpOutputInternal(ctxt, cur);
    826 	if (ctxt->format == 1) {
    827 	    xmlOutputBufferWrite(buf, 1, "\n");
    828 	}
    829 	cur = cur->next;
    830     }
    831 }
    832 
    833 #ifdef LIBXML_HTML_ENABLED
    834 /**
    835  * xmlNodeDumpOutputInternal:
    836  * @cur:  the current node
    837  *
    838  * Dump an HTML node, recursive behaviour, children are printed too.
    839  */
    840 static int
    841 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
    842     const xmlChar *oldenc = NULL;
    843     const xmlChar *oldctxtenc = ctxt->encoding;
    844     const xmlChar *encoding = ctxt->encoding;
    845     xmlOutputBufferPtr buf = ctxt->buf;
    846     int switched_encoding = 0;
    847     xmlDocPtr doc;
    848 
    849     xmlInitParser();
    850 
    851     doc = cur->doc;
    852     if (doc != NULL) {
    853         oldenc = doc->encoding;
    854 	if (ctxt->encoding != NULL) {
    855 	    doc->encoding = BAD_CAST ctxt->encoding;
    856 	} else if (doc->encoding != NULL) {
    857 	    encoding = doc->encoding;
    858 	}
    859     }
    860 
    861     if ((encoding != NULL) && (doc != NULL))
    862 	htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
    863     if ((encoding == NULL) && (doc != NULL))
    864 	encoding = htmlGetMetaEncoding(doc);
    865     if (encoding == NULL)
    866 	encoding = BAD_CAST "HTML";
    867     if ((encoding != NULL) && (oldctxtenc == NULL) &&
    868 	(buf->encoder == NULL) && (buf->conv == NULL)) {
    869 	if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
    870 	    doc->encoding = oldenc;
    871 	    return(-1);
    872 	}
    873 	switched_encoding = 1;
    874     }
    875     if (ctxt->options & XML_SAVE_FORMAT)
    876 	htmlNodeDumpFormatOutput(buf, doc, cur,
    877 				       (const char *)encoding, 1);
    878     else
    879 	htmlNodeDumpFormatOutput(buf, doc, cur,
    880 				       (const char *)encoding, 0);
    881     /*
    882      * Restore the state of the saving context at the end of the document
    883      */
    884     if ((switched_encoding) && (oldctxtenc == NULL)) {
    885 	xmlSaveClearEncoding(ctxt);
    886     }
    887     if (doc != NULL)
    888 	doc->encoding = oldenc;
    889     return(0);
    890 }
    891 #endif
    892 
    893 /**
    894  * xmlNodeDumpOutputInternal:
    895  * @cur:  the current node
    896  *
    897  * Dump an XML node, recursive behaviour, children are printed too.
    898  */
    899 static void
    900 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
    901     int format;
    902     xmlNodePtr tmp;
    903     xmlChar *start, *end;
    904     xmlOutputBufferPtr buf;
    905 
    906     if (cur == NULL) return;
    907     buf = ctxt->buf;
    908     if (cur->type == XML_XINCLUDE_START)
    909 	return;
    910     if (cur->type == XML_XINCLUDE_END)
    911 	return;
    912     if ((cur->type == XML_DOCUMENT_NODE) ||
    913         (cur->type == XML_HTML_DOCUMENT_NODE)) {
    914 	xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
    915 	return;
    916     }
    917 #ifdef LIBXML_HTML_ENABLED
    918     if (ctxt->options & XML_SAVE_XHTML) {
    919         xhtmlNodeDumpOutput(ctxt, cur);
    920         return;
    921     }
    922     if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
    923          (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
    924          ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
    925         (ctxt->options & XML_SAVE_AS_HTML)) {
    926 	htmlNodeDumpOutputInternal(ctxt, cur);
    927 	return;
    928     }
    929 #endif
    930     if (cur->type == XML_DTD_NODE) {
    931         xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
    932 	return;
    933     }
    934     if (cur->type == XML_DOCUMENT_FRAG_NODE) {
    935         xmlNodeListDumpOutput(ctxt, cur->children);
    936 	return;
    937     }
    938     if (cur->type == XML_ELEMENT_DECL) {
    939         xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
    940 	return;
    941     }
    942     if (cur->type == XML_ATTRIBUTE_DECL) {
    943         xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
    944 	return;
    945     }
    946     if (cur->type == XML_ENTITY_DECL) {
    947         xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
    948 	return;
    949     }
    950     if (cur->type == XML_TEXT_NODE) {
    951 	if (cur->content != NULL) {
    952 	    if (cur->name != xmlStringTextNoenc) {
    953                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
    954 	    } else {
    955 		/*
    956 		 * Disable escaping, needed for XSLT
    957 		 */
    958 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
    959 	    }
    960 	}
    961 
    962 	return;
    963     }
    964     if (cur->type == XML_PI_NODE) {
    965 	if (cur->content != NULL) {
    966 	    xmlOutputBufferWrite(buf, 2, "<?");
    967 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
    968 	    if (cur->content != NULL) {
    969 	        if (ctxt->format == 2)
    970 	            xmlOutputBufferWriteWSNonSig(ctxt, 0);
    971 	        else
    972 	            xmlOutputBufferWrite(buf, 1, " ");
    973 		xmlOutputBufferWriteString(buf, (const char *)cur->content);
    974 	    }
    975 	    xmlOutputBufferWrite(buf, 2, "?>");
    976 	} else {
    977 	    xmlOutputBufferWrite(buf, 2, "<?");
    978 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
    979 	    if (ctxt->format == 2)
    980 	        xmlOutputBufferWriteWSNonSig(ctxt, 0);
    981 	    xmlOutputBufferWrite(buf, 2, "?>");
    982 	}
    983 	return;
    984     }
    985     if (cur->type == XML_COMMENT_NODE) {
    986 	if (cur->content != NULL) {
    987 	    xmlOutputBufferWrite(buf, 4, "<!--");
    988 	    xmlOutputBufferWriteString(buf, (const char *)cur->content);
    989 	    xmlOutputBufferWrite(buf, 3, "-->");
    990 	}
    991 	return;
    992     }
    993     if (cur->type == XML_ENTITY_REF_NODE) {
    994         xmlOutputBufferWrite(buf, 1, "&");
    995 	xmlOutputBufferWriteString(buf, (const char *)cur->name);
    996         xmlOutputBufferWrite(buf, 1, ";");
    997 	return;
    998     }
    999     if (cur->type == XML_CDATA_SECTION_NODE) {
   1000 	if (cur->content == NULL || *cur->content == '\0') {
   1001 	    xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
   1002 	} else {
   1003 	    start = end = cur->content;
   1004 	    while (*end != '\0') {
   1005 		if ((*end == ']') && (*(end + 1) == ']') &&
   1006 		    (*(end + 2) == '>')) {
   1007 		    end = end + 2;
   1008 		    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1009 		    xmlOutputBufferWrite(buf, end - start, (const char *)start);
   1010 		    xmlOutputBufferWrite(buf, 3, "]]>");
   1011 		    start = end;
   1012 		}
   1013 		end++;
   1014 	    }
   1015 	    if (start != end) {
   1016 		xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1017 		xmlOutputBufferWriteString(buf, (const char *)start);
   1018 		xmlOutputBufferWrite(buf, 3, "]]>");
   1019 	    }
   1020 	}
   1021 	return;
   1022     }
   1023     if (cur->type == XML_ATTRIBUTE_NODE) {
   1024 	xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
   1025 	return;
   1026     }
   1027     if (cur->type == XML_NAMESPACE_DECL) {
   1028 	xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
   1029 	return;
   1030     }
   1031 
   1032     format = ctxt->format;
   1033     if (format == 1) {
   1034 	tmp = cur->children;
   1035 	while (tmp != NULL) {
   1036 	    if ((tmp->type == XML_TEXT_NODE) ||
   1037 		(tmp->type == XML_CDATA_SECTION_NODE) ||
   1038 		(tmp->type == XML_ENTITY_REF_NODE)) {
   1039 		ctxt->format = 0;
   1040 		break;
   1041 	    }
   1042 	    tmp = tmp->next;
   1043 	}
   1044     }
   1045     xmlOutputBufferWrite(buf, 1, "<");
   1046     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1047         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1048 	xmlOutputBufferWrite(buf, 1, ":");
   1049     }
   1050 
   1051     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1052     if (cur->nsDef)
   1053         xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
   1054     if (cur->properties != NULL)
   1055         xmlAttrListDumpOutput(ctxt, cur->properties);
   1056 
   1057     if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
   1058 	(cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
   1059         if (ctxt->format == 2)
   1060             xmlOutputBufferWriteWSNonSig(ctxt, 0);
   1061         xmlOutputBufferWrite(buf, 2, "/>");
   1062 	ctxt->format = format;
   1063 	return;
   1064     }
   1065     if (ctxt->format == 2)
   1066         xmlOutputBufferWriteWSNonSig(ctxt, 1);
   1067     xmlOutputBufferWrite(buf, 1, ">");
   1068     if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
   1069 	xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
   1070     }
   1071     if (cur->children != NULL) {
   1072 	if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
   1073 	if (ctxt->level >= 0) ctxt->level++;
   1074 	xmlNodeListDumpOutput(ctxt, cur->children);
   1075 	if (ctxt->level > 0) ctxt->level--;
   1076 	if ((xmlIndentTreeOutput) && (ctxt->format == 1))
   1077 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
   1078 	                         (ctxt->level > ctxt->indent_nr ?
   1079 				  ctxt->indent_nr : ctxt->level),
   1080 				 ctxt->indent);
   1081     }
   1082     xmlOutputBufferWrite(buf, 2, "</");
   1083     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1084         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1085 	xmlOutputBufferWrite(buf, 1, ":");
   1086     }
   1087 
   1088     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1089     if (ctxt->format == 2)
   1090         xmlOutputBufferWriteWSNonSig(ctxt, 0);
   1091     xmlOutputBufferWrite(buf, 1, ">");
   1092     ctxt->format = format;
   1093 }
   1094 
   1095 /**
   1096  * xmlDocContentDumpOutput:
   1097  * @cur:  the document
   1098  *
   1099  * Dump an XML document.
   1100  */
   1101 static int
   1102 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
   1103 #ifdef LIBXML_HTML_ENABLED
   1104     xmlDtdPtr dtd;
   1105     int is_xhtml = 0;
   1106 #endif
   1107     const xmlChar *oldenc = cur->encoding;
   1108     const xmlChar *oldctxtenc = ctxt->encoding;
   1109     const xmlChar *encoding = ctxt->encoding;
   1110     xmlCharEncodingOutputFunc oldescape = ctxt->escape;
   1111     xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
   1112     xmlOutputBufferPtr buf = ctxt->buf;
   1113     xmlCharEncoding enc;
   1114     int switched_encoding = 0;
   1115 
   1116     xmlInitParser();
   1117 
   1118     if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
   1119         (cur->type != XML_DOCUMENT_NODE))
   1120 	 return(-1);
   1121 
   1122     if (ctxt->encoding != NULL) {
   1123         cur->encoding = BAD_CAST ctxt->encoding;
   1124     } else if (cur->encoding != NULL) {
   1125 	encoding = cur->encoding;
   1126     } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
   1127 	encoding = (const xmlChar *)
   1128 		     xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
   1129     }
   1130 
   1131     if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
   1132          ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
   1133          ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
   1134         (ctxt->options & XML_SAVE_AS_HTML)) {
   1135 #ifdef LIBXML_HTML_ENABLED
   1136         if (encoding != NULL)
   1137 	    htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
   1138         if (encoding == NULL)
   1139 	    encoding = htmlGetMetaEncoding(cur);
   1140         if (encoding == NULL)
   1141 	    encoding = BAD_CAST "HTML";
   1142 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
   1143 	    (buf->encoder == NULL) && (buf->conv == NULL)) {
   1144 	    if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
   1145 		cur->encoding = oldenc;
   1146 		return(-1);
   1147 	    }
   1148 	}
   1149         if (ctxt->options & XML_SAVE_FORMAT)
   1150 	    htmlDocContentDumpFormatOutput(buf, cur,
   1151 	                                   (const char *)encoding, 1);
   1152 	else
   1153 	    htmlDocContentDumpFormatOutput(buf, cur,
   1154 	                                   (const char *)encoding, 0);
   1155 	if (ctxt->encoding != NULL)
   1156 	    cur->encoding = oldenc;
   1157 	return(0);
   1158 #else
   1159         return(-1);
   1160 #endif
   1161     } else if ((cur->type == XML_DOCUMENT_NODE) ||
   1162                (ctxt->options & XML_SAVE_AS_XML) ||
   1163                (ctxt->options & XML_SAVE_XHTML)) {
   1164 	enc = xmlParseCharEncoding((const char*) encoding);
   1165 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
   1166 	    (buf->encoder == NULL) && (buf->conv == NULL) &&
   1167 	    ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
   1168 	    if ((enc != XML_CHAR_ENCODING_UTF8) &&
   1169 		(enc != XML_CHAR_ENCODING_NONE) &&
   1170 		(enc != XML_CHAR_ENCODING_ASCII)) {
   1171 		/*
   1172 		 * we need to switch to this encoding but just for this
   1173 		 * document since we output the XMLDecl the conversion
   1174 		 * must be done to not generate not well formed documents.
   1175 		 */
   1176 		if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
   1177 		    cur->encoding = oldenc;
   1178 		    return(-1);
   1179 		}
   1180 		switched_encoding = 1;
   1181 	    }
   1182 	    if (ctxt->escape == xmlEscapeEntities)
   1183 		ctxt->escape = NULL;
   1184 	    if (ctxt->escapeAttr == xmlEscapeEntities)
   1185 		ctxt->escapeAttr = NULL;
   1186 	}
   1187 
   1188 
   1189 	/*
   1190 	 * Save the XML declaration
   1191 	 */
   1192 	if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
   1193 	    xmlOutputBufferWrite(buf, 14, "<?xml version=");
   1194 	    if (cur->version != NULL)
   1195 		xmlBufWriteQuotedString(buf->buffer, cur->version);
   1196 	    else
   1197 		xmlOutputBufferWrite(buf, 5, "\"1.0\"");
   1198 	    if (encoding != NULL) {
   1199 		xmlOutputBufferWrite(buf, 10, " encoding=");
   1200 		xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
   1201 	    }
   1202 	    switch (cur->standalone) {
   1203 		case 0:
   1204 		    xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
   1205 		    break;
   1206 		case 1:
   1207 		    xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
   1208 		    break;
   1209 	    }
   1210 	    xmlOutputBufferWrite(buf, 3, "?>\n");
   1211 	}
   1212 
   1213 #ifdef LIBXML_HTML_ENABLED
   1214         if (ctxt->options & XML_SAVE_XHTML)
   1215             is_xhtml = 1;
   1216 	if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
   1217 	    dtd = xmlGetIntSubset(cur);
   1218 	    if (dtd != NULL) {
   1219 		is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
   1220 		if (is_xhtml < 0) is_xhtml = 0;
   1221 	    }
   1222 	}
   1223 #endif
   1224 	if (cur->children != NULL) {
   1225 	    xmlNodePtr child = cur->children;
   1226 
   1227 	    while (child != NULL) {
   1228 		ctxt->level = 0;
   1229 #ifdef LIBXML_HTML_ENABLED
   1230 		if (is_xhtml)
   1231 		    xhtmlNodeDumpOutput(ctxt, child);
   1232 		else
   1233 #endif
   1234 		    xmlNodeDumpOutputInternal(ctxt, child);
   1235 		xmlOutputBufferWrite(buf, 1, "\n");
   1236 		child = child->next;
   1237 	    }
   1238 	}
   1239     }
   1240 
   1241     /*
   1242      * Restore the state of the saving context at the end of the document
   1243      */
   1244     if ((switched_encoding) && (oldctxtenc == NULL)) {
   1245 	xmlSaveClearEncoding(ctxt);
   1246 	ctxt->escape = oldescape;
   1247 	ctxt->escapeAttr = oldescapeAttr;
   1248     }
   1249     cur->encoding = oldenc;
   1250     return(0);
   1251 }
   1252 
   1253 #ifdef LIBXML_HTML_ENABLED
   1254 /************************************************************************
   1255  *									*
   1256  *		Functions specific to XHTML serialization		*
   1257  *									*
   1258  ************************************************************************/
   1259 
   1260 /**
   1261  * xhtmlIsEmpty:
   1262  * @node:  the node
   1263  *
   1264  * Check if a node is an empty xhtml node
   1265  *
   1266  * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
   1267  */
   1268 static int
   1269 xhtmlIsEmpty(xmlNodePtr node) {
   1270     if (node == NULL)
   1271 	return(-1);
   1272     if (node->type != XML_ELEMENT_NODE)
   1273 	return(0);
   1274     if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
   1275 	return(0);
   1276     if (node->children != NULL)
   1277 	return(0);
   1278     switch (node->name[0]) {
   1279 	case 'a':
   1280 	    if (xmlStrEqual(node->name, BAD_CAST "area"))
   1281 		return(1);
   1282 	    return(0);
   1283 	case 'b':
   1284 	    if (xmlStrEqual(node->name, BAD_CAST "br"))
   1285 		return(1);
   1286 	    if (xmlStrEqual(node->name, BAD_CAST "base"))
   1287 		return(1);
   1288 	    if (xmlStrEqual(node->name, BAD_CAST "basefont"))
   1289 		return(1);
   1290 	    return(0);
   1291 	case 'c':
   1292 	    if (xmlStrEqual(node->name, BAD_CAST "col"))
   1293 		return(1);
   1294 	    return(0);
   1295 	case 'f':
   1296 	    if (xmlStrEqual(node->name, BAD_CAST "frame"))
   1297 		return(1);
   1298 	    return(0);
   1299 	case 'h':
   1300 	    if (xmlStrEqual(node->name, BAD_CAST "hr"))
   1301 		return(1);
   1302 	    return(0);
   1303 	case 'i':
   1304 	    if (xmlStrEqual(node->name, BAD_CAST "img"))
   1305 		return(1);
   1306 	    if (xmlStrEqual(node->name, BAD_CAST "input"))
   1307 		return(1);
   1308 	    if (xmlStrEqual(node->name, BAD_CAST "isindex"))
   1309 		return(1);
   1310 	    return(0);
   1311 	case 'l':
   1312 	    if (xmlStrEqual(node->name, BAD_CAST "link"))
   1313 		return(1);
   1314 	    return(0);
   1315 	case 'm':
   1316 	    if (xmlStrEqual(node->name, BAD_CAST "meta"))
   1317 		return(1);
   1318 	    return(0);
   1319 	case 'p':
   1320 	    if (xmlStrEqual(node->name, BAD_CAST "param"))
   1321 		return(1);
   1322 	    return(0);
   1323     }
   1324     return(0);
   1325 }
   1326 
   1327 /**
   1328  * xhtmlAttrListDumpOutput:
   1329  * @cur:  the first attribute pointer
   1330  *
   1331  * Dump a list of XML attributes
   1332  */
   1333 static void
   1334 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
   1335     xmlAttrPtr xml_lang = NULL;
   1336     xmlAttrPtr lang = NULL;
   1337     xmlAttrPtr name = NULL;
   1338     xmlAttrPtr id = NULL;
   1339     xmlNodePtr parent;
   1340     xmlOutputBufferPtr buf;
   1341 
   1342     if (cur == NULL) return;
   1343     buf = ctxt->buf;
   1344     parent = cur->parent;
   1345     while (cur != NULL) {
   1346 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
   1347 	    id = cur;
   1348 	else
   1349 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
   1350 	    name = cur;
   1351 	else
   1352 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
   1353 	    lang = cur;
   1354 	else
   1355 	if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
   1356 	    (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
   1357 	    xml_lang = cur;
   1358 	else if ((cur->ns == NULL) &&
   1359 		 ((cur->children == NULL) ||
   1360 		  (cur->children->content == NULL) ||
   1361 		  (cur->children->content[0] == 0)) &&
   1362 		 (htmlIsBooleanAttr(cur->name))) {
   1363 	    if (cur->children != NULL)
   1364 		xmlFreeNode(cur->children);
   1365 	    cur->children = xmlNewText(cur->name);
   1366 	    if (cur->children != NULL)
   1367 		cur->children->parent = (xmlNodePtr) cur;
   1368 	}
   1369         xmlAttrDumpOutput(ctxt, cur);
   1370 	cur = cur->next;
   1371     }
   1372     /*
   1373      * C.8
   1374      */
   1375     if ((name != NULL) && (id == NULL)) {
   1376 	if ((parent != NULL) && (parent->name != NULL) &&
   1377 	    ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
   1378 	     (xmlStrEqual(parent->name, BAD_CAST "p")) ||
   1379 	     (xmlStrEqual(parent->name, BAD_CAST "div")) ||
   1380 	     (xmlStrEqual(parent->name, BAD_CAST "img")) ||
   1381 	     (xmlStrEqual(parent->name, BAD_CAST "map")) ||
   1382 	     (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
   1383 	     (xmlStrEqual(parent->name, BAD_CAST "form")) ||
   1384 	     (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
   1385 	     (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
   1386 	    xmlOutputBufferWrite(buf, 5, " id=\"");
   1387 	    xmlAttrSerializeContent(buf, name);
   1388 	    xmlOutputBufferWrite(buf, 1, "\"");
   1389 	}
   1390     }
   1391     /*
   1392      * C.7.
   1393      */
   1394     if ((lang != NULL) && (xml_lang == NULL)) {
   1395 	xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
   1396 	xmlAttrSerializeContent(buf, lang);
   1397 	xmlOutputBufferWrite(buf, 1, "\"");
   1398     } else
   1399     if ((xml_lang != NULL) && (lang == NULL)) {
   1400 	xmlOutputBufferWrite(buf, 7, " lang=\"");
   1401 	xmlAttrSerializeContent(buf, xml_lang);
   1402 	xmlOutputBufferWrite(buf, 1, "\"");
   1403     }
   1404 }
   1405 
   1406 /**
   1407  * xhtmlNodeListDumpOutput:
   1408  * @buf:  the XML buffer output
   1409  * @doc:  the XHTML document
   1410  * @cur:  the first node
   1411  * @level: the imbrication level for indenting
   1412  * @format: is formatting allowed
   1413  * @encoding:  an optional encoding string
   1414  *
   1415  * Dump an XML node list, recursive behaviour, children are printed too.
   1416  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   1417  * or xmlKeepBlanksDefault(0) was called
   1418  */
   1419 static void
   1420 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
   1421     xmlOutputBufferPtr buf;
   1422 
   1423     if (cur == NULL) return;
   1424     buf = ctxt->buf;
   1425     while (cur != NULL) {
   1426 	if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
   1427 	    (cur->type == XML_ELEMENT_NODE))
   1428 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
   1429 	                         (ctxt->level > ctxt->indent_nr ?
   1430 				  ctxt->indent_nr : ctxt->level),
   1431 				 ctxt->indent);
   1432         xhtmlNodeDumpOutput(ctxt, cur);
   1433 	if (ctxt->format == 1) {
   1434 	    xmlOutputBufferWrite(buf, 1, "\n");
   1435 	}
   1436 	cur = cur->next;
   1437     }
   1438 }
   1439 
   1440 /**
   1441  * xhtmlNodeDumpOutput:
   1442  * @buf:  the XML buffer output
   1443  * @doc:  the XHTML document
   1444  * @cur:  the current node
   1445  * @level: the imbrication level for indenting
   1446  * @format: is formatting allowed
   1447  * @encoding:  an optional encoding string
   1448  *
   1449  * Dump an XHTML node, recursive behaviour, children are printed too.
   1450  */
   1451 static void
   1452 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
   1453     int format, addmeta = 0;
   1454     xmlNodePtr tmp;
   1455     xmlChar *start, *end;
   1456     xmlOutputBufferPtr buf;
   1457 
   1458     if (cur == NULL) return;
   1459     if ((cur->type == XML_DOCUMENT_NODE) ||
   1460         (cur->type == XML_HTML_DOCUMENT_NODE)) {
   1461         xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
   1462 	return;
   1463     }
   1464     if (cur->type == XML_XINCLUDE_START)
   1465 	return;
   1466     if (cur->type == XML_XINCLUDE_END)
   1467 	return;
   1468     if (cur->type == XML_NAMESPACE_DECL) {
   1469 	xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
   1470 	return;
   1471     }
   1472     if (cur->type == XML_DTD_NODE) {
   1473         xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
   1474 	return;
   1475     }
   1476     if (cur->type == XML_DOCUMENT_FRAG_NODE) {
   1477         xhtmlNodeListDumpOutput(ctxt, cur->children);
   1478 	return;
   1479     }
   1480     buf = ctxt->buf;
   1481     if (cur->type == XML_ELEMENT_DECL) {
   1482         xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
   1483 	return;
   1484     }
   1485     if (cur->type == XML_ATTRIBUTE_DECL) {
   1486         xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
   1487 	return;
   1488     }
   1489     if (cur->type == XML_ENTITY_DECL) {
   1490         xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
   1491 	return;
   1492     }
   1493     if (cur->type == XML_TEXT_NODE) {
   1494 	if (cur->content != NULL) {
   1495 	    if ((cur->name == xmlStringText) ||
   1496 		(cur->name != xmlStringTextNoenc)) {
   1497                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
   1498 	    } else {
   1499 		/*
   1500 		 * Disable escaping, needed for XSLT
   1501 		 */
   1502 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
   1503 	    }
   1504 	}
   1505 
   1506 	return;
   1507     }
   1508     if (cur->type == XML_PI_NODE) {
   1509 	if (cur->content != NULL) {
   1510 	    xmlOutputBufferWrite(buf, 2, "<?");
   1511 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1512 	    if (cur->content != NULL) {
   1513 		xmlOutputBufferWrite(buf, 1, " ");
   1514 		xmlOutputBufferWriteString(buf, (const char *)cur->content);
   1515 	    }
   1516 	    xmlOutputBufferWrite(buf, 2, "?>");
   1517 	} else {
   1518 	    xmlOutputBufferWrite(buf, 2, "<?");
   1519 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1520 	    xmlOutputBufferWrite(buf, 2, "?>");
   1521 	}
   1522 	return;
   1523     }
   1524     if (cur->type == XML_COMMENT_NODE) {
   1525 	if (cur->content != NULL) {
   1526 	    xmlOutputBufferWrite(buf, 4, "<!--");
   1527 	    xmlOutputBufferWriteString(buf, (const char *)cur->content);
   1528 	    xmlOutputBufferWrite(buf, 3, "-->");
   1529 	}
   1530 	return;
   1531     }
   1532     if (cur->type == XML_ENTITY_REF_NODE) {
   1533         xmlOutputBufferWrite(buf, 1, "&");
   1534 	xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1535         xmlOutputBufferWrite(buf, 1, ";");
   1536 	return;
   1537     }
   1538     if (cur->type == XML_CDATA_SECTION_NODE) {
   1539 	if (cur->content == NULL || *cur->content == '\0') {
   1540 	    xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
   1541 	} else {
   1542 	    start = end = cur->content;
   1543 	    while (*end != '\0') {
   1544 		if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
   1545 		    end = end + 2;
   1546 		    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1547 		    xmlOutputBufferWrite(buf, end - start, (const char *)start);
   1548 		    xmlOutputBufferWrite(buf, 3, "]]>");
   1549 		    start = end;
   1550 		}
   1551 		end++;
   1552 	    }
   1553 	    if (start != end) {
   1554 		xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1555 		xmlOutputBufferWriteString(buf, (const char *)start);
   1556 		xmlOutputBufferWrite(buf, 3, "]]>");
   1557 	    }
   1558 	}
   1559 	return;
   1560     }
   1561     if (cur->type == XML_ATTRIBUTE_NODE) {
   1562         xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
   1563 	return;
   1564     }
   1565 
   1566     format = ctxt->format;
   1567     if (format == 1) {
   1568 	tmp = cur->children;
   1569 	while (tmp != NULL) {
   1570 	    if ((tmp->type == XML_TEXT_NODE) ||
   1571 		(tmp->type == XML_ENTITY_REF_NODE)) {
   1572 		format = 0;
   1573 		break;
   1574 	    }
   1575 	    tmp = tmp->next;
   1576 	}
   1577     }
   1578     xmlOutputBufferWrite(buf, 1, "<");
   1579     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1580         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1581 	xmlOutputBufferWrite(buf, 1, ":");
   1582     }
   1583 
   1584     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1585     if (cur->nsDef)
   1586         xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
   1587     if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
   1588 	(cur->ns == NULL) && (cur->nsDef == NULL))) {
   1589 	/*
   1590 	 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
   1591 	 */
   1592 	xmlOutputBufferWriteString(buf,
   1593 		" xmlns=\"http://www.w3.org/1999/xhtml\"");
   1594     }
   1595     if (cur->properties != NULL)
   1596         xhtmlAttrListDumpOutput(ctxt, cur->properties);
   1597 
   1598 	if ((cur->type == XML_ELEMENT_NODE) &&
   1599 		(cur->parent != NULL) &&
   1600 		(cur->parent->parent == (xmlNodePtr) cur->doc) &&
   1601 		xmlStrEqual(cur->name, BAD_CAST"head") &&
   1602 		xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
   1603 
   1604 		tmp = cur->children;
   1605 		while (tmp != NULL) {
   1606 			if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
   1607 				xmlChar *httpequiv;
   1608 
   1609 				httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
   1610 				if (httpequiv != NULL) {
   1611 					if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
   1612 						xmlFree(httpequiv);
   1613 						break;
   1614 					}
   1615 					xmlFree(httpequiv);
   1616 				}
   1617 			}
   1618 			tmp = tmp->next;
   1619 		}
   1620 		if (tmp == NULL)
   1621 			addmeta = 1;
   1622 	}
   1623 
   1624     if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
   1625 	if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
   1626 	    ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
   1627 	    /*
   1628 	     * C.2. Empty Elements
   1629 	     */
   1630 	    xmlOutputBufferWrite(buf, 3, " />");
   1631 	} else {
   1632 		if (addmeta == 1) {
   1633 			xmlOutputBufferWrite(buf, 1, ">");
   1634 			if (ctxt->format == 1) {
   1635 				xmlOutputBufferWrite(buf, 1, "\n");
   1636 				if (xmlIndentTreeOutput)
   1637 					xmlOutputBufferWrite(buf, ctxt->indent_size *
   1638 					(ctxt->level + 1 > ctxt->indent_nr ?
   1639 					ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
   1640 			}
   1641 			xmlOutputBufferWriteString(buf,
   1642 				"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
   1643 			if (ctxt->encoding) {
   1644 				xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
   1645 			} else {
   1646 				xmlOutputBufferWrite(buf, 5, "UTF-8");
   1647 			}
   1648 			xmlOutputBufferWrite(buf, 4, "\" />");
   1649 			if (ctxt->format == 1)
   1650 				xmlOutputBufferWrite(buf, 1, "\n");
   1651 		} else {
   1652 			xmlOutputBufferWrite(buf, 1, ">");
   1653 		}
   1654 	    /*
   1655 	     * C.3. Element Minimization and Empty Element Content
   1656 	     */
   1657 	    xmlOutputBufferWrite(buf, 2, "</");
   1658 	    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1659 		xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1660 		xmlOutputBufferWrite(buf, 1, ":");
   1661 	    }
   1662 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1663 	    xmlOutputBufferWrite(buf, 1, ">");
   1664 	}
   1665 	return;
   1666     }
   1667     xmlOutputBufferWrite(buf, 1, ">");
   1668 	if (addmeta == 1) {
   1669 		if (ctxt->format == 1) {
   1670 			xmlOutputBufferWrite(buf, 1, "\n");
   1671 			if (xmlIndentTreeOutput)
   1672 				xmlOutputBufferWrite(buf, ctxt->indent_size *
   1673 				(ctxt->level + 1 > ctxt->indent_nr ?
   1674 				ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
   1675 		}
   1676 		xmlOutputBufferWriteString(buf,
   1677 			"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
   1678 		if (ctxt->encoding) {
   1679 			xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
   1680 		} else {
   1681 			xmlOutputBufferWrite(buf, 5, "UTF-8");
   1682 		}
   1683 		xmlOutputBufferWrite(buf, 4, "\" />");
   1684 	}
   1685     if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
   1686 	xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
   1687     }
   1688 
   1689 #if 0
   1690     /*
   1691     * This was removed due to problems with HTML processors.
   1692     * See bug #345147.
   1693     */
   1694     /*
   1695      * 4.8. Script and Style elements
   1696      */
   1697     if ((cur->type == XML_ELEMENT_NODE) &&
   1698 	((xmlStrEqual(cur->name, BAD_CAST "script")) ||
   1699 	 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
   1700 	((cur->ns == NULL) ||
   1701 	 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
   1702 	xmlNodePtr child = cur->children;
   1703 
   1704 	while (child != NULL) {
   1705 	    if (child->type == XML_TEXT_NODE) {
   1706 		if ((xmlStrchr(child->content, '<') == NULL) &&
   1707 		    (xmlStrchr(child->content, '&') == NULL) &&
   1708 		    (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
   1709 		    /* Nothing to escape, so just output as is... */
   1710 		    /* FIXME: Should we do something about "--" also? */
   1711 		    int level = ctxt->level;
   1712 		    int indent = ctxt->format;
   1713 
   1714 		    ctxt->level = 0;
   1715 		    ctxt->format = 0;
   1716 		    xmlOutputBufferWriteString(buf, (const char *) child->content);
   1717 		    /* (We cannot use xhtmlNodeDumpOutput() here because
   1718 		     * we wish to leave '>' unescaped!) */
   1719 		    ctxt->level = level;
   1720 		    ctxt->format = indent;
   1721 		} else {
   1722 		    /* We must use a CDATA section.  Unfortunately,
   1723 		     * this will break CSS and JavaScript when read by
   1724 		     * a browser in HTML4-compliant mode. :-( */
   1725 		    start = end = child->content;
   1726 		    while (*end != '\0') {
   1727 			if (*end == ']' &&
   1728 			    *(end + 1) == ']' &&
   1729 			    *(end + 2) == '>') {
   1730 			    end = end + 2;
   1731 			    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1732 			    xmlOutputBufferWrite(buf, end - start,
   1733 						 (const char *)start);
   1734 			    xmlOutputBufferWrite(buf, 3, "]]>");
   1735 			    start = end;
   1736 			}
   1737 			end++;
   1738 		    }
   1739 		    if (start != end) {
   1740 			xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1741 			xmlOutputBufferWrite(buf, end - start,
   1742 			                     (const char *)start);
   1743 			xmlOutputBufferWrite(buf, 3, "]]>");
   1744 		    }
   1745 		}
   1746 	    } else {
   1747 		int level = ctxt->level;
   1748 		int indent = ctxt->format;
   1749 
   1750 		ctxt->level = 0;
   1751 		ctxt->format = 0;
   1752 		xhtmlNodeDumpOutput(ctxt, child);
   1753 		ctxt->level = level;
   1754 		ctxt->format = indent;
   1755 	    }
   1756 	    child = child->next;
   1757 	}
   1758     }
   1759 #endif
   1760 
   1761     if (cur->children != NULL) {
   1762 	int indent = ctxt->format;
   1763 
   1764 	if (format == 1) xmlOutputBufferWrite(buf, 1, "\n");
   1765 	if (ctxt->level >= 0) ctxt->level++;
   1766 	ctxt->format = format;
   1767 	xhtmlNodeListDumpOutput(ctxt, cur->children);
   1768 	if (ctxt->level > 0) ctxt->level--;
   1769 	ctxt->format = indent;
   1770 	if ((xmlIndentTreeOutput) && (format == 1))
   1771 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
   1772 	                         (ctxt->level > ctxt->indent_nr ?
   1773 				  ctxt->indent_nr : ctxt->level),
   1774 				 ctxt->indent);
   1775     }
   1776     xmlOutputBufferWrite(buf, 2, "</");
   1777     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1778         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1779 	xmlOutputBufferWrite(buf, 1, ":");
   1780     }
   1781 
   1782     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1783     xmlOutputBufferWrite(buf, 1, ">");
   1784 }
   1785 #endif
   1786 
   1787 /************************************************************************
   1788  *									*
   1789  *			Public entry points				*
   1790  *									*
   1791  ************************************************************************/
   1792 
   1793 /**
   1794  * xmlSaveToFd:
   1795  * @fd:  a file descriptor number
   1796  * @encoding:  the encoding name to use or NULL
   1797  * @options:  a set of xmlSaveOptions
   1798  *
   1799  * Create a document saving context serializing to a file descriptor
   1800  * with the encoding and the options given.
   1801  *
   1802  * Returns a new serialization context or NULL in case of error.
   1803  */
   1804 xmlSaveCtxtPtr
   1805 xmlSaveToFd(int fd, const char *encoding, int options)
   1806 {
   1807     xmlSaveCtxtPtr ret;
   1808 
   1809     ret = xmlNewSaveCtxt(encoding, options);
   1810     if (ret == NULL) return(NULL);
   1811     ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
   1812     if (ret->buf == NULL) {
   1813 	xmlFreeSaveCtxt(ret);
   1814 	return(NULL);
   1815     }
   1816     return(ret);
   1817 }
   1818 
   1819 /**
   1820  * xmlSaveToFilename:
   1821  * @filename:  a file name or an URL
   1822  * @encoding:  the encoding name to use or NULL
   1823  * @options:  a set of xmlSaveOptions
   1824  *
   1825  * Create a document saving context serializing to a filename or possibly
   1826  * to an URL (but this is less reliable) with the encoding and the options
   1827  * given.
   1828  *
   1829  * Returns a new serialization context or NULL in case of error.
   1830  */
   1831 xmlSaveCtxtPtr
   1832 xmlSaveToFilename(const char *filename, const char *encoding, int options)
   1833 {
   1834     xmlSaveCtxtPtr ret;
   1835     int compression = 0; /* TODO handle compression option */
   1836 
   1837     ret = xmlNewSaveCtxt(encoding, options);
   1838     if (ret == NULL) return(NULL);
   1839     ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
   1840                                              compression);
   1841     if (ret->buf == NULL) {
   1842 	xmlFreeSaveCtxt(ret);
   1843 	return(NULL);
   1844     }
   1845     return(ret);
   1846 }
   1847 
   1848 /**
   1849  * xmlSaveToBuffer:
   1850  * @buffer:  a buffer
   1851  * @encoding:  the encoding name to use or NULL
   1852  * @options:  a set of xmlSaveOptions
   1853  *
   1854  * Create a document saving context serializing to a buffer
   1855  * with the encoding and the options given
   1856  *
   1857  * Returns a new serialization context or NULL in case of error.
   1858  */
   1859 
   1860 xmlSaveCtxtPtr
   1861 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
   1862 {
   1863     xmlSaveCtxtPtr ret;
   1864     xmlOutputBufferPtr out_buff;
   1865     xmlCharEncodingHandlerPtr handler;
   1866 
   1867     ret = xmlNewSaveCtxt(encoding, options);
   1868     if (ret == NULL) return(NULL);
   1869 
   1870     if (encoding != NULL) {
   1871         handler = xmlFindCharEncodingHandler(encoding);
   1872         if (handler == NULL) {
   1873             xmlFree(ret);
   1874             return(NULL);
   1875         }
   1876     } else
   1877         handler = NULL;
   1878     out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
   1879     if (out_buff == NULL) {
   1880         xmlFree(ret);
   1881         if (handler) xmlCharEncCloseFunc(handler);
   1882         return(NULL);
   1883     }
   1884 
   1885     ret->buf = out_buff;
   1886     return(ret);
   1887 }
   1888 
   1889 /**
   1890  * xmlSaveToIO:
   1891  * @iowrite:  an I/O write function
   1892  * @ioclose:  an I/O close function
   1893  * @ioctx:  an I/O handler
   1894  * @encoding:  the encoding name to use or NULL
   1895  * @options:  a set of xmlSaveOptions
   1896  *
   1897  * Create a document saving context serializing to a file descriptor
   1898  * with the encoding and the options given
   1899  *
   1900  * Returns a new serialization context or NULL in case of error.
   1901  */
   1902 xmlSaveCtxtPtr
   1903 xmlSaveToIO(xmlOutputWriteCallback iowrite,
   1904             xmlOutputCloseCallback ioclose,
   1905             void *ioctx, const char *encoding, int options)
   1906 {
   1907     xmlSaveCtxtPtr ret;
   1908 
   1909     ret = xmlNewSaveCtxt(encoding, options);
   1910     if (ret == NULL) return(NULL);
   1911     ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
   1912     if (ret->buf == NULL) {
   1913 	xmlFreeSaveCtxt(ret);
   1914 	return(NULL);
   1915     }
   1916     return(ret);
   1917 }
   1918 
   1919 /**
   1920  * xmlSaveDoc:
   1921  * @ctxt:  a document saving context
   1922  * @doc:  a document
   1923  *
   1924  * Save a full document to a saving context
   1925  * TODO: The function is not fully implemented yet as it does not return the
   1926  * byte count but 0 instead
   1927  *
   1928  * Returns the number of byte written or -1 in case of error
   1929  */
   1930 long
   1931 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
   1932 {
   1933     long ret = 0;
   1934 
   1935     if ((ctxt == NULL) || (doc == NULL)) return(-1);
   1936     if (xmlDocContentDumpOutput(ctxt, doc) < 0)
   1937         return(-1);
   1938     return(ret);
   1939 }
   1940 
   1941 /**
   1942  * xmlSaveTree:
   1943  * @ctxt:  a document saving context
   1944  * @node:  the top node of the subtree to save
   1945  *
   1946  * Save a subtree starting at the node parameter to a saving context
   1947  * TODO: The function is not fully implemented yet as it does not return the
   1948  * byte count but 0 instead
   1949  *
   1950  * Returns the number of byte written or -1 in case of error
   1951  */
   1952 long
   1953 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
   1954 {
   1955     long ret = 0;
   1956 
   1957     if ((ctxt == NULL) || (node == NULL)) return(-1);
   1958     xmlNodeDumpOutputInternal(ctxt, node);
   1959     return(ret);
   1960 }
   1961 
   1962 /**
   1963  * xmlSaveFlush:
   1964  * @ctxt:  a document saving context
   1965  *
   1966  * Flush a document saving context, i.e. make sure that all bytes have
   1967  * been output.
   1968  *
   1969  * Returns the number of byte written or -1 in case of error.
   1970  */
   1971 int
   1972 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
   1973 {
   1974     if (ctxt == NULL) return(-1);
   1975     if (ctxt->buf == NULL) return(-1);
   1976     return(xmlOutputBufferFlush(ctxt->buf));
   1977 }
   1978 
   1979 /**
   1980  * xmlSaveClose:
   1981  * @ctxt:  a document saving context
   1982  *
   1983  * Close a document saving context, i.e. make sure that all bytes have
   1984  * been output and free the associated data.
   1985  *
   1986  * Returns the number of byte written or -1 in case of error.
   1987  */
   1988 int
   1989 xmlSaveClose(xmlSaveCtxtPtr ctxt)
   1990 {
   1991     int ret;
   1992 
   1993     if (ctxt == NULL) return(-1);
   1994     ret = xmlSaveFlush(ctxt);
   1995     xmlFreeSaveCtxt(ctxt);
   1996     return(ret);
   1997 }
   1998 
   1999 /**
   2000  * xmlSaveSetEscape:
   2001  * @ctxt:  a document saving context
   2002  * @escape:  the escaping function
   2003  *
   2004  * Set a custom escaping function to be used for text in element content
   2005  *
   2006  * Returns 0 if successful or -1 in case of error.
   2007  */
   2008 int
   2009 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
   2010 {
   2011     if (ctxt == NULL) return(-1);
   2012     ctxt->escape = escape;
   2013     return(0);
   2014 }
   2015 
   2016 /**
   2017  * xmlSaveSetAttrEscape:
   2018  * @ctxt:  a document saving context
   2019  * @escape:  the escaping function
   2020  *
   2021  * Set a custom escaping function to be used for text in attribute content
   2022  *
   2023  * Returns 0 if successful or -1 in case of error.
   2024  */
   2025 int
   2026 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
   2027 {
   2028     if (ctxt == NULL) return(-1);
   2029     ctxt->escapeAttr = escape;
   2030     return(0);
   2031 }
   2032 
   2033 /************************************************************************
   2034  *									*
   2035  *		Public entry points based on buffers			*
   2036  *									*
   2037  ************************************************************************/
   2038 
   2039 /**
   2040  * xmlBufAttrSerializeTxtContent:
   2041  * @buf:  and xmlBufPtr output
   2042  * @doc:  the document
   2043  * @attr: the attribute node
   2044  * @string: the text content
   2045  *
   2046  * Serialize text attribute values to an xmlBufPtr
   2047  */
   2048 void
   2049 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
   2050                               xmlAttrPtr attr, const xmlChar * string)
   2051 {
   2052     xmlChar *base, *cur;
   2053 
   2054     if (string == NULL)
   2055         return;
   2056     base = cur = (xmlChar *) string;
   2057     while (*cur != 0) {
   2058         if (*cur == '\n') {
   2059             if (base != cur)
   2060                 xmlBufAdd(buf, base, cur - base);
   2061             xmlBufAdd(buf, BAD_CAST "&#10;", 5);
   2062             cur++;
   2063             base = cur;
   2064         } else if (*cur == '\r') {
   2065             if (base != cur)
   2066                 xmlBufAdd(buf, base, cur - base);
   2067             xmlBufAdd(buf, BAD_CAST "&#13;", 5);
   2068             cur++;
   2069             base = cur;
   2070         } else if (*cur == '\t') {
   2071             if (base != cur)
   2072                 xmlBufAdd(buf, base, cur - base);
   2073             xmlBufAdd(buf, BAD_CAST "&#9;", 4);
   2074             cur++;
   2075             base = cur;
   2076         } else if (*cur == '"') {
   2077             if (base != cur)
   2078                 xmlBufAdd(buf, base, cur - base);
   2079             xmlBufAdd(buf, BAD_CAST "&quot;", 6);
   2080             cur++;
   2081             base = cur;
   2082         } else if (*cur == '<') {
   2083             if (base != cur)
   2084                 xmlBufAdd(buf, base, cur - base);
   2085             xmlBufAdd(buf, BAD_CAST "&lt;", 4);
   2086             cur++;
   2087             base = cur;
   2088         } else if (*cur == '>') {
   2089             if (base != cur)
   2090                 xmlBufAdd(buf, base, cur - base);
   2091             xmlBufAdd(buf, BAD_CAST "&gt;", 4);
   2092             cur++;
   2093             base = cur;
   2094         } else if (*cur == '&') {
   2095             if (base != cur)
   2096                 xmlBufAdd(buf, base, cur - base);
   2097             xmlBufAdd(buf, BAD_CAST "&amp;", 5);
   2098             cur++;
   2099             base = cur;
   2100         } else if ((*cur >= 0x80) && ((doc == NULL) ||
   2101                                       (doc->encoding == NULL))) {
   2102             /*
   2103              * We assume we have UTF-8 content.
   2104              */
   2105             unsigned char tmp[12];
   2106             int val = 0, l = 1;
   2107 
   2108             if (base != cur)
   2109                 xmlBufAdd(buf, base, cur - base);
   2110             if (*cur < 0xC0) {
   2111                 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
   2112                 if (doc != NULL)
   2113                     doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
   2114 		xmlSerializeHexCharRef(tmp, *cur);
   2115                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2116                 cur++;
   2117                 base = cur;
   2118                 continue;
   2119             } else if (*cur < 0xE0) {
   2120                 val = (cur[0]) & 0x1F;
   2121                 val <<= 6;
   2122                 val |= (cur[1]) & 0x3F;
   2123                 l = 2;
   2124             } else if (*cur < 0xF0) {
   2125                 val = (cur[0]) & 0x0F;
   2126                 val <<= 6;
   2127                 val |= (cur[1]) & 0x3F;
   2128                 val <<= 6;
   2129                 val |= (cur[2]) & 0x3F;
   2130                 l = 3;
   2131             } else if (*cur < 0xF8) {
   2132                 val = (cur[0]) & 0x07;
   2133                 val <<= 6;
   2134                 val |= (cur[1]) & 0x3F;
   2135                 val <<= 6;
   2136                 val |= (cur[2]) & 0x3F;
   2137                 val <<= 6;
   2138                 val |= (cur[3]) & 0x3F;
   2139                 l = 4;
   2140             }
   2141             if ((l == 1) || (!IS_CHAR(val))) {
   2142                 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
   2143                 if (doc != NULL)
   2144                     doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
   2145 
   2146 		xmlSerializeHexCharRef(tmp, *cur);
   2147                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2148                 cur++;
   2149                 base = cur;
   2150                 continue;
   2151             }
   2152             /*
   2153              * We could do multiple things here. Just save
   2154              * as a char ref
   2155              */
   2156 	    xmlSerializeHexCharRef(tmp, val);
   2157             xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2158             cur += l;
   2159             base = cur;
   2160         } else {
   2161             cur++;
   2162         }
   2163     }
   2164     if (base != cur)
   2165         xmlBufAdd(buf, base, cur - base);
   2166 }
   2167 
   2168 /**
   2169  * xmlAttrSerializeTxtContent:
   2170  * @buf:  the XML buffer output
   2171  * @doc:  the document
   2172  * @attr: the attribute node
   2173  * @string: the text content
   2174  *
   2175  * Serialize text attribute values to an xml simple buffer
   2176  */
   2177 void
   2178 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
   2179                            xmlAttrPtr attr, const xmlChar * string)
   2180 {
   2181     xmlBufPtr buffer;
   2182 
   2183     if ((buf == NULL) || (string == NULL))
   2184         return;
   2185     buffer = xmlBufFromBuffer(buf);
   2186     if (buffer == NULL)
   2187         return;
   2188     xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
   2189     xmlBufBackToBuffer(buffer);
   2190 }
   2191 
   2192 /**
   2193  * xmlNodeDump:
   2194  * @buf:  the XML buffer output
   2195  * @doc:  the document
   2196  * @cur:  the current node
   2197  * @level: the imbrication level for indenting
   2198  * @format: is formatting allowed
   2199  *
   2200  * Dump an XML node, recursive behaviour,children are printed too.
   2201  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2202  * or xmlKeepBlanksDefault(0) was called
   2203  * Since this is using xmlBuffer structures it is limited to 2GB and somehow
   2204  * deprecated, use xmlBufNodeDump() instead.
   2205  *
   2206  * Returns the number of bytes written to the buffer or -1 in case of error
   2207  */
   2208 int
   2209 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
   2210             int format)
   2211 {
   2212     xmlBufPtr buffer;
   2213     int ret;
   2214 
   2215     if ((buf == NULL) || (cur == NULL))
   2216         return(-1);
   2217     buffer = xmlBufFromBuffer(buf);
   2218     if (buffer == NULL)
   2219         return(-1);
   2220     ret = xmlBufNodeDump(buffer, doc, cur, level, format);
   2221     xmlBufBackToBuffer(buffer);
   2222     if (ret > INT_MAX)
   2223         return(-1);
   2224     return((int) ret);
   2225 }
   2226 
   2227 /**
   2228  * xmlBufNodeDump:
   2229  * @buf:  the XML buffer output
   2230  * @doc:  the document
   2231  * @cur:  the current node
   2232  * @level: the imbrication level for indenting
   2233  * @format: is formatting allowed
   2234  *
   2235  * Dump an XML node, recursive behaviour,children are printed too.
   2236  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2237  * or xmlKeepBlanksDefault(0) was called
   2238  *
   2239  * Returns the number of bytes written to the buffer, in case of error 0
   2240  *     is returned or @buf stores the error
   2241  */
   2242 
   2243 size_t
   2244 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
   2245             int format)
   2246 {
   2247     size_t use;
   2248     int ret;
   2249     xmlOutputBufferPtr outbuf;
   2250     int oldalloc;
   2251 
   2252     xmlInitParser();
   2253 
   2254     if (cur == NULL) {
   2255 #ifdef DEBUG_TREE
   2256         xmlGenericError(xmlGenericErrorContext,
   2257                         "xmlNodeDump : node == NULL\n");
   2258 #endif
   2259         return (-1);
   2260     }
   2261     if (buf == NULL) {
   2262 #ifdef DEBUG_TREE
   2263         xmlGenericError(xmlGenericErrorContext,
   2264                         "xmlNodeDump : buf == NULL\n");
   2265 #endif
   2266         return (-1);
   2267     }
   2268     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
   2269     if (outbuf == NULL) {
   2270         xmlSaveErrMemory("creating buffer");
   2271         return (-1);
   2272     }
   2273     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
   2274     outbuf->buffer = buf;
   2275     outbuf->encoder = NULL;
   2276     outbuf->writecallback = NULL;
   2277     outbuf->closecallback = NULL;
   2278     outbuf->context = NULL;
   2279     outbuf->written = 0;
   2280 
   2281     use = xmlBufUse(buf);
   2282     oldalloc = xmlBufGetAllocationScheme(buf);
   2283     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
   2284     xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
   2285     xmlBufSetAllocationScheme(buf, oldalloc);
   2286     xmlFree(outbuf);
   2287     ret = xmlBufUse(buf) - use;
   2288     return (ret);
   2289 }
   2290 
   2291 /**
   2292  * xmlElemDump:
   2293  * @f:  the FILE * for the output
   2294  * @doc:  the document
   2295  * @cur:  the current node
   2296  *
   2297  * Dump an XML/HTML node, recursive behaviour, children are printed too.
   2298  */
   2299 void
   2300 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
   2301 {
   2302     xmlOutputBufferPtr outbuf;
   2303 
   2304     xmlInitParser();
   2305 
   2306     if (cur == NULL) {
   2307 #ifdef DEBUG_TREE
   2308         xmlGenericError(xmlGenericErrorContext,
   2309                         "xmlElemDump : cur == NULL\n");
   2310 #endif
   2311         return;
   2312     }
   2313 #ifdef DEBUG_TREE
   2314     if (doc == NULL) {
   2315         xmlGenericError(xmlGenericErrorContext,
   2316                         "xmlElemDump : doc == NULL\n");
   2317     }
   2318 #endif
   2319 
   2320     outbuf = xmlOutputBufferCreateFile(f, NULL);
   2321     if (outbuf == NULL)
   2322         return;
   2323     if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
   2324 #ifdef LIBXML_HTML_ENABLED
   2325         htmlNodeDumpOutput(outbuf, doc, cur, NULL);
   2326 #else
   2327 	xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
   2328 #endif /* LIBXML_HTML_ENABLED */
   2329     } else
   2330         xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
   2331     xmlOutputBufferClose(outbuf);
   2332 }
   2333 
   2334 /************************************************************************
   2335  *									*
   2336  *		Saving functions front-ends				*
   2337  *									*
   2338  ************************************************************************/
   2339 
   2340 /**
   2341  * xmlNodeDumpOutput:
   2342  * @buf:  the XML buffer output
   2343  * @doc:  the document
   2344  * @cur:  the current node
   2345  * @level: the imbrication level for indenting
   2346  * @format: is formatting allowed
   2347  * @encoding:  an optional encoding string
   2348  *
   2349  * Dump an XML node, recursive behaviour, children are printed too.
   2350  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2351  * or xmlKeepBlanksDefault(0) was called
   2352  */
   2353 void
   2354 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
   2355                   int level, int format, const char *encoding)
   2356 {
   2357     xmlSaveCtxt ctxt;
   2358 #ifdef LIBXML_HTML_ENABLED
   2359     xmlDtdPtr dtd;
   2360     int is_xhtml = 0;
   2361 #endif
   2362 
   2363     xmlInitParser();
   2364 
   2365     if ((buf == NULL) || (cur == NULL)) return;
   2366 
   2367     if (encoding == NULL)
   2368         encoding = "UTF-8";
   2369 
   2370     memset(&ctxt, 0, sizeof(ctxt));
   2371     ctxt.doc = doc;
   2372     ctxt.buf = buf;
   2373     ctxt.level = level;
   2374     ctxt.format = format ? 1 : 0;
   2375     ctxt.encoding = (const xmlChar *) encoding;
   2376     xmlSaveCtxtInit(&ctxt);
   2377     ctxt.options |= XML_SAVE_AS_XML;
   2378 
   2379 #ifdef LIBXML_HTML_ENABLED
   2380     dtd = xmlGetIntSubset(doc);
   2381     if (dtd != NULL) {
   2382 	is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
   2383 	if (is_xhtml < 0)
   2384 	    is_xhtml = 0;
   2385     }
   2386 
   2387     if (is_xhtml)
   2388         xhtmlNodeDumpOutput(&ctxt, cur);
   2389     else
   2390 #endif
   2391         xmlNodeDumpOutputInternal(&ctxt, cur);
   2392 }
   2393 
   2394 /**
   2395  * xmlDocDumpFormatMemoryEnc:
   2396  * @out_doc:  Document to generate XML text from
   2397  * @doc_txt_ptr:  Memory pointer for allocated XML text
   2398  * @doc_txt_len:  Length of the generated XML text
   2399  * @txt_encoding:  Character encoding to use when generating XML text
   2400  * @format:  should formatting spaces been added
   2401  *
   2402  * Dump the current DOM tree into memory using the character encoding specified
   2403  * by the caller.  Note it is up to the caller of this function to free the
   2404  * allocated memory with xmlFree().
   2405  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2406  * or xmlKeepBlanksDefault(0) was called
   2407  */
   2408 
   2409 void
   2410 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
   2411 		int * doc_txt_len, const char * txt_encoding,
   2412 		int format) {
   2413     xmlSaveCtxt ctxt;
   2414     int                         dummy = 0;
   2415     xmlOutputBufferPtr          out_buff = NULL;
   2416     xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
   2417 
   2418     if (doc_txt_len == NULL) {
   2419         doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
   2420     }
   2421 
   2422     if (doc_txt_ptr == NULL) {
   2423         *doc_txt_len = 0;
   2424         return;
   2425     }
   2426 
   2427     *doc_txt_ptr = NULL;
   2428     *doc_txt_len = 0;
   2429 
   2430     if (out_doc == NULL) {
   2431         /*  No document, no output  */
   2432         return;
   2433     }
   2434 
   2435     /*
   2436      *  Validate the encoding value, if provided.
   2437      *  This logic is copied from xmlSaveFileEnc.
   2438      */
   2439 
   2440     if (txt_encoding == NULL)
   2441 	txt_encoding = (const char *) out_doc->encoding;
   2442     if (txt_encoding != NULL) {
   2443 	conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
   2444 	if ( conv_hdlr == NULL ) {
   2445 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
   2446 		       txt_encoding);
   2447 	    return;
   2448 	}
   2449     }
   2450 
   2451     if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
   2452         xmlSaveErrMemory("creating buffer");
   2453         return;
   2454     }
   2455 
   2456     memset(&ctxt, 0, sizeof(ctxt));
   2457     ctxt.doc = out_doc;
   2458     ctxt.buf = out_buff;
   2459     ctxt.level = 0;
   2460     ctxt.format = format ? 1 : 0;
   2461     ctxt.encoding = (const xmlChar *) txt_encoding;
   2462     xmlSaveCtxtInit(&ctxt);
   2463     ctxt.options |= XML_SAVE_AS_XML;
   2464     xmlDocContentDumpOutput(&ctxt, out_doc);
   2465     xmlOutputBufferFlush(out_buff);
   2466     if (out_buff->conv != NULL) {
   2467 	*doc_txt_len = xmlBufUse(out_buff->conv);
   2468 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
   2469     } else {
   2470 	*doc_txt_len = xmlBufUse(out_buff->buffer);
   2471 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
   2472     }
   2473     (void)xmlOutputBufferClose(out_buff);
   2474 
   2475     if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
   2476         *doc_txt_len = 0;
   2477         xmlSaveErrMemory("creating output");
   2478     }
   2479 
   2480     return;
   2481 }
   2482 
   2483 /**
   2484  * xmlDocDumpMemory:
   2485  * @cur:  the document
   2486  * @mem:  OUT: the memory pointer
   2487  * @size:  OUT: the memory length
   2488  *
   2489  * Dump an XML document in memory and return the #xmlChar * and it's size
   2490  * in bytes. It's up to the caller to free the memory with xmlFree().
   2491  * The resulting byte array is zero terminated, though the last 0 is not
   2492  * included in the returned size.
   2493  */
   2494 void
   2495 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
   2496     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
   2497 }
   2498 
   2499 /**
   2500  * xmlDocDumpFormatMemory:
   2501  * @cur:  the document
   2502  * @mem:  OUT: the memory pointer
   2503  * @size:  OUT: the memory length
   2504  * @format:  should formatting spaces been added
   2505  *
   2506  *
   2507  * Dump an XML document in memory and return the #xmlChar * and it's size.
   2508  * It's up to the caller to free the memory with xmlFree().
   2509  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2510  * or xmlKeepBlanksDefault(0) was called
   2511  */
   2512 void
   2513 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
   2514     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
   2515 }
   2516 
   2517 /**
   2518  * xmlDocDumpMemoryEnc:
   2519  * @out_doc:  Document to generate XML text from
   2520  * @doc_txt_ptr:  Memory pointer for allocated XML text
   2521  * @doc_txt_len:  Length of the generated XML text
   2522  * @txt_encoding:  Character encoding to use when generating XML text
   2523  *
   2524  * Dump the current DOM tree into memory using the character encoding specified
   2525  * by the caller.  Note it is up to the caller of this function to free the
   2526  * allocated memory with xmlFree().
   2527  */
   2528 
   2529 void
   2530 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
   2531 	            int * doc_txt_len, const char * txt_encoding) {
   2532     xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
   2533 	                      txt_encoding, 0);
   2534 }
   2535 
   2536 /**
   2537  * xmlDocFormatDump:
   2538  * @f:  the FILE*
   2539  * @cur:  the document
   2540  * @format: should formatting spaces been added
   2541  *
   2542  * Dump an XML document to an open FILE.
   2543  *
   2544  * returns: the number of bytes written or -1 in case of failure.
   2545  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2546  * or xmlKeepBlanksDefault(0) was called
   2547  */
   2548 int
   2549 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
   2550     xmlSaveCtxt ctxt;
   2551     xmlOutputBufferPtr buf;
   2552     const char * encoding;
   2553     xmlCharEncodingHandlerPtr handler = NULL;
   2554     int ret;
   2555 
   2556     if (cur == NULL) {
   2557 #ifdef DEBUG_TREE
   2558         xmlGenericError(xmlGenericErrorContext,
   2559 		"xmlDocDump : document == NULL\n");
   2560 #endif
   2561 	return(-1);
   2562     }
   2563     encoding = (const char *) cur->encoding;
   2564 
   2565     if (encoding != NULL) {
   2566 	handler = xmlFindCharEncodingHandler(encoding);
   2567 	if (handler == NULL) {
   2568 	    xmlFree((char *) cur->encoding);
   2569 	    cur->encoding = NULL;
   2570 	    encoding = NULL;
   2571 	}
   2572     }
   2573     buf = xmlOutputBufferCreateFile(f, handler);
   2574     if (buf == NULL) return(-1);
   2575     memset(&ctxt, 0, sizeof(ctxt));
   2576     ctxt.doc = cur;
   2577     ctxt.buf = buf;
   2578     ctxt.level = 0;
   2579     ctxt.format = format ? 1 : 0;
   2580     ctxt.encoding = (const xmlChar *) encoding;
   2581     xmlSaveCtxtInit(&ctxt);
   2582     ctxt.options |= XML_SAVE_AS_XML;
   2583     xmlDocContentDumpOutput(&ctxt, cur);
   2584 
   2585     ret = xmlOutputBufferClose(buf);
   2586     return(ret);
   2587 }
   2588 
   2589 /**
   2590  * xmlDocDump:
   2591  * @f:  the FILE*
   2592  * @cur:  the document
   2593  *
   2594  * Dump an XML document to an open FILE.
   2595  *
   2596  * returns: the number of bytes written or -1 in case of failure.
   2597  */
   2598 int
   2599 xmlDocDump(FILE *f, xmlDocPtr cur) {
   2600     return(xmlDocFormatDump (f, cur, 0));
   2601 }
   2602 
   2603 /**
   2604  * xmlSaveFileTo:
   2605  * @buf:  an output I/O buffer
   2606  * @cur:  the document
   2607  * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
   2608  *
   2609  * Dump an XML document to an I/O buffer.
   2610  * Warning ! This call xmlOutputBufferClose() on buf which is not available
   2611  * after this call.
   2612  *
   2613  * returns: the number of bytes written or -1 in case of failure.
   2614  */
   2615 int
   2616 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
   2617     xmlSaveCtxt ctxt;
   2618     int ret;
   2619 
   2620     if (buf == NULL) return(-1);
   2621     if (cur == NULL) {
   2622         xmlOutputBufferClose(buf);
   2623 	return(-1);
   2624     }
   2625     memset(&ctxt, 0, sizeof(ctxt));
   2626     ctxt.doc = cur;
   2627     ctxt.buf = buf;
   2628     ctxt.level = 0;
   2629     ctxt.format = 0;
   2630     ctxt.encoding = (const xmlChar *) encoding;
   2631     xmlSaveCtxtInit(&ctxt);
   2632     ctxt.options |= XML_SAVE_AS_XML;
   2633     xmlDocContentDumpOutput(&ctxt, cur);
   2634     ret = xmlOutputBufferClose(buf);
   2635     return(ret);
   2636 }
   2637 
   2638 /**
   2639  * xmlSaveFormatFileTo:
   2640  * @buf:  an output I/O buffer
   2641  * @cur:  the document
   2642  * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
   2643  * @format: should formatting spaces been added
   2644  *
   2645  * Dump an XML document to an I/O buffer.
   2646  * Warning ! This call xmlOutputBufferClose() on buf which is not available
   2647  * after this call.
   2648  *
   2649  * returns: the number of bytes written or -1 in case of failure.
   2650  */
   2651 int
   2652 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
   2653                     const char *encoding, int format)
   2654 {
   2655     xmlSaveCtxt ctxt;
   2656     int ret;
   2657 
   2658     if (buf == NULL) return(-1);
   2659     if ((cur == NULL) ||
   2660         ((cur->type != XML_DOCUMENT_NODE) &&
   2661 	 (cur->type != XML_HTML_DOCUMENT_NODE))) {
   2662         xmlOutputBufferClose(buf);
   2663 	return(-1);
   2664     }
   2665     memset(&ctxt, 0, sizeof(ctxt));
   2666     ctxt.doc = cur;
   2667     ctxt.buf = buf;
   2668     ctxt.level = 0;
   2669     ctxt.format = format ? 1 : 0;
   2670     ctxt.encoding = (const xmlChar *) encoding;
   2671     xmlSaveCtxtInit(&ctxt);
   2672     ctxt.options |= XML_SAVE_AS_XML;
   2673     xmlDocContentDumpOutput(&ctxt, cur);
   2674     ret = xmlOutputBufferClose(buf);
   2675     return (ret);
   2676 }
   2677 
   2678 /**
   2679  * xmlSaveFormatFileEnc:
   2680  * @filename:  the filename or URL to output
   2681  * @cur:  the document being saved
   2682  * @encoding:  the name of the encoding to use or NULL.
   2683  * @format:  should formatting spaces be added.
   2684  *
   2685  * Dump an XML document to a file or an URL.
   2686  *
   2687  * Returns the number of bytes written or -1 in case of error.
   2688  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2689  * or xmlKeepBlanksDefault(0) was called
   2690  */
   2691 int
   2692 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
   2693 			const char * encoding, int format ) {
   2694     xmlSaveCtxt ctxt;
   2695     xmlOutputBufferPtr buf;
   2696     xmlCharEncodingHandlerPtr handler = NULL;
   2697     int ret;
   2698 
   2699     if (cur == NULL)
   2700 	return(-1);
   2701 
   2702     if (encoding == NULL)
   2703 	encoding = (const char *) cur->encoding;
   2704 
   2705     if (encoding != NULL) {
   2706 
   2707 	    handler = xmlFindCharEncodingHandler(encoding);
   2708 	    if (handler == NULL)
   2709 		return(-1);
   2710     }
   2711 
   2712 #ifdef HAVE_ZLIB_H
   2713     if (cur->compression < 0) cur->compression = xmlGetCompressMode();
   2714 #endif
   2715     /*
   2716      * save the content to a temp buffer.
   2717      */
   2718     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
   2719     if (buf == NULL) return(-1);
   2720     memset(&ctxt, 0, sizeof(ctxt));
   2721     ctxt.doc = cur;
   2722     ctxt.buf = buf;
   2723     ctxt.level = 0;
   2724     ctxt.format = format ? 1 : 0;
   2725     ctxt.encoding = (const xmlChar *) encoding;
   2726     xmlSaveCtxtInit(&ctxt);
   2727     ctxt.options |= XML_SAVE_AS_XML;
   2728 
   2729     xmlDocContentDumpOutput(&ctxt, cur);
   2730 
   2731     ret = xmlOutputBufferClose(buf);
   2732     return(ret);
   2733 }
   2734 
   2735 
   2736 /**
   2737  * xmlSaveFileEnc:
   2738  * @filename:  the filename (or URL)
   2739  * @cur:  the document
   2740  * @encoding:  the name of an encoding (or NULL)
   2741  *
   2742  * Dump an XML document, converting it to the given encoding
   2743  *
   2744  * returns: the number of bytes written or -1 in case of failure.
   2745  */
   2746 int
   2747 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
   2748     return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
   2749 }
   2750 
   2751 /**
   2752  * xmlSaveFormatFile:
   2753  * @filename:  the filename (or URL)
   2754  * @cur:  the document
   2755  * @format:  should formatting spaces been added
   2756  *
   2757  * Dump an XML document to a file. Will use compression if
   2758  * compiled in and enabled. If @filename is "-" the stdout file is
   2759  * used. If @format is set then the document will be indented on output.
   2760  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2761  * or xmlKeepBlanksDefault(0) was called
   2762  *
   2763  * returns: the number of bytes written or -1 in case of failure.
   2764  */
   2765 int
   2766 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
   2767     return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
   2768 }
   2769 
   2770 /**
   2771  * xmlSaveFile:
   2772  * @filename:  the filename (or URL)
   2773  * @cur:  the document
   2774  *
   2775  * Dump an XML document to a file. Will use compression if
   2776  * compiled in and enabled. If @filename is "-" the stdout file is
   2777  * used.
   2778  * returns: the number of bytes written or -1 in case of failure.
   2779  */
   2780 int
   2781 xmlSaveFile(const char *filename, xmlDocPtr cur) {
   2782     return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
   2783 }
   2784 
   2785 #endif /* LIBXML_OUTPUT_ENABLED */
   2786 
   2787 #define bottom_xmlsave
   2788 #include "elfgcchack.h"
   2789