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     }
   1127 
   1128     if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
   1129          ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
   1130          ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
   1131         (ctxt->options & XML_SAVE_AS_HTML)) {
   1132 #ifdef LIBXML_HTML_ENABLED
   1133         if (encoding != NULL)
   1134 	    htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
   1135         if (encoding == NULL)
   1136 	    encoding = htmlGetMetaEncoding(cur);
   1137         if (encoding == NULL)
   1138 	    encoding = BAD_CAST "HTML";
   1139 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
   1140 	    (buf->encoder == NULL) && (buf->conv == NULL)) {
   1141 	    if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
   1142 		cur->encoding = oldenc;
   1143 		return(-1);
   1144 	    }
   1145 	}
   1146         if (ctxt->options & XML_SAVE_FORMAT)
   1147 	    htmlDocContentDumpFormatOutput(buf, cur,
   1148 	                                   (const char *)encoding, 1);
   1149 	else
   1150 	    htmlDocContentDumpFormatOutput(buf, cur,
   1151 	                                   (const char *)encoding, 0);
   1152 	if (ctxt->encoding != NULL)
   1153 	    cur->encoding = oldenc;
   1154 	return(0);
   1155 #else
   1156         return(-1);
   1157 #endif
   1158     } else if ((cur->type == XML_DOCUMENT_NODE) ||
   1159                (ctxt->options & XML_SAVE_AS_XML) ||
   1160                (ctxt->options & XML_SAVE_XHTML)) {
   1161 	enc = xmlParseCharEncoding((const char*) encoding);
   1162 	if ((encoding != NULL) && (oldctxtenc == NULL) &&
   1163 	    (buf->encoder == NULL) && (buf->conv == NULL) &&
   1164 	    ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
   1165 	    if ((enc != XML_CHAR_ENCODING_UTF8) &&
   1166 		(enc != XML_CHAR_ENCODING_NONE) &&
   1167 		(enc != XML_CHAR_ENCODING_ASCII)) {
   1168 		/*
   1169 		 * we need to switch to this encoding but just for this
   1170 		 * document since we output the XMLDecl the conversion
   1171 		 * must be done to not generate not well formed documents.
   1172 		 */
   1173 		if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
   1174 		    cur->encoding = oldenc;
   1175 		    return(-1);
   1176 		}
   1177 		switched_encoding = 1;
   1178 	    }
   1179 	    if (ctxt->escape == xmlEscapeEntities)
   1180 		ctxt->escape = NULL;
   1181 	    if (ctxt->escapeAttr == xmlEscapeEntities)
   1182 		ctxt->escapeAttr = NULL;
   1183 	}
   1184 
   1185 
   1186 	/*
   1187 	 * Save the XML declaration
   1188 	 */
   1189 	if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
   1190 	    xmlOutputBufferWrite(buf, 14, "<?xml version=");
   1191 	    if (cur->version != NULL)
   1192 		xmlBufWriteQuotedString(buf->buffer, cur->version);
   1193 	    else
   1194 		xmlOutputBufferWrite(buf, 5, "\"1.0\"");
   1195 	    if (encoding != NULL) {
   1196 		xmlOutputBufferWrite(buf, 10, " encoding=");
   1197 		xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
   1198 	    }
   1199 	    switch (cur->standalone) {
   1200 		case 0:
   1201 		    xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
   1202 		    break;
   1203 		case 1:
   1204 		    xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
   1205 		    break;
   1206 	    }
   1207 	    xmlOutputBufferWrite(buf, 3, "?>\n");
   1208 	}
   1209 
   1210 #ifdef LIBXML_HTML_ENABLED
   1211         if (ctxt->options & XML_SAVE_XHTML)
   1212             is_xhtml = 1;
   1213 	if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
   1214 	    dtd = xmlGetIntSubset(cur);
   1215 	    if (dtd != NULL) {
   1216 		is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
   1217 		if (is_xhtml < 0) is_xhtml = 0;
   1218 	    }
   1219 	}
   1220 #endif
   1221 	if (cur->children != NULL) {
   1222 	    xmlNodePtr child = cur->children;
   1223 
   1224 	    while (child != NULL) {
   1225 		ctxt->level = 0;
   1226 #ifdef LIBXML_HTML_ENABLED
   1227 		if (is_xhtml)
   1228 		    xhtmlNodeDumpOutput(ctxt, child);
   1229 		else
   1230 #endif
   1231 		    xmlNodeDumpOutputInternal(ctxt, child);
   1232 		xmlOutputBufferWrite(buf, 1, "\n");
   1233 		child = child->next;
   1234 	    }
   1235 	}
   1236     }
   1237 
   1238     /*
   1239      * Restore the state of the saving context at the end of the document
   1240      */
   1241     if ((switched_encoding) && (oldctxtenc == NULL)) {
   1242 	xmlSaveClearEncoding(ctxt);
   1243 	ctxt->escape = oldescape;
   1244 	ctxt->escapeAttr = oldescapeAttr;
   1245     }
   1246     cur->encoding = oldenc;
   1247     return(0);
   1248 }
   1249 
   1250 #ifdef LIBXML_HTML_ENABLED
   1251 /************************************************************************
   1252  *									*
   1253  *		Functions specific to XHTML serialization		*
   1254  *									*
   1255  ************************************************************************/
   1256 
   1257 /**
   1258  * xhtmlIsEmpty:
   1259  * @node:  the node
   1260  *
   1261  * Check if a node is an empty xhtml node
   1262  *
   1263  * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
   1264  */
   1265 static int
   1266 xhtmlIsEmpty(xmlNodePtr node) {
   1267     if (node == NULL)
   1268 	return(-1);
   1269     if (node->type != XML_ELEMENT_NODE)
   1270 	return(0);
   1271     if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
   1272 	return(0);
   1273     if (node->children != NULL)
   1274 	return(0);
   1275     switch (node->name[0]) {
   1276 	case 'a':
   1277 	    if (xmlStrEqual(node->name, BAD_CAST "area"))
   1278 		return(1);
   1279 	    return(0);
   1280 	case 'b':
   1281 	    if (xmlStrEqual(node->name, BAD_CAST "br"))
   1282 		return(1);
   1283 	    if (xmlStrEqual(node->name, BAD_CAST "base"))
   1284 		return(1);
   1285 	    if (xmlStrEqual(node->name, BAD_CAST "basefont"))
   1286 		return(1);
   1287 	    return(0);
   1288 	case 'c':
   1289 	    if (xmlStrEqual(node->name, BAD_CAST "col"))
   1290 		return(1);
   1291 	    return(0);
   1292 	case 'f':
   1293 	    if (xmlStrEqual(node->name, BAD_CAST "frame"))
   1294 		return(1);
   1295 	    return(0);
   1296 	case 'h':
   1297 	    if (xmlStrEqual(node->name, BAD_CAST "hr"))
   1298 		return(1);
   1299 	    return(0);
   1300 	case 'i':
   1301 	    if (xmlStrEqual(node->name, BAD_CAST "img"))
   1302 		return(1);
   1303 	    if (xmlStrEqual(node->name, BAD_CAST "input"))
   1304 		return(1);
   1305 	    if (xmlStrEqual(node->name, BAD_CAST "isindex"))
   1306 		return(1);
   1307 	    return(0);
   1308 	case 'l':
   1309 	    if (xmlStrEqual(node->name, BAD_CAST "link"))
   1310 		return(1);
   1311 	    return(0);
   1312 	case 'm':
   1313 	    if (xmlStrEqual(node->name, BAD_CAST "meta"))
   1314 		return(1);
   1315 	    return(0);
   1316 	case 'p':
   1317 	    if (xmlStrEqual(node->name, BAD_CAST "param"))
   1318 		return(1);
   1319 	    return(0);
   1320     }
   1321     return(0);
   1322 }
   1323 
   1324 /**
   1325  * xhtmlAttrListDumpOutput:
   1326  * @cur:  the first attribute pointer
   1327  *
   1328  * Dump a list of XML attributes
   1329  */
   1330 static void
   1331 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
   1332     xmlAttrPtr xml_lang = NULL;
   1333     xmlAttrPtr lang = NULL;
   1334     xmlAttrPtr name = NULL;
   1335     xmlAttrPtr id = NULL;
   1336     xmlNodePtr parent;
   1337     xmlOutputBufferPtr buf;
   1338 
   1339     if (cur == NULL) return;
   1340     buf = ctxt->buf;
   1341     parent = cur->parent;
   1342     while (cur != NULL) {
   1343 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
   1344 	    id = cur;
   1345 	else
   1346 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
   1347 	    name = cur;
   1348 	else
   1349 	if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
   1350 	    lang = cur;
   1351 	else
   1352 	if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
   1353 	    (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
   1354 	    xml_lang = cur;
   1355 	else if ((cur->ns == NULL) &&
   1356 		 ((cur->children == NULL) ||
   1357 		  (cur->children->content == NULL) ||
   1358 		  (cur->children->content[0] == 0)) &&
   1359 		 (htmlIsBooleanAttr(cur->name))) {
   1360 	    if (cur->children != NULL)
   1361 		xmlFreeNode(cur->children);
   1362 	    cur->children = xmlNewText(cur->name);
   1363 	    if (cur->children != NULL)
   1364 		cur->children->parent = (xmlNodePtr) cur;
   1365 	}
   1366         xmlAttrDumpOutput(ctxt, cur);
   1367 	cur = cur->next;
   1368     }
   1369     /*
   1370      * C.8
   1371      */
   1372     if ((name != NULL) && (id == NULL)) {
   1373 	if ((parent != NULL) && (parent->name != NULL) &&
   1374 	    ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
   1375 	     (xmlStrEqual(parent->name, BAD_CAST "p")) ||
   1376 	     (xmlStrEqual(parent->name, BAD_CAST "div")) ||
   1377 	     (xmlStrEqual(parent->name, BAD_CAST "img")) ||
   1378 	     (xmlStrEqual(parent->name, BAD_CAST "map")) ||
   1379 	     (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
   1380 	     (xmlStrEqual(parent->name, BAD_CAST "form")) ||
   1381 	     (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
   1382 	     (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
   1383 	    xmlOutputBufferWrite(buf, 5, " id=\"");
   1384 	    xmlAttrSerializeContent(buf, name);
   1385 	    xmlOutputBufferWrite(buf, 1, "\"");
   1386 	}
   1387     }
   1388     /*
   1389      * C.7.
   1390      */
   1391     if ((lang != NULL) && (xml_lang == NULL)) {
   1392 	xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
   1393 	xmlAttrSerializeContent(buf, lang);
   1394 	xmlOutputBufferWrite(buf, 1, "\"");
   1395     } else
   1396     if ((xml_lang != NULL) && (lang == NULL)) {
   1397 	xmlOutputBufferWrite(buf, 7, " lang=\"");
   1398 	xmlAttrSerializeContent(buf, xml_lang);
   1399 	xmlOutputBufferWrite(buf, 1, "\"");
   1400     }
   1401 }
   1402 
   1403 /**
   1404  * xhtmlNodeListDumpOutput:
   1405  * @buf:  the XML buffer output
   1406  * @doc:  the XHTML document
   1407  * @cur:  the first node
   1408  * @level: the imbrication level for indenting
   1409  * @format: is formatting allowed
   1410  * @encoding:  an optional encoding string
   1411  *
   1412  * Dump an XML node list, recursive behaviour, children are printed too.
   1413  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   1414  * or xmlKeepBlanksDefault(0) was called
   1415  */
   1416 static void
   1417 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
   1418     xmlOutputBufferPtr buf;
   1419 
   1420     if (cur == NULL) return;
   1421     buf = ctxt->buf;
   1422     while (cur != NULL) {
   1423 	if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
   1424 	    (cur->type == XML_ELEMENT_NODE))
   1425 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
   1426 	                         (ctxt->level > ctxt->indent_nr ?
   1427 				  ctxt->indent_nr : ctxt->level),
   1428 				 ctxt->indent);
   1429         xhtmlNodeDumpOutput(ctxt, cur);
   1430 	if (ctxt->format == 1) {
   1431 	    xmlOutputBufferWrite(buf, 1, "\n");
   1432 	}
   1433 	cur = cur->next;
   1434     }
   1435 }
   1436 
   1437 /**
   1438  * xhtmlNodeDumpOutput:
   1439  * @buf:  the XML buffer output
   1440  * @doc:  the XHTML document
   1441  * @cur:  the current node
   1442  * @level: the imbrication level for indenting
   1443  * @format: is formatting allowed
   1444  * @encoding:  an optional encoding string
   1445  *
   1446  * Dump an XHTML node, recursive behaviour, children are printed too.
   1447  */
   1448 static void
   1449 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
   1450     int format, addmeta = 0;
   1451     xmlNodePtr tmp;
   1452     xmlChar *start, *end;
   1453     xmlOutputBufferPtr buf;
   1454 
   1455     if (cur == NULL) return;
   1456     if ((cur->type == XML_DOCUMENT_NODE) ||
   1457         (cur->type == XML_HTML_DOCUMENT_NODE)) {
   1458         xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
   1459 	return;
   1460     }
   1461     if (cur->type == XML_XINCLUDE_START)
   1462 	return;
   1463     if (cur->type == XML_XINCLUDE_END)
   1464 	return;
   1465     if (cur->type == XML_NAMESPACE_DECL) {
   1466 	xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
   1467 	return;
   1468     }
   1469     if (cur->type == XML_DTD_NODE) {
   1470         xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
   1471 	return;
   1472     }
   1473     if (cur->type == XML_DOCUMENT_FRAG_NODE) {
   1474         xhtmlNodeListDumpOutput(ctxt, cur->children);
   1475 	return;
   1476     }
   1477     buf = ctxt->buf;
   1478     if (cur->type == XML_ELEMENT_DECL) {
   1479         xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
   1480 	return;
   1481     }
   1482     if (cur->type == XML_ATTRIBUTE_DECL) {
   1483         xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
   1484 	return;
   1485     }
   1486     if (cur->type == XML_ENTITY_DECL) {
   1487         xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
   1488 	return;
   1489     }
   1490     if (cur->type == XML_TEXT_NODE) {
   1491 	if (cur->content != NULL) {
   1492 	    if ((cur->name == xmlStringText) ||
   1493 		(cur->name != xmlStringTextNoenc)) {
   1494                 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
   1495 	    } else {
   1496 		/*
   1497 		 * Disable escaping, needed for XSLT
   1498 		 */
   1499 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
   1500 	    }
   1501 	}
   1502 
   1503 	return;
   1504     }
   1505     if (cur->type == XML_PI_NODE) {
   1506 	if (cur->content != NULL) {
   1507 	    xmlOutputBufferWrite(buf, 2, "<?");
   1508 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1509 	    if (cur->content != NULL) {
   1510 		xmlOutputBufferWrite(buf, 1, " ");
   1511 		xmlOutputBufferWriteString(buf, (const char *)cur->content);
   1512 	    }
   1513 	    xmlOutputBufferWrite(buf, 2, "?>");
   1514 	} else {
   1515 	    xmlOutputBufferWrite(buf, 2, "<?");
   1516 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1517 	    xmlOutputBufferWrite(buf, 2, "?>");
   1518 	}
   1519 	return;
   1520     }
   1521     if (cur->type == XML_COMMENT_NODE) {
   1522 	if (cur->content != NULL) {
   1523 	    xmlOutputBufferWrite(buf, 4, "<!--");
   1524 	    xmlOutputBufferWriteString(buf, (const char *)cur->content);
   1525 	    xmlOutputBufferWrite(buf, 3, "-->");
   1526 	}
   1527 	return;
   1528     }
   1529     if (cur->type == XML_ENTITY_REF_NODE) {
   1530         xmlOutputBufferWrite(buf, 1, "&");
   1531 	xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1532         xmlOutputBufferWrite(buf, 1, ";");
   1533 	return;
   1534     }
   1535     if (cur->type == XML_CDATA_SECTION_NODE) {
   1536 	if (cur->content == NULL || *cur->content == '\0') {
   1537 	    xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
   1538 	} else {
   1539 	    start = end = cur->content;
   1540 	    while (*end != '\0') {
   1541 		if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
   1542 		    end = end + 2;
   1543 		    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1544 		    xmlOutputBufferWrite(buf, end - start, (const char *)start);
   1545 		    xmlOutputBufferWrite(buf, 3, "]]>");
   1546 		    start = end;
   1547 		}
   1548 		end++;
   1549 	    }
   1550 	    if (start != end) {
   1551 		xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1552 		xmlOutputBufferWriteString(buf, (const char *)start);
   1553 		xmlOutputBufferWrite(buf, 3, "]]>");
   1554 	    }
   1555 	}
   1556 	return;
   1557     }
   1558     if (cur->type == XML_ATTRIBUTE_NODE) {
   1559         xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
   1560 	return;
   1561     }
   1562 
   1563     format = ctxt->format;
   1564     if (format == 1) {
   1565 	tmp = cur->children;
   1566 	while (tmp != NULL) {
   1567 	    if ((tmp->type == XML_TEXT_NODE) ||
   1568 		(tmp->type == XML_ENTITY_REF_NODE)) {
   1569 		format = 0;
   1570 		break;
   1571 	    }
   1572 	    tmp = tmp->next;
   1573 	}
   1574     }
   1575     xmlOutputBufferWrite(buf, 1, "<");
   1576     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1577         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1578 	xmlOutputBufferWrite(buf, 1, ":");
   1579     }
   1580 
   1581     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1582     if (cur->nsDef)
   1583         xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
   1584     if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
   1585 	(cur->ns == NULL) && (cur->nsDef == NULL))) {
   1586 	/*
   1587 	 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
   1588 	 */
   1589 	xmlOutputBufferWriteString(buf,
   1590 		" xmlns=\"http://www.w3.org/1999/xhtml\"");
   1591     }
   1592     if (cur->properties != NULL)
   1593         xhtmlAttrListDumpOutput(ctxt, cur->properties);
   1594 
   1595     if ((cur->type == XML_ELEMENT_NODE) &&
   1596         (cur->parent != NULL) &&
   1597         (cur->parent->parent == (xmlNodePtr) cur->doc) &&
   1598         xmlStrEqual(cur->name, BAD_CAST"head") &&
   1599         xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
   1600 
   1601         tmp = cur->children;
   1602         while (tmp != NULL) {
   1603             if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
   1604                 xmlChar *httpequiv;
   1605 
   1606                 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
   1607                 if (httpequiv != NULL) {
   1608                     if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
   1609                         xmlFree(httpequiv);
   1610                         break;
   1611                     }
   1612                     xmlFree(httpequiv);
   1613                 }
   1614             }
   1615             tmp = tmp->next;
   1616         }
   1617         if (tmp == NULL)
   1618             addmeta = 1;
   1619     }
   1620 
   1621     if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
   1622 	if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
   1623 	    ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
   1624 	    /*
   1625 	     * C.2. Empty Elements
   1626 	     */
   1627 	    xmlOutputBufferWrite(buf, 3, " />");
   1628 	} else {
   1629 		if (addmeta == 1) {
   1630 			xmlOutputBufferWrite(buf, 1, ">");
   1631 			if (ctxt->format == 1) {
   1632 				xmlOutputBufferWrite(buf, 1, "\n");
   1633 				if (xmlIndentTreeOutput)
   1634 					xmlOutputBufferWrite(buf, ctxt->indent_size *
   1635 					(ctxt->level + 1 > ctxt->indent_nr ?
   1636 					ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
   1637 			}
   1638 			xmlOutputBufferWriteString(buf,
   1639 				"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
   1640 			if (ctxt->encoding) {
   1641 				xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
   1642 			} else {
   1643 				xmlOutputBufferWrite(buf, 5, "UTF-8");
   1644 			}
   1645 			xmlOutputBufferWrite(buf, 4, "\" />");
   1646 			if (ctxt->format == 1)
   1647 				xmlOutputBufferWrite(buf, 1, "\n");
   1648 		} else {
   1649 			xmlOutputBufferWrite(buf, 1, ">");
   1650 		}
   1651 	    /*
   1652 	     * C.3. Element Minimization and Empty Element Content
   1653 	     */
   1654 	    xmlOutputBufferWrite(buf, 2, "</");
   1655 	    if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1656 		xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1657 		xmlOutputBufferWrite(buf, 1, ":");
   1658 	    }
   1659 	    xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1660 	    xmlOutputBufferWrite(buf, 1, ">");
   1661 	}
   1662 	return;
   1663     }
   1664     xmlOutputBufferWrite(buf, 1, ">");
   1665 	if (addmeta == 1) {
   1666 		if (ctxt->format == 1) {
   1667 			xmlOutputBufferWrite(buf, 1, "\n");
   1668 			if (xmlIndentTreeOutput)
   1669 				xmlOutputBufferWrite(buf, ctxt->indent_size *
   1670 				(ctxt->level + 1 > ctxt->indent_nr ?
   1671 				ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
   1672 		}
   1673 		xmlOutputBufferWriteString(buf,
   1674 			"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
   1675 		if (ctxt->encoding) {
   1676 			xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
   1677 		} else {
   1678 			xmlOutputBufferWrite(buf, 5, "UTF-8");
   1679 		}
   1680 		xmlOutputBufferWrite(buf, 4, "\" />");
   1681 	}
   1682     if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
   1683 	xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
   1684     }
   1685 
   1686 #if 0
   1687     /*
   1688     * This was removed due to problems with HTML processors.
   1689     * See bug #345147.
   1690     */
   1691     /*
   1692      * 4.8. Script and Style elements
   1693      */
   1694     if ((cur->type == XML_ELEMENT_NODE) &&
   1695 	((xmlStrEqual(cur->name, BAD_CAST "script")) ||
   1696 	 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
   1697 	((cur->ns == NULL) ||
   1698 	 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
   1699 	xmlNodePtr child = cur->children;
   1700 
   1701 	while (child != NULL) {
   1702 	    if (child->type == XML_TEXT_NODE) {
   1703 		if ((xmlStrchr(child->content, '<') == NULL) &&
   1704 		    (xmlStrchr(child->content, '&') == NULL) &&
   1705 		    (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
   1706 		    /* Nothing to escape, so just output as is... */
   1707 		    /* FIXME: Should we do something about "--" also? */
   1708 		    int level = ctxt->level;
   1709 		    int indent = ctxt->format;
   1710 
   1711 		    ctxt->level = 0;
   1712 		    ctxt->format = 0;
   1713 		    xmlOutputBufferWriteString(buf, (const char *) child->content);
   1714 		    /* (We cannot use xhtmlNodeDumpOutput() here because
   1715 		     * we wish to leave '>' unescaped!) */
   1716 		    ctxt->level = level;
   1717 		    ctxt->format = indent;
   1718 		} else {
   1719 		    /* We must use a CDATA section.  Unfortunately,
   1720 		     * this will break CSS and JavaScript when read by
   1721 		     * a browser in HTML4-compliant mode. :-( */
   1722 		    start = end = child->content;
   1723 		    while (*end != '\0') {
   1724 			if (*end == ']' &&
   1725 			    *(end + 1) == ']' &&
   1726 			    *(end + 2) == '>') {
   1727 			    end = end + 2;
   1728 			    xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1729 			    xmlOutputBufferWrite(buf, end - start,
   1730 						 (const char *)start);
   1731 			    xmlOutputBufferWrite(buf, 3, "]]>");
   1732 			    start = end;
   1733 			}
   1734 			end++;
   1735 		    }
   1736 		    if (start != end) {
   1737 			xmlOutputBufferWrite(buf, 9, "<![CDATA[");
   1738 			xmlOutputBufferWrite(buf, end - start,
   1739 			                     (const char *)start);
   1740 			xmlOutputBufferWrite(buf, 3, "]]>");
   1741 		    }
   1742 		}
   1743 	    } else {
   1744 		int level = ctxt->level;
   1745 		int indent = ctxt->format;
   1746 
   1747 		ctxt->level = 0;
   1748 		ctxt->format = 0;
   1749 		xhtmlNodeDumpOutput(ctxt, child);
   1750 		ctxt->level = level;
   1751 		ctxt->format = indent;
   1752 	    }
   1753 	    child = child->next;
   1754 	}
   1755     }
   1756 #endif
   1757 
   1758     if (cur->children != NULL) {
   1759 	int indent = ctxt->format;
   1760 
   1761 	if (format == 1) xmlOutputBufferWrite(buf, 1, "\n");
   1762 	if (ctxt->level >= 0) ctxt->level++;
   1763 	ctxt->format = format;
   1764 	xhtmlNodeListDumpOutput(ctxt, cur->children);
   1765 	if (ctxt->level > 0) ctxt->level--;
   1766 	ctxt->format = indent;
   1767 	if ((xmlIndentTreeOutput) && (format == 1))
   1768 	    xmlOutputBufferWrite(buf, ctxt->indent_size *
   1769 	                         (ctxt->level > ctxt->indent_nr ?
   1770 				  ctxt->indent_nr : ctxt->level),
   1771 				 ctxt->indent);
   1772     }
   1773     xmlOutputBufferWrite(buf, 2, "</");
   1774     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
   1775         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
   1776 	xmlOutputBufferWrite(buf, 1, ":");
   1777     }
   1778 
   1779     xmlOutputBufferWriteString(buf, (const char *)cur->name);
   1780     xmlOutputBufferWrite(buf, 1, ">");
   1781 }
   1782 #endif
   1783 
   1784 /************************************************************************
   1785  *									*
   1786  *			Public entry points				*
   1787  *									*
   1788  ************************************************************************/
   1789 
   1790 /**
   1791  * xmlSaveToFd:
   1792  * @fd:  a file descriptor number
   1793  * @encoding:  the encoding name to use or NULL
   1794  * @options:  a set of xmlSaveOptions
   1795  *
   1796  * Create a document saving context serializing to a file descriptor
   1797  * with the encoding and the options given.
   1798  *
   1799  * Returns a new serialization context or NULL in case of error.
   1800  */
   1801 xmlSaveCtxtPtr
   1802 xmlSaveToFd(int fd, const char *encoding, int options)
   1803 {
   1804     xmlSaveCtxtPtr ret;
   1805 
   1806     ret = xmlNewSaveCtxt(encoding, options);
   1807     if (ret == NULL) return(NULL);
   1808     ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
   1809     if (ret->buf == NULL) {
   1810 	xmlFreeSaveCtxt(ret);
   1811 	return(NULL);
   1812     }
   1813     return(ret);
   1814 }
   1815 
   1816 /**
   1817  * xmlSaveToFilename:
   1818  * @filename:  a file name or an URL
   1819  * @encoding:  the encoding name to use or NULL
   1820  * @options:  a set of xmlSaveOptions
   1821  *
   1822  * Create a document saving context serializing to a filename or possibly
   1823  * to an URL (but this is less reliable) with the encoding and the options
   1824  * given.
   1825  *
   1826  * Returns a new serialization context or NULL in case of error.
   1827  */
   1828 xmlSaveCtxtPtr
   1829 xmlSaveToFilename(const char *filename, const char *encoding, int options)
   1830 {
   1831     xmlSaveCtxtPtr ret;
   1832     int compression = 0; /* TODO handle compression option */
   1833 
   1834     ret = xmlNewSaveCtxt(encoding, options);
   1835     if (ret == NULL) return(NULL);
   1836     ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
   1837                                              compression);
   1838     if (ret->buf == NULL) {
   1839 	xmlFreeSaveCtxt(ret);
   1840 	return(NULL);
   1841     }
   1842     return(ret);
   1843 }
   1844 
   1845 /**
   1846  * xmlSaveToBuffer:
   1847  * @buffer:  a buffer
   1848  * @encoding:  the encoding name to use or NULL
   1849  * @options:  a set of xmlSaveOptions
   1850  *
   1851  * Create a document saving context serializing to a buffer
   1852  * with the encoding and the options given
   1853  *
   1854  * Returns a new serialization context or NULL in case of error.
   1855  */
   1856 
   1857 xmlSaveCtxtPtr
   1858 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
   1859 {
   1860     xmlSaveCtxtPtr ret;
   1861     xmlOutputBufferPtr out_buff;
   1862     xmlCharEncodingHandlerPtr handler;
   1863 
   1864     ret = xmlNewSaveCtxt(encoding, options);
   1865     if (ret == NULL) return(NULL);
   1866 
   1867     if (encoding != NULL) {
   1868         handler = xmlFindCharEncodingHandler(encoding);
   1869         if (handler == NULL) {
   1870             xmlFree(ret);
   1871             return(NULL);
   1872         }
   1873     } else
   1874         handler = NULL;
   1875     out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
   1876     if (out_buff == NULL) {
   1877         xmlFree(ret);
   1878         if (handler) xmlCharEncCloseFunc(handler);
   1879         return(NULL);
   1880     }
   1881 
   1882     ret->buf = out_buff;
   1883     return(ret);
   1884 }
   1885 
   1886 /**
   1887  * xmlSaveToIO:
   1888  * @iowrite:  an I/O write function
   1889  * @ioclose:  an I/O close function
   1890  * @ioctx:  an I/O handler
   1891  * @encoding:  the encoding name to use or NULL
   1892  * @options:  a set of xmlSaveOptions
   1893  *
   1894  * Create a document saving context serializing to a file descriptor
   1895  * with the encoding and the options given
   1896  *
   1897  * Returns a new serialization context or NULL in case of error.
   1898  */
   1899 xmlSaveCtxtPtr
   1900 xmlSaveToIO(xmlOutputWriteCallback iowrite,
   1901             xmlOutputCloseCallback ioclose,
   1902             void *ioctx, const char *encoding, int options)
   1903 {
   1904     xmlSaveCtxtPtr ret;
   1905 
   1906     ret = xmlNewSaveCtxt(encoding, options);
   1907     if (ret == NULL) return(NULL);
   1908     ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
   1909     if (ret->buf == NULL) {
   1910 	xmlFreeSaveCtxt(ret);
   1911 	return(NULL);
   1912     }
   1913     return(ret);
   1914 }
   1915 
   1916 /**
   1917  * xmlSaveDoc:
   1918  * @ctxt:  a document saving context
   1919  * @doc:  a document
   1920  *
   1921  * Save a full document to a saving context
   1922  * TODO: The function is not fully implemented yet as it does not return the
   1923  * byte count but 0 instead
   1924  *
   1925  * Returns the number of byte written or -1 in case of error
   1926  */
   1927 long
   1928 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
   1929 {
   1930     long ret = 0;
   1931 
   1932     if ((ctxt == NULL) || (doc == NULL)) return(-1);
   1933     if (xmlDocContentDumpOutput(ctxt, doc) < 0)
   1934         return(-1);
   1935     return(ret);
   1936 }
   1937 
   1938 /**
   1939  * xmlSaveTree:
   1940  * @ctxt:  a document saving context
   1941  * @node:  the top node of the subtree to save
   1942  *
   1943  * Save a subtree starting at the node parameter to a saving context
   1944  * TODO: The function is not fully implemented yet as it does not return the
   1945  * byte count but 0 instead
   1946  *
   1947  * Returns the number of byte written or -1 in case of error
   1948  */
   1949 long
   1950 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
   1951 {
   1952     long ret = 0;
   1953 
   1954     if ((ctxt == NULL) || (node == NULL)) return(-1);
   1955     xmlNodeDumpOutputInternal(ctxt, node);
   1956     return(ret);
   1957 }
   1958 
   1959 /**
   1960  * xmlSaveFlush:
   1961  * @ctxt:  a document saving context
   1962  *
   1963  * Flush a document saving context, i.e. make sure that all bytes have
   1964  * been output.
   1965  *
   1966  * Returns the number of byte written or -1 in case of error.
   1967  */
   1968 int
   1969 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
   1970 {
   1971     if (ctxt == NULL) return(-1);
   1972     if (ctxt->buf == NULL) return(-1);
   1973     return(xmlOutputBufferFlush(ctxt->buf));
   1974 }
   1975 
   1976 /**
   1977  * xmlSaveClose:
   1978  * @ctxt:  a document saving context
   1979  *
   1980  * Close a document saving context, i.e. make sure that all bytes have
   1981  * been output and free the associated data.
   1982  *
   1983  * Returns the number of byte written or -1 in case of error.
   1984  */
   1985 int
   1986 xmlSaveClose(xmlSaveCtxtPtr ctxt)
   1987 {
   1988     int ret;
   1989 
   1990     if (ctxt == NULL) return(-1);
   1991     ret = xmlSaveFlush(ctxt);
   1992     xmlFreeSaveCtxt(ctxt);
   1993     return(ret);
   1994 }
   1995 
   1996 /**
   1997  * xmlSaveSetEscape:
   1998  * @ctxt:  a document saving context
   1999  * @escape:  the escaping function
   2000  *
   2001  * Set a custom escaping function to be used for text in element content
   2002  *
   2003  * Returns 0 if successful or -1 in case of error.
   2004  */
   2005 int
   2006 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
   2007 {
   2008     if (ctxt == NULL) return(-1);
   2009     ctxt->escape = escape;
   2010     return(0);
   2011 }
   2012 
   2013 /**
   2014  * xmlSaveSetAttrEscape:
   2015  * @ctxt:  a document saving context
   2016  * @escape:  the escaping function
   2017  *
   2018  * Set a custom escaping function to be used for text in attribute content
   2019  *
   2020  * Returns 0 if successful or -1 in case of error.
   2021  */
   2022 int
   2023 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
   2024 {
   2025     if (ctxt == NULL) return(-1);
   2026     ctxt->escapeAttr = escape;
   2027     return(0);
   2028 }
   2029 
   2030 /************************************************************************
   2031  *									*
   2032  *		Public entry points based on buffers			*
   2033  *									*
   2034  ************************************************************************/
   2035 
   2036 /**
   2037  * xmlBufAttrSerializeTxtContent:
   2038  * @buf:  and xmlBufPtr output
   2039  * @doc:  the document
   2040  * @attr: the attribute node
   2041  * @string: the text content
   2042  *
   2043  * Serialize text attribute values to an xmlBufPtr
   2044  */
   2045 void
   2046 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
   2047                               xmlAttrPtr attr, const xmlChar * string)
   2048 {
   2049     xmlChar *base, *cur;
   2050 
   2051     if (string == NULL)
   2052         return;
   2053     base = cur = (xmlChar *) string;
   2054     while (*cur != 0) {
   2055         if (*cur == '\n') {
   2056             if (base != cur)
   2057                 xmlBufAdd(buf, base, cur - base);
   2058             xmlBufAdd(buf, BAD_CAST "&#10;", 5);
   2059             cur++;
   2060             base = cur;
   2061         } else if (*cur == '\r') {
   2062             if (base != cur)
   2063                 xmlBufAdd(buf, base, cur - base);
   2064             xmlBufAdd(buf, BAD_CAST "&#13;", 5);
   2065             cur++;
   2066             base = cur;
   2067         } else if (*cur == '\t') {
   2068             if (base != cur)
   2069                 xmlBufAdd(buf, base, cur - base);
   2070             xmlBufAdd(buf, BAD_CAST "&#9;", 4);
   2071             cur++;
   2072             base = cur;
   2073         } else if (*cur == '"') {
   2074             if (base != cur)
   2075                 xmlBufAdd(buf, base, cur - base);
   2076             xmlBufAdd(buf, BAD_CAST "&quot;", 6);
   2077             cur++;
   2078             base = cur;
   2079         } else if (*cur == '<') {
   2080             if (base != cur)
   2081                 xmlBufAdd(buf, base, cur - base);
   2082             xmlBufAdd(buf, BAD_CAST "&lt;", 4);
   2083             cur++;
   2084             base = cur;
   2085         } else if (*cur == '>') {
   2086             if (base != cur)
   2087                 xmlBufAdd(buf, base, cur - base);
   2088             xmlBufAdd(buf, BAD_CAST "&gt;", 4);
   2089             cur++;
   2090             base = cur;
   2091         } else if (*cur == '&') {
   2092             if (base != cur)
   2093                 xmlBufAdd(buf, base, cur - base);
   2094             xmlBufAdd(buf, BAD_CAST "&amp;", 5);
   2095             cur++;
   2096             base = cur;
   2097         } else if ((*cur >= 0x80) && (cur[1] != 0) &&
   2098 	           ((doc == NULL) || (doc->encoding == NULL))) {
   2099             /*
   2100              * We assume we have UTF-8 content.
   2101              */
   2102             unsigned char tmp[12];
   2103             int val = 0, l = 1;
   2104 
   2105             if (base != cur)
   2106                 xmlBufAdd(buf, base, cur - base);
   2107             if (*cur < 0xC0) {
   2108                 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
   2109 		xmlSerializeHexCharRef(tmp, *cur);
   2110                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2111                 cur++;
   2112                 base = cur;
   2113                 continue;
   2114             } else if (*cur < 0xE0) {
   2115                 val = (cur[0]) & 0x1F;
   2116                 val <<= 6;
   2117                 val |= (cur[1]) & 0x3F;
   2118                 l = 2;
   2119             } else if ((*cur < 0xF0) && (cur [2] != 0)) {
   2120                 val = (cur[0]) & 0x0F;
   2121                 val <<= 6;
   2122                 val |= (cur[1]) & 0x3F;
   2123                 val <<= 6;
   2124                 val |= (cur[2]) & 0x3F;
   2125                 l = 3;
   2126             } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
   2127                 val = (cur[0]) & 0x07;
   2128                 val <<= 6;
   2129                 val |= (cur[1]) & 0x3F;
   2130                 val <<= 6;
   2131                 val |= (cur[2]) & 0x3F;
   2132                 val <<= 6;
   2133                 val |= (cur[3]) & 0x3F;
   2134                 l = 4;
   2135             }
   2136             if ((l == 1) || (!IS_CHAR(val))) {
   2137                 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
   2138 		xmlSerializeHexCharRef(tmp, *cur);
   2139                 xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2140                 cur++;
   2141                 base = cur;
   2142                 continue;
   2143             }
   2144             /*
   2145              * We could do multiple things here. Just save
   2146              * as a char ref
   2147              */
   2148 	    xmlSerializeHexCharRef(tmp, val);
   2149             xmlBufAdd(buf, (xmlChar *) tmp, -1);
   2150             cur += l;
   2151             base = cur;
   2152         } else {
   2153             cur++;
   2154         }
   2155     }
   2156     if (base != cur)
   2157         xmlBufAdd(buf, base, cur - base);
   2158 }
   2159 
   2160 /**
   2161  * xmlAttrSerializeTxtContent:
   2162  * @buf:  the XML buffer output
   2163  * @doc:  the document
   2164  * @attr: the attribute node
   2165  * @string: the text content
   2166  *
   2167  * Serialize text attribute values to an xml simple buffer
   2168  */
   2169 void
   2170 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
   2171                            xmlAttrPtr attr, const xmlChar * string)
   2172 {
   2173     xmlBufPtr buffer;
   2174 
   2175     if ((buf == NULL) || (string == NULL))
   2176         return;
   2177     buffer = xmlBufFromBuffer(buf);
   2178     if (buffer == NULL)
   2179         return;
   2180     xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
   2181     xmlBufBackToBuffer(buffer);
   2182 }
   2183 
   2184 /**
   2185  * xmlNodeDump:
   2186  * @buf:  the XML buffer output
   2187  * @doc:  the document
   2188  * @cur:  the current node
   2189  * @level: the imbrication level for indenting
   2190  * @format: is formatting allowed
   2191  *
   2192  * Dump an XML node, recursive behaviour,children are printed too.
   2193  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2194  * or xmlKeepBlanksDefault(0) was called
   2195  * Since this is using xmlBuffer structures it is limited to 2GB and somehow
   2196  * deprecated, use xmlBufNodeDump() instead.
   2197  *
   2198  * Returns the number of bytes written to the buffer or -1 in case of error
   2199  */
   2200 int
   2201 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
   2202             int format)
   2203 {
   2204     xmlBufPtr buffer;
   2205     int ret;
   2206 
   2207     if ((buf == NULL) || (cur == NULL))
   2208         return(-1);
   2209     buffer = xmlBufFromBuffer(buf);
   2210     if (buffer == NULL)
   2211         return(-1);
   2212     ret = xmlBufNodeDump(buffer, doc, cur, level, format);
   2213     xmlBufBackToBuffer(buffer);
   2214     if (ret > INT_MAX)
   2215         return(-1);
   2216     return((int) ret);
   2217 }
   2218 
   2219 /**
   2220  * xmlBufNodeDump:
   2221  * @buf:  the XML buffer output
   2222  * @doc:  the document
   2223  * @cur:  the current node
   2224  * @level: the imbrication level for indenting
   2225  * @format: is formatting allowed
   2226  *
   2227  * Dump an XML node, recursive behaviour,children are printed too.
   2228  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2229  * or xmlKeepBlanksDefault(0) was called
   2230  *
   2231  * Returns the number of bytes written to the buffer, in case of error 0
   2232  *     is returned or @buf stores the error
   2233  */
   2234 
   2235 size_t
   2236 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
   2237             int format)
   2238 {
   2239     size_t use;
   2240     int ret;
   2241     xmlOutputBufferPtr outbuf;
   2242     int oldalloc;
   2243 
   2244     xmlInitParser();
   2245 
   2246     if (cur == NULL) {
   2247 #ifdef DEBUG_TREE
   2248         xmlGenericError(xmlGenericErrorContext,
   2249                         "xmlNodeDump : node == NULL\n");
   2250 #endif
   2251         return (-1);
   2252     }
   2253     if (buf == NULL) {
   2254 #ifdef DEBUG_TREE
   2255         xmlGenericError(xmlGenericErrorContext,
   2256                         "xmlNodeDump : buf == NULL\n");
   2257 #endif
   2258         return (-1);
   2259     }
   2260     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
   2261     if (outbuf == NULL) {
   2262         xmlSaveErrMemory("creating buffer");
   2263         return (-1);
   2264     }
   2265     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
   2266     outbuf->buffer = buf;
   2267     outbuf->encoder = NULL;
   2268     outbuf->writecallback = NULL;
   2269     outbuf->closecallback = NULL;
   2270     outbuf->context = NULL;
   2271     outbuf->written = 0;
   2272 
   2273     use = xmlBufUse(buf);
   2274     oldalloc = xmlBufGetAllocationScheme(buf);
   2275     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
   2276     xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
   2277     xmlBufSetAllocationScheme(buf, oldalloc);
   2278     xmlFree(outbuf);
   2279     ret = xmlBufUse(buf) - use;
   2280     return (ret);
   2281 }
   2282 
   2283 /**
   2284  * xmlElemDump:
   2285  * @f:  the FILE * for the output
   2286  * @doc:  the document
   2287  * @cur:  the current node
   2288  *
   2289  * Dump an XML/HTML node, recursive behaviour, children are printed too.
   2290  */
   2291 void
   2292 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
   2293 {
   2294     xmlOutputBufferPtr outbuf;
   2295 
   2296     xmlInitParser();
   2297 
   2298     if (cur == NULL) {
   2299 #ifdef DEBUG_TREE
   2300         xmlGenericError(xmlGenericErrorContext,
   2301                         "xmlElemDump : cur == NULL\n");
   2302 #endif
   2303         return;
   2304     }
   2305 #ifdef DEBUG_TREE
   2306     if (doc == NULL) {
   2307         xmlGenericError(xmlGenericErrorContext,
   2308                         "xmlElemDump : doc == NULL\n");
   2309     }
   2310 #endif
   2311 
   2312     outbuf = xmlOutputBufferCreateFile(f, NULL);
   2313     if (outbuf == NULL)
   2314         return;
   2315     if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
   2316 #ifdef LIBXML_HTML_ENABLED
   2317         htmlNodeDumpOutput(outbuf, doc, cur, NULL);
   2318 #else
   2319 	xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
   2320 #endif /* LIBXML_HTML_ENABLED */
   2321     } else
   2322         xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
   2323     xmlOutputBufferClose(outbuf);
   2324 }
   2325 
   2326 /************************************************************************
   2327  *									*
   2328  *		Saving functions front-ends				*
   2329  *									*
   2330  ************************************************************************/
   2331 
   2332 /**
   2333  * xmlNodeDumpOutput:
   2334  * @buf:  the XML buffer output
   2335  * @doc:  the document
   2336  * @cur:  the current node
   2337  * @level: the imbrication level for indenting
   2338  * @format: is formatting allowed
   2339  * @encoding:  an optional encoding string
   2340  *
   2341  * Dump an XML node, recursive behaviour, children are printed too.
   2342  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2343  * or xmlKeepBlanksDefault(0) was called
   2344  */
   2345 void
   2346 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
   2347                   int level, int format, const char *encoding)
   2348 {
   2349     xmlSaveCtxt ctxt;
   2350 #ifdef LIBXML_HTML_ENABLED
   2351     xmlDtdPtr dtd;
   2352     int is_xhtml = 0;
   2353 #endif
   2354 
   2355     xmlInitParser();
   2356 
   2357     if ((buf == NULL) || (cur == NULL)) return;
   2358 
   2359     if (encoding == NULL)
   2360         encoding = "UTF-8";
   2361 
   2362     memset(&ctxt, 0, sizeof(ctxt));
   2363     ctxt.doc = doc;
   2364     ctxt.buf = buf;
   2365     ctxt.level = level;
   2366     ctxt.format = format ? 1 : 0;
   2367     ctxt.encoding = (const xmlChar *) encoding;
   2368     xmlSaveCtxtInit(&ctxt);
   2369     ctxt.options |= XML_SAVE_AS_XML;
   2370 
   2371 #ifdef LIBXML_HTML_ENABLED
   2372     dtd = xmlGetIntSubset(doc);
   2373     if (dtd != NULL) {
   2374 	is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
   2375 	if (is_xhtml < 0)
   2376 	    is_xhtml = 0;
   2377     }
   2378 
   2379     if (is_xhtml)
   2380         xhtmlNodeDumpOutput(&ctxt, cur);
   2381     else
   2382 #endif
   2383         xmlNodeDumpOutputInternal(&ctxt, cur);
   2384 }
   2385 
   2386 /**
   2387  * xmlDocDumpFormatMemoryEnc:
   2388  * @out_doc:  Document to generate XML text from
   2389  * @doc_txt_ptr:  Memory pointer for allocated XML text
   2390  * @doc_txt_len:  Length of the generated XML text
   2391  * @txt_encoding:  Character encoding to use when generating XML text
   2392  * @format:  should formatting spaces been added
   2393  *
   2394  * Dump the current DOM tree into memory using the character encoding specified
   2395  * by the caller.  Note it is up to the caller of this function to free the
   2396  * allocated memory with xmlFree().
   2397  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2398  * or xmlKeepBlanksDefault(0) was called
   2399  */
   2400 
   2401 void
   2402 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
   2403 		int * doc_txt_len, const char * txt_encoding,
   2404 		int format) {
   2405     xmlSaveCtxt ctxt;
   2406     int                         dummy = 0;
   2407     xmlOutputBufferPtr          out_buff = NULL;
   2408     xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
   2409 
   2410     if (doc_txt_len == NULL) {
   2411         doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
   2412     }
   2413 
   2414     if (doc_txt_ptr == NULL) {
   2415         *doc_txt_len = 0;
   2416         return;
   2417     }
   2418 
   2419     *doc_txt_ptr = NULL;
   2420     *doc_txt_len = 0;
   2421 
   2422     if (out_doc == NULL) {
   2423         /*  No document, no output  */
   2424         return;
   2425     }
   2426 
   2427     /*
   2428      *  Validate the encoding value, if provided.
   2429      *  This logic is copied from xmlSaveFileEnc.
   2430      */
   2431 
   2432     if (txt_encoding == NULL)
   2433 	txt_encoding = (const char *) out_doc->encoding;
   2434     if (txt_encoding != NULL) {
   2435 	conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
   2436 	if ( conv_hdlr == NULL ) {
   2437 	    xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
   2438 		       txt_encoding);
   2439 	    return;
   2440 	}
   2441     }
   2442 
   2443     if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
   2444         xmlSaveErrMemory("creating buffer");
   2445         return;
   2446     }
   2447 
   2448     memset(&ctxt, 0, sizeof(ctxt));
   2449     ctxt.doc = out_doc;
   2450     ctxt.buf = out_buff;
   2451     ctxt.level = 0;
   2452     ctxt.format = format ? 1 : 0;
   2453     ctxt.encoding = (const xmlChar *) txt_encoding;
   2454     xmlSaveCtxtInit(&ctxt);
   2455     ctxt.options |= XML_SAVE_AS_XML;
   2456     xmlDocContentDumpOutput(&ctxt, out_doc);
   2457     xmlOutputBufferFlush(out_buff);
   2458     if (out_buff->conv != NULL) {
   2459 	*doc_txt_len = xmlBufUse(out_buff->conv);
   2460 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
   2461     } else {
   2462 	*doc_txt_len = xmlBufUse(out_buff->buffer);
   2463 	*doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
   2464     }
   2465     (void)xmlOutputBufferClose(out_buff);
   2466 
   2467     if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
   2468         *doc_txt_len = 0;
   2469         xmlSaveErrMemory("creating output");
   2470     }
   2471 
   2472     return;
   2473 }
   2474 
   2475 /**
   2476  * xmlDocDumpMemory:
   2477  * @cur:  the document
   2478  * @mem:  OUT: the memory pointer
   2479  * @size:  OUT: the memory length
   2480  *
   2481  * Dump an XML document in memory and return the #xmlChar * and it's size
   2482  * in bytes. It's up to the caller to free the memory with xmlFree().
   2483  * The resulting byte array is zero terminated, though the last 0 is not
   2484  * included in the returned size.
   2485  */
   2486 void
   2487 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
   2488     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
   2489 }
   2490 
   2491 /**
   2492  * xmlDocDumpFormatMemory:
   2493  * @cur:  the document
   2494  * @mem:  OUT: the memory pointer
   2495  * @size:  OUT: the memory length
   2496  * @format:  should formatting spaces been added
   2497  *
   2498  *
   2499  * Dump an XML document in memory and return the #xmlChar * and it's size.
   2500  * It's up to the caller to free the memory with xmlFree().
   2501  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2502  * or xmlKeepBlanksDefault(0) was called
   2503  */
   2504 void
   2505 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
   2506     xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
   2507 }
   2508 
   2509 /**
   2510  * xmlDocDumpMemoryEnc:
   2511  * @out_doc:  Document to generate XML text from
   2512  * @doc_txt_ptr:  Memory pointer for allocated XML text
   2513  * @doc_txt_len:  Length of the generated XML text
   2514  * @txt_encoding:  Character encoding to use when generating XML text
   2515  *
   2516  * Dump the current DOM tree into memory using the character encoding specified
   2517  * by the caller.  Note it is up to the caller of this function to free the
   2518  * allocated memory with xmlFree().
   2519  */
   2520 
   2521 void
   2522 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
   2523 	            int * doc_txt_len, const char * txt_encoding) {
   2524     xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
   2525 	                      txt_encoding, 0);
   2526 }
   2527 
   2528 /**
   2529  * xmlDocFormatDump:
   2530  * @f:  the FILE*
   2531  * @cur:  the document
   2532  * @format: should formatting spaces been added
   2533  *
   2534  * Dump an XML document to an open FILE.
   2535  *
   2536  * returns: the number of bytes written or -1 in case of failure.
   2537  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2538  * or xmlKeepBlanksDefault(0) was called
   2539  */
   2540 int
   2541 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
   2542     xmlSaveCtxt ctxt;
   2543     xmlOutputBufferPtr buf;
   2544     const char * encoding;
   2545     xmlCharEncodingHandlerPtr handler = NULL;
   2546     int ret;
   2547 
   2548     if (cur == NULL) {
   2549 #ifdef DEBUG_TREE
   2550         xmlGenericError(xmlGenericErrorContext,
   2551 		"xmlDocDump : document == NULL\n");
   2552 #endif
   2553 	return(-1);
   2554     }
   2555     encoding = (const char *) cur->encoding;
   2556 
   2557     if (encoding != NULL) {
   2558 	handler = xmlFindCharEncodingHandler(encoding);
   2559 	if (handler == NULL) {
   2560 	    xmlFree((char *) cur->encoding);
   2561 	    cur->encoding = NULL;
   2562 	    encoding = NULL;
   2563 	}
   2564     }
   2565     buf = xmlOutputBufferCreateFile(f, handler);
   2566     if (buf == NULL) return(-1);
   2567     memset(&ctxt, 0, sizeof(ctxt));
   2568     ctxt.doc = cur;
   2569     ctxt.buf = buf;
   2570     ctxt.level = 0;
   2571     ctxt.format = format ? 1 : 0;
   2572     ctxt.encoding = (const xmlChar *) encoding;
   2573     xmlSaveCtxtInit(&ctxt);
   2574     ctxt.options |= XML_SAVE_AS_XML;
   2575     xmlDocContentDumpOutput(&ctxt, cur);
   2576 
   2577     ret = xmlOutputBufferClose(buf);
   2578     return(ret);
   2579 }
   2580 
   2581 /**
   2582  * xmlDocDump:
   2583  * @f:  the FILE*
   2584  * @cur:  the document
   2585  *
   2586  * Dump an XML document to an open FILE.
   2587  *
   2588  * returns: the number of bytes written or -1 in case of failure.
   2589  */
   2590 int
   2591 xmlDocDump(FILE *f, xmlDocPtr cur) {
   2592     return(xmlDocFormatDump (f, cur, 0));
   2593 }
   2594 
   2595 /**
   2596  * xmlSaveFileTo:
   2597  * @buf:  an output I/O buffer
   2598  * @cur:  the document
   2599  * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
   2600  *
   2601  * Dump an XML document to an I/O buffer.
   2602  * Warning ! This call xmlOutputBufferClose() on buf which is not available
   2603  * after this call.
   2604  *
   2605  * returns: the number of bytes written or -1 in case of failure.
   2606  */
   2607 int
   2608 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
   2609     xmlSaveCtxt ctxt;
   2610     int ret;
   2611 
   2612     if (buf == NULL) return(-1);
   2613     if (cur == NULL) {
   2614         xmlOutputBufferClose(buf);
   2615 	return(-1);
   2616     }
   2617     memset(&ctxt, 0, sizeof(ctxt));
   2618     ctxt.doc = cur;
   2619     ctxt.buf = buf;
   2620     ctxt.level = 0;
   2621     ctxt.format = 0;
   2622     ctxt.encoding = (const xmlChar *) encoding;
   2623     xmlSaveCtxtInit(&ctxt);
   2624     ctxt.options |= XML_SAVE_AS_XML;
   2625     xmlDocContentDumpOutput(&ctxt, cur);
   2626     ret = xmlOutputBufferClose(buf);
   2627     return(ret);
   2628 }
   2629 
   2630 /**
   2631  * xmlSaveFormatFileTo:
   2632  * @buf:  an output I/O buffer
   2633  * @cur:  the document
   2634  * @encoding:  the encoding if any assuming the I/O layer handles the trancoding
   2635  * @format: should formatting spaces been added
   2636  *
   2637  * Dump an XML document to an I/O buffer.
   2638  * Warning ! This call xmlOutputBufferClose() on buf which is not available
   2639  * after this call.
   2640  *
   2641  * returns: the number of bytes written or -1 in case of failure.
   2642  */
   2643 int
   2644 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
   2645                     const char *encoding, int format)
   2646 {
   2647     xmlSaveCtxt ctxt;
   2648     int ret;
   2649 
   2650     if (buf == NULL) return(-1);
   2651     if ((cur == NULL) ||
   2652         ((cur->type != XML_DOCUMENT_NODE) &&
   2653 	 (cur->type != XML_HTML_DOCUMENT_NODE))) {
   2654         xmlOutputBufferClose(buf);
   2655 	return(-1);
   2656     }
   2657     memset(&ctxt, 0, sizeof(ctxt));
   2658     ctxt.doc = cur;
   2659     ctxt.buf = buf;
   2660     ctxt.level = 0;
   2661     ctxt.format = format ? 1 : 0;
   2662     ctxt.encoding = (const xmlChar *) encoding;
   2663     xmlSaveCtxtInit(&ctxt);
   2664     ctxt.options |= XML_SAVE_AS_XML;
   2665     xmlDocContentDumpOutput(&ctxt, cur);
   2666     ret = xmlOutputBufferClose(buf);
   2667     return (ret);
   2668 }
   2669 
   2670 /**
   2671  * xmlSaveFormatFileEnc:
   2672  * @filename:  the filename or URL to output
   2673  * @cur:  the document being saved
   2674  * @encoding:  the name of the encoding to use or NULL.
   2675  * @format:  should formatting spaces be added.
   2676  *
   2677  * Dump an XML document to a file or an URL.
   2678  *
   2679  * Returns the number of bytes written or -1 in case of error.
   2680  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2681  * or xmlKeepBlanksDefault(0) was called
   2682  */
   2683 int
   2684 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
   2685 			const char * encoding, int format ) {
   2686     xmlSaveCtxt ctxt;
   2687     xmlOutputBufferPtr buf;
   2688     xmlCharEncodingHandlerPtr handler = NULL;
   2689     int ret;
   2690 
   2691     if (cur == NULL)
   2692 	return(-1);
   2693 
   2694     if (encoding == NULL)
   2695 	encoding = (const char *) cur->encoding;
   2696 
   2697     if (encoding != NULL) {
   2698 
   2699 	    handler = xmlFindCharEncodingHandler(encoding);
   2700 	    if (handler == NULL)
   2701 		return(-1);
   2702     }
   2703 
   2704 #ifdef LIBXML_ZLIB_ENABLED
   2705     if (cur->compression < 0) cur->compression = xmlGetCompressMode();
   2706 #endif
   2707     /*
   2708      * save the content to a temp buffer.
   2709      */
   2710     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
   2711     if (buf == NULL) return(-1);
   2712     memset(&ctxt, 0, sizeof(ctxt));
   2713     ctxt.doc = cur;
   2714     ctxt.buf = buf;
   2715     ctxt.level = 0;
   2716     ctxt.format = format ? 1 : 0;
   2717     ctxt.encoding = (const xmlChar *) encoding;
   2718     xmlSaveCtxtInit(&ctxt);
   2719     ctxt.options |= XML_SAVE_AS_XML;
   2720 
   2721     xmlDocContentDumpOutput(&ctxt, cur);
   2722 
   2723     ret = xmlOutputBufferClose(buf);
   2724     return(ret);
   2725 }
   2726 
   2727 
   2728 /**
   2729  * xmlSaveFileEnc:
   2730  * @filename:  the filename (or URL)
   2731  * @cur:  the document
   2732  * @encoding:  the name of an encoding (or NULL)
   2733  *
   2734  * Dump an XML document, converting it to the given encoding
   2735  *
   2736  * returns: the number of bytes written or -1 in case of failure.
   2737  */
   2738 int
   2739 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
   2740     return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
   2741 }
   2742 
   2743 /**
   2744  * xmlSaveFormatFile:
   2745  * @filename:  the filename (or URL)
   2746  * @cur:  the document
   2747  * @format:  should formatting spaces been added
   2748  *
   2749  * Dump an XML document to a file. Will use compression if
   2750  * compiled in and enabled. If @filename is "-" the stdout file is
   2751  * used. If @format is set then the document will be indented on output.
   2752  * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
   2753  * or xmlKeepBlanksDefault(0) was called
   2754  *
   2755  * returns: the number of bytes written or -1 in case of failure.
   2756  */
   2757 int
   2758 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
   2759     return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
   2760 }
   2761 
   2762 /**
   2763  * xmlSaveFile:
   2764  * @filename:  the filename (or URL)
   2765  * @cur:  the document
   2766  *
   2767  * Dump an XML document to a file. Will use compression if
   2768  * compiled in and enabled. If @filename is "-" the stdout file is
   2769  * used.
   2770  * returns: the number of bytes written or -1 in case of failure.
   2771  */
   2772 int
   2773 xmlSaveFile(const char *filename, xmlDocPtr cur) {
   2774     return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
   2775 }
   2776 
   2777 #endif /* LIBXML_OUTPUT_ENABLED */
   2778 
   2779 #define bottom_xmlsave
   2780 #include "elfgcchack.h"
   2781