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