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