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