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