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