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