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