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