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