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