added Info.plist
[TestXSLT.git] / libxml2 / HTMLtree.c
1 /*
2  * HTMLtree.c : implementation of access function for an HTML tree.
3  *
4  * See Copyright for the status of this software.
5  *
6  * daniel@veillard.com
7  */
8
9
10 #define IN_LIBXML
11 #include "libxml.h"
12 #ifdef LIBXML_HTML_ENABLED
13
14 #include <string.h> /* for memset() only ! */
15
16 #ifdef HAVE_CTYPE_H
17 #include <ctype.h>
18 #endif
19 #ifdef HAVE_STDLIB_H
20 #include <stdlib.h>
21 #endif
22
23 #include <libxml/xmlmemory.h>
24 #include <libxml/HTMLparser.h>
25 #include <libxml/HTMLtree.h>
26 #include <libxml/entities.h>
27 #include <libxml/valid.h>
28 #include <libxml/xmlerror.h>
29 #include <libxml/parserInternals.h>
30 #include <libxml/globals.h>
31 #include <libxml/uri.h>
32
33 /************************************************************************
34  *                                                                      *
35  *              Getting/Setting encoding meta tags                      *
36  *                                                                      *
37  ************************************************************************/
38
39 /**
40  * htmlGetMetaEncoding:
41  * @doc:  the document
42  * 
43  * Encoding definition lookup in the Meta tags
44  *
45  * Returns the current encoding as flagged in the HTML source
46  */
47 const xmlChar *
48 htmlGetMetaEncoding(htmlDocPtr doc) {
49     htmlNodePtr cur;
50     const xmlChar *content;
51     const xmlChar *encoding;
52
53     if (doc == NULL)
54         return(NULL);
55     cur = doc->children;
56
57     /*
58      * Search the html
59      */
60     while (cur != NULL) {
61         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
62             if (xmlStrEqual(cur->name, BAD_CAST"html"))
63                 break;
64             if (xmlStrEqual(cur->name, BAD_CAST"head"))
65                 goto found_head;
66             if (xmlStrEqual(cur->name, BAD_CAST"meta"))
67                 goto found_meta;
68         }
69         cur = cur->next;
70     }
71     if (cur == NULL)
72         return(NULL);
73     cur = cur->children;
74
75     /*
76      * Search the head
77      */
78     while (cur != NULL) {
79         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
80             if (xmlStrEqual(cur->name, BAD_CAST"head"))
81                 break;
82             if (xmlStrEqual(cur->name, BAD_CAST"meta"))
83                 goto found_meta;
84         }
85         cur = cur->next;
86     }
87     if (cur == NULL)
88         return(NULL);
89 found_head:
90     cur = cur->children;
91
92     /*
93      * Search the meta elements
94      */
95 found_meta:
96     while (cur != NULL) {
97         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
98             if (xmlStrEqual(cur->name, BAD_CAST"meta")) {
99                 xmlAttrPtr attr = cur->properties;
100                 int http;
101                 const xmlChar *value;
102
103                 content = NULL;
104                 http = 0;
105                 while (attr != NULL) {
106                     if ((attr->children != NULL) &&
107                         (attr->children->type == XML_TEXT_NODE) &&
108                         (attr->children->next == NULL)) {
109                         value = attr->children->content;
110                         if ((!xmlStrcasecmp(attr->name, BAD_CAST"http-equiv"))
111                          && (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
112                             http = 1;
113                         else if ((value != NULL)
114                          && (!xmlStrcasecmp(attr->name, BAD_CAST"content")))
115                             content = value;
116                         if ((http != 0) && (content != NULL))
117                             goto found_content;
118                     }
119                     attr = attr->next;
120                 }
121             }
122         }
123         cur = cur->next;
124     }
125     return(NULL);
126
127 found_content:
128     encoding = xmlStrstr(content, BAD_CAST"charset=");
129     if (encoding == NULL) 
130         encoding = xmlStrstr(content, BAD_CAST"Charset=");
131     if (encoding == NULL) 
132         encoding = xmlStrstr(content, BAD_CAST"CHARSET=");
133     if (encoding != NULL) {
134         encoding += 8;
135     } else {
136         encoding = xmlStrstr(content, BAD_CAST"charset =");
137         if (encoding == NULL) 
138             encoding = xmlStrstr(content, BAD_CAST"Charset =");
139         if (encoding == NULL) 
140             encoding = xmlStrstr(content, BAD_CAST"CHARSET =");
141         if (encoding != NULL)
142             encoding += 9;
143     }
144     if (encoding != NULL) {
145         while ((*encoding == ' ') || (*encoding == '\t')) encoding++;
146     }
147     return(encoding);
148 }
149
150 /**
151  * htmlSetMetaEncoding:
152  * @doc:  the document
153  * @encoding:  the encoding string
154  * 
155  * Sets the current encoding in the Meta tags
156  * NOTE: this will not change the document content encoding, just
157  * the META flag associated.
158  *
159  * Returns 0 in case of success and -1 in case of error
160  */
161 int
162 htmlSetMetaEncoding(htmlDocPtr doc, const xmlChar *encoding) {
163     htmlNodePtr cur, meta;
164     const xmlChar *content;
165     char newcontent[100];
166
167
168     if (doc == NULL)
169         return(-1);
170
171     if (encoding != NULL) {
172         snprintf(newcontent, sizeof(newcontent), "text/html; charset=%s",
173                 encoding);
174         newcontent[sizeof(newcontent) - 1] = 0;
175     }
176
177     cur = doc->children;
178
179     /*
180      * Search the html
181      */
182     while (cur != NULL) {
183         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
184             if (xmlStrcasecmp(cur->name, BAD_CAST"html") == 0)
185                 break;
186             if (xmlStrcasecmp(cur->name, BAD_CAST"head") == 0)
187                 goto found_head;
188             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0)
189                 goto found_meta;
190         }
191         cur = cur->next;
192     }
193     if (cur == NULL)
194         return(-1);
195     cur = cur->children;
196
197     /*
198      * Search the head
199      */
200     while (cur != NULL) {
201         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
202             if (xmlStrcasecmp(cur->name, BAD_CAST"head") == 0)
203                 break;
204             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0)
205                 goto found_meta;
206         }
207         cur = cur->next;
208     }
209     if (cur == NULL)
210         return(-1);
211 found_head:
212     if (cur->children == NULL) {
213         if (encoding == NULL)
214             return(0);
215         meta = xmlNewDocNode(doc, NULL, BAD_CAST"meta", NULL);
216         xmlAddChild(cur, meta);
217         xmlNewProp(meta, BAD_CAST"http-equiv", BAD_CAST"Content-Type");
218         xmlNewProp(meta, BAD_CAST"content", BAD_CAST newcontent);
219         return(0);
220     }
221     cur = cur->children;
222
223 found_meta:
224     if (encoding != NULL) {
225         /*
226          * Create a new Meta element with the right attributes
227          */
228
229         meta = xmlNewDocNode(doc, NULL, BAD_CAST"meta", NULL);
230         xmlAddPrevSibling(cur, meta);
231         xmlNewProp(meta, BAD_CAST"http-equiv", BAD_CAST"Content-Type");
232         xmlNewProp(meta, BAD_CAST"content", BAD_CAST newcontent);
233     }
234
235     /*
236      * Search and destroy all the remaining the meta elements carrying
237      * encoding informations
238      */
239     while (cur != NULL) {
240         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
241             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0) {
242                 xmlAttrPtr attr = cur->properties;
243                 int http;
244                 const xmlChar *value;
245
246                 content = NULL;
247                 http = 0;
248                 while (attr != NULL) {
249                     if ((attr->children != NULL) &&
250                         (attr->children->type == XML_TEXT_NODE) &&
251                         (attr->children->next == NULL)) {
252                         value = attr->children->content;
253                         if ((!xmlStrcasecmp(attr->name, BAD_CAST"http-equiv"))
254                          && (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
255                             http = 1;
256                         else 
257                         {
258                            if ((value != NULL) && 
259                                 (!xmlStrcasecmp(attr->name, BAD_CAST"content")))
260                               content = value;
261                         }
262                         if ((http != 0) && (content != NULL))
263                             break;
264                     }
265                     attr = attr->next;
266                 }
267                 if ((http != 0) && (content != NULL)) {
268                     meta = cur;
269                     cur = cur->next;
270                     xmlUnlinkNode(meta);
271                     xmlFreeNode(meta);
272                     continue;
273                 }
274
275             }
276         }
277         cur = cur->next;
278     }
279     return(0);
280 }
281
282 /**
283  * booleanHTMLAttrs:
284  *
285  * These are the HTML attributes which will be output
286  * in minimized form, i.e. <option selected="selected"> will be
287  * output as <option selected>, as per XSLT 1.0 16.2 "HTML Output Method"
288  *
289  */
290 static const char* htmlBooleanAttrs[] = {
291   "checked", "compact", "declare", "defer", "disabled", "ismap",
292   "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly",
293   "selected", NULL
294 };
295
296
297 /**
298  * htmlIsBooleanAttr:
299  * @name:  the name of the attribute to check
300  *
301  * Determine if a given attribute is a boolean attribute.
302  * 
303  * returns: false if the attribute is not boolean, true otherwise.
304  */
305 int
306 htmlIsBooleanAttr(const xmlChar *name)
307 {
308     int i = 0;
309
310     while (htmlBooleanAttrs[i] != NULL) {
311         if (xmlStrcasecmp((const xmlChar *)htmlBooleanAttrs[i], name) == 0)
312             return 1;
313         i++;
314     }
315     return 0;
316 }
317
318 /************************************************************************
319  *                                                                      *
320  *              Dumping HTML tree content to a simple buffer            *
321  *                                                                      *
322  ************************************************************************/
323
324 static int
325 htmlNodeDumpFormat(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
326                    int format);
327
328 /**
329  * htmlNodeDumpFormat:
330  * @buf:  the HTML buffer output
331  * @doc:  the document
332  * @cur:  the current node
333  * @format:  should formatting spaces been added
334  *
335  * Dump an HTML node, recursive behaviour,children are printed too.
336  *
337  * Returns the number of byte written or -1 in case of error
338  */
339 static int
340 htmlNodeDumpFormat(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
341                    int format) {
342     unsigned int use;
343     int ret;
344     xmlOutputBufferPtr outbuf;
345
346     if (cur == NULL) {
347         return (-1);
348     }
349     if (buf == NULL) {
350         return (-1);
351     }
352     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
353     if (outbuf == NULL) {
354         xmlGenericError(xmlGenericErrorContext,
355                         "htmlNodeDumpFormat: out of memory!\n");
356         return (-1);
357     }
358     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
359     outbuf->buffer = buf;
360     outbuf->encoder = NULL;
361     outbuf->writecallback = NULL;
362     outbuf->closecallback = NULL;
363     outbuf->context = NULL;
364     outbuf->written = 0;
365
366     use = buf->use;
367     htmlNodeDumpFormatOutput(outbuf, doc, cur, NULL, format);
368     xmlFree(outbuf);
369     ret = buf->use - use;
370     return (ret);
371 }
372
373 /**
374  * htmlNodeDump:
375  * @buf:  the HTML buffer output
376  * @doc:  the document
377  * @cur:  the current node
378  *
379  * Dump an HTML node, recursive behaviour,children are printed too,
380  * and formatting returns are added.
381  *
382  * Returns the number of byte written or -1 in case of error
383  */
384 int
385 htmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur) {
386     xmlInitParser();
387
388     return(htmlNodeDumpFormat(buf, doc, cur, 1));
389 }
390
391 /**
392  * htmlNodeDumpFileFormat:
393  * @out:  the FILE pointer
394  * @doc:  the document
395  * @cur:  the current node
396  * @encoding: the document encoding
397  * @format:  should formatting spaces been added
398  *
399  * Dump an HTML node, recursive behaviour,children are printed too.
400  *
401  * TODO: if encoding == NULL try to save in the doc encoding
402  *
403  * returns: the number of byte written or -1 in case of failure.
404  */
405 int
406 htmlNodeDumpFileFormat(FILE *out, xmlDocPtr doc,
407                        xmlNodePtr cur, const char *encoding, int format) {
408     xmlOutputBufferPtr buf;
409     xmlCharEncodingHandlerPtr handler = NULL;
410     int ret;
411
412     xmlInitParser();
413
414     if (encoding != NULL) {
415         xmlCharEncoding enc;
416
417         enc = xmlParseCharEncoding(encoding);
418         if (enc != XML_CHAR_ENCODING_UTF8) {
419             handler = xmlFindCharEncodingHandler(encoding);
420             if (handler == NULL)
421                 return(-1);
422         }
423     }
424
425     /*
426      * Fallback to HTML or ASCII when the encoding is unspecified
427      */
428     if (handler == NULL)
429         handler = xmlFindCharEncodingHandler("HTML");
430     if (handler == NULL)
431         handler = xmlFindCharEncodingHandler("ascii");
432
433     /* 
434      * save the content to a temp buffer.
435      */
436     buf = xmlOutputBufferCreateFile(out, handler);
437     if (buf == NULL) return(0);
438
439     htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
440
441     ret = xmlOutputBufferClose(buf);
442     return(ret);
443 }
444
445 /**
446  * htmlNodeDumpFile:
447  * @out:  the FILE pointer
448  * @doc:  the document
449  * @cur:  the current node
450  *
451  * Dump an HTML node, recursive behaviour,children are printed too,
452  * and formatting returns are added.
453  */
454 void
455 htmlNodeDumpFile(FILE *out, xmlDocPtr doc, xmlNodePtr cur) {
456     htmlNodeDumpFileFormat(out, doc, cur, NULL, 1);
457 }
458
459 /**
460  * htmlDocDumpMemory:
461  * @cur:  the document
462  * @mem:  OUT: the memory pointer
463  * @size:  OUT: the memory length
464  *
465  * Dump an HTML document in memory and return the xmlChar * and it's size.
466  * It's up to the caller to free the memory.
467  */
468 void
469 htmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
470     xmlOutputBufferPtr buf;
471     xmlCharEncodingHandlerPtr handler = NULL;
472     const char *encoding;
473
474     xmlInitParser();
475
476     if (cur == NULL) {
477 #ifdef DEBUG_TREE
478         xmlGenericError(xmlGenericErrorContext,
479                 "htmlDocDumpMemory : document == NULL\n");
480 #endif
481         *mem = NULL;
482         *size = 0;
483         return;
484     }
485
486     encoding = (const char *) htmlGetMetaEncoding(cur);
487
488     if (encoding != NULL) {
489         xmlCharEncoding enc;
490
491         enc = xmlParseCharEncoding(encoding);
492         if (enc != cur->charset) {
493             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
494                 /*
495                  * Not supported yet
496                  */
497                 *mem = NULL;
498                 *size = 0;
499                 return;
500             }
501
502             handler = xmlFindCharEncodingHandler(encoding);
503             if (handler == NULL) {
504                 *mem = NULL;
505                 *size = 0;
506                 return;
507             }
508         }
509     }
510
511     /*
512      * Fallback to HTML or ASCII when the encoding is unspecified
513      */
514     if (handler == NULL)
515         handler = xmlFindCharEncodingHandler("HTML");
516     if (handler == NULL)
517         handler = xmlFindCharEncodingHandler("ascii");
518
519     buf = xmlAllocOutputBuffer(handler);
520     if (buf == NULL) {
521         *mem = NULL;
522         *size = 0;
523         return;
524     }
525
526     htmlDocContentDumpOutput(buf, cur, NULL);
527     xmlOutputBufferFlush(buf);
528     if (buf->conv != NULL) {
529         *size = buf->conv->use;
530         *mem = xmlStrndup(buf->conv->content, *size);
531     } else {
532         *size = buf->buffer->use;
533         *mem = xmlStrndup(buf->buffer->content, *size);
534     }
535     (void)xmlOutputBufferClose(buf);
536 }
537
538
539 /************************************************************************
540  *                                                                      *
541  *              Dumping HTML tree content to an I/O output buffer       *
542  *                                                                      *
543  ************************************************************************/
544
545 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
546
547 /**
548  * htmlDtdDumpOutput:
549  * @buf:  the HTML buffer output
550  * @doc:  the document
551  * @encoding:  the encoding string
552  * 
553  * TODO: check whether encoding is needed
554  *
555  * Dump the HTML document DTD, if any.
556  */
557 static void
558 htmlDtdDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
559                   const char *encoding ATTRIBUTE_UNUSED) {
560     xmlDtdPtr cur = doc->intSubset;
561
562     if (cur == NULL) {
563         xmlGenericError(xmlGenericErrorContext,
564                 "htmlDtdDumpOutput : no internal subset\n");
565         return;
566     }
567     xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
568     xmlOutputBufferWriteString(buf, (const char *)cur->name);
569     if (cur->ExternalID != NULL) {
570         xmlOutputBufferWriteString(buf, " PUBLIC ");
571         xmlBufferWriteQuotedString(buf->buffer, cur->ExternalID);
572         if (cur->SystemID != NULL) {
573             xmlOutputBufferWriteString(buf, " ");
574             xmlBufferWriteQuotedString(buf->buffer, cur->SystemID);
575         } 
576     }  else if (cur->SystemID != NULL) {
577         xmlOutputBufferWriteString(buf, " SYSTEM ");
578         xmlBufferWriteQuotedString(buf->buffer, cur->SystemID);
579     }
580     xmlOutputBufferWriteString(buf, ">\n");
581 }
582
583 /**
584  * htmlAttrDumpOutput:
585  * @buf:  the HTML buffer output
586  * @doc:  the document
587  * @cur:  the attribute pointer
588  * @encoding:  the encoding string
589  *
590  * Dump an HTML attribute
591  */
592 static void
593 htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
594                    const char *encoding ATTRIBUTE_UNUSED) {
595     xmlChar *value;
596
597     /*
598      * TODO: The html output method should not escape a & character
599      *       occurring in an attribute value immediately followed by
600      *       a { character (see Section B.7.1 of the HTML 4.0 Recommendation).
601      */
602
603     if (cur == NULL) {
604         xmlGenericError(xmlGenericErrorContext,
605                 "htmlAttrDumpOutput : property == NULL\n");
606         return;
607     }
608     xmlOutputBufferWriteString(buf, " ");
609     xmlOutputBufferWriteString(buf, (const char *)cur->name);
610     if ((cur->children != NULL) && (!htmlIsBooleanAttr(cur->name))) {
611         value = xmlNodeListGetString(doc, cur->children, 0);
612         if (value) {
613             xmlOutputBufferWriteString(buf, "=");
614             if ((cur->ns == NULL) && (cur->parent != NULL) &&
615                 (cur->parent->ns == NULL) &&
616                 ((!xmlStrcasecmp(cur->name, BAD_CAST "href")) ||
617                  (!xmlStrcasecmp(cur->name, BAD_CAST "action")) ||
618                  (!xmlStrcasecmp(cur->name, BAD_CAST "src")))) {
619                 xmlChar *escaped;
620                 xmlChar *tmp = value;
621
622                 while (IS_BLANK(*tmp)) tmp++;
623
624                 escaped = xmlURIEscapeStr(tmp, BAD_CAST"@/:=?;#%&,+");
625                 if (escaped != NULL) {
626                     xmlBufferWriteQuotedString(buf->buffer, escaped);
627                     xmlFree(escaped);
628                 } else {
629                     xmlBufferWriteQuotedString(buf->buffer, value);
630                 }
631             } else {
632                 xmlBufferWriteQuotedString(buf->buffer, value);
633             }
634             xmlFree(value);
635         } else  {
636             xmlOutputBufferWriteString(buf, "=\"\"");
637         }
638     }
639 }
640
641 /**
642  * htmlAttrListDumpOutput:
643  * @buf:  the HTML buffer output
644  * @doc:  the document
645  * @cur:  the first attribute pointer
646  * @encoding:  the encoding string
647  *
648  * Dump a list of HTML attributes
649  */
650 static void
651 htmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur, const char *encoding) {
652     if (cur == NULL) {
653         xmlGenericError(xmlGenericErrorContext,
654                 "htmlAttrListDumpOutput : property == NULL\n");
655         return;
656     }
657     while (cur != NULL) {
658         htmlAttrDumpOutput(buf, doc, cur, encoding);
659         cur = cur->next;
660     }
661 }
662
663
664
665 /**
666  * htmlNodeListDumpOutput:
667  * @buf:  the HTML buffer output
668  * @doc:  the document
669  * @cur:  the first node
670  * @encoding:  the encoding string
671  * @format:  should formatting spaces been added
672  *
673  * Dump an HTML node list, recursive behaviour,children are printed too.
674  */
675 static void
676 htmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
677                        xmlNodePtr cur, const char *encoding, int format) {
678     if (cur == NULL) {
679         xmlGenericError(xmlGenericErrorContext,
680                 "htmlNodeListDumpOutput : node == NULL\n");
681         return;
682     }
683     while (cur != NULL) {
684         htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
685         cur = cur->next;
686     }
687 }
688
689 /**
690  * htmlNodeDumpFormatOutput:
691  * @buf:  the HTML buffer output
692  * @doc:  the document
693  * @cur:  the current node
694  * @encoding:  the encoding string
695  * @format:  should formatting spaces been added
696  *
697  * Dump an HTML node, recursive behaviour,children are printed too.
698  */
699 void
700 htmlNodeDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
701                          xmlNodePtr cur, const char *encoding, int format) {
702     const htmlElemDesc * info;
703
704     xmlInitParser();
705
706     if (cur == NULL) {
707         xmlGenericError(xmlGenericErrorContext,
708                 "htmlNodeDumpFormatOutput : node == NULL\n");
709         return;
710     }
711     /*
712      * Special cases.
713      */
714     if (cur->type == XML_DTD_NODE)
715         return;
716     if (cur->type == XML_HTML_DOCUMENT_NODE) {
717         htmlDocContentDumpOutput(buf, (xmlDocPtr) cur, encoding);
718         return;
719     }
720     if (cur->type == HTML_TEXT_NODE) {
721         if (cur->content != NULL) {
722             if (((cur->name == (const xmlChar *)xmlStringText) ||
723                  (cur->name != (const xmlChar *)xmlStringTextNoenc)) &&
724                 ((cur->parent == NULL) ||
725                  ((xmlStrcasecmp(cur->parent->name, BAD_CAST "script")) &&
726                   (xmlStrcasecmp(cur->parent->name, BAD_CAST "style"))))) {
727                 xmlChar *buffer;
728
729                 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
730                 if (buffer != NULL) {
731                     xmlOutputBufferWriteString(buf, (const char *)buffer);
732                     xmlFree(buffer);
733                 }
734             } else {
735                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
736             }
737         }
738         return;
739     }
740     if (cur->type == HTML_COMMENT_NODE) {
741         if (cur->content != NULL) {
742             xmlOutputBufferWriteString(buf, "<!--");
743             xmlOutputBufferWriteString(buf, (const char *)cur->content);
744             xmlOutputBufferWriteString(buf, "-->");
745         }
746         return;
747     }
748     if (cur->type == HTML_PI_NODE) {
749         if (cur->name == NULL)
750             return;
751         xmlOutputBufferWriteString(buf, "<?");
752         xmlOutputBufferWriteString(buf, (const char *)cur->name);
753         if (cur->content != NULL) {
754             xmlOutputBufferWriteString(buf, " ");
755             xmlOutputBufferWriteString(buf, (const char *)cur->content);
756         }
757         xmlOutputBufferWriteString(buf, ">");
758         return;
759     }
760     if (cur->type == HTML_ENTITY_REF_NODE) {
761         xmlOutputBufferWriteString(buf, "&");
762         xmlOutputBufferWriteString(buf, (const char *)cur->name);
763         xmlOutputBufferWriteString(buf, ";");
764         return;
765     }
766     if (cur->type == HTML_PRESERVE_NODE) {
767         if (cur->content != NULL) {
768             xmlOutputBufferWriteString(buf, (const char *)cur->content);
769         }
770         return;
771     }
772
773     /*
774      * Get specific HTML info for that node.
775      */
776     if (cur->ns == NULL)
777         info = htmlTagLookup(cur->name);
778     else
779         info = NULL;
780
781     xmlOutputBufferWriteString(buf, "<");
782     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
783         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
784         xmlOutputBufferWriteString(buf, ":");
785     }
786     xmlOutputBufferWriteString(buf, (const char *)cur->name);
787     if (cur->nsDef)
788         xmlNsListDumpOutput(buf, cur->nsDef);
789     if (cur->properties != NULL)
790         htmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
791
792     if ((info != NULL) && (info->empty)) {
793         xmlOutputBufferWriteString(buf, ">");
794         if ((format) && (!info->isinline) && (cur->next != NULL)) {
795             if ((cur->next->type != HTML_TEXT_NODE) &&
796                 (cur->next->type != HTML_ENTITY_REF_NODE) &&
797                 (cur->parent != NULL) &&
798                 (!xmlStrEqual(cur->parent->name, BAD_CAST "pre")))
799                 xmlOutputBufferWriteString(buf, "\n");
800         }
801         return;
802     }
803     if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
804         (cur->children == NULL)) {
805         if ((info != NULL) && (info->saveEndTag != 0) &&
806             (xmlStrcmp(BAD_CAST info->name, BAD_CAST "html")) &&
807             (xmlStrcmp(BAD_CAST info->name, BAD_CAST "body"))) {
808             xmlOutputBufferWriteString(buf, ">");
809         } else {
810             xmlOutputBufferWriteString(buf, "></");
811             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
812                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
813                 xmlOutputBufferWriteString(buf, ":");
814             }
815             xmlOutputBufferWriteString(buf, (const char *)cur->name);
816             xmlOutputBufferWriteString(buf, ">");
817         }
818         if ((format) && (cur->next != NULL) &&
819             (info != NULL) && (!info->isinline)) {
820             if ((cur->next->type != HTML_TEXT_NODE) &&
821                 (cur->next->type != HTML_ENTITY_REF_NODE) &&
822                 (cur->parent != NULL) &&
823                 (!xmlStrEqual(cur->parent->name, BAD_CAST "pre")))
824                 xmlOutputBufferWriteString(buf, "\n");
825         }
826         return;
827     }
828     xmlOutputBufferWriteString(buf, ">");
829     if ((cur->type != XML_ELEMENT_NODE) &&
830         (cur->content != NULL)) {
831             /*
832              * Uses the OutputBuffer property to automatically convert
833              * invalids to charrefs
834              */
835
836             xmlOutputBufferWriteString(buf, (const char *) cur->content);
837     }
838     if (cur->children != NULL) {
839         if ((format) && (info != NULL) && (!info->isinline) &&
840             (cur->children->type != HTML_TEXT_NODE) &&
841             (cur->children->type != HTML_ENTITY_REF_NODE) &&
842             (cur->children != cur->last) &&
843             (!xmlStrEqual(cur->name, BAD_CAST "pre")))
844             xmlOutputBufferWriteString(buf, "\n");
845         htmlNodeListDumpOutput(buf, doc, cur->children, encoding, format);
846         if ((format) && (info != NULL) && (!info->isinline) &&
847             (cur->last->type != HTML_TEXT_NODE) &&
848             (cur->last->type != HTML_ENTITY_REF_NODE) &&
849             (cur->children != cur->last) &&
850             (!xmlStrEqual(cur->name, BAD_CAST "pre")))
851             xmlOutputBufferWriteString(buf, "\n");
852     }
853     xmlOutputBufferWriteString(buf, "</");
854     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
855         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
856         xmlOutputBufferWriteString(buf, ":");
857     }
858     xmlOutputBufferWriteString(buf, (const char *)cur->name);
859     xmlOutputBufferWriteString(buf, ">");
860     if ((format) && (info != NULL) && (!info->isinline) &&
861         (cur->next != NULL)) {
862         if ((cur->next->type != HTML_TEXT_NODE) &&
863             (cur->next->type != HTML_ENTITY_REF_NODE) &&
864             (cur->parent != NULL) &&
865             (!xmlStrEqual(cur->parent->name, BAD_CAST "pre")))
866             xmlOutputBufferWriteString(buf, "\n");
867     }
868 }
869
870 /**
871  * htmlNodeDumpOutput:
872  * @buf:  the HTML buffer output
873  * @doc:  the document
874  * @cur:  the current node
875  * @encoding:  the encoding string
876  *
877  * Dump an HTML node, recursive behaviour,children are printed too,
878  * and formatting returns/spaces are added.
879  */
880 void
881 htmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
882                    xmlNodePtr cur, const char *encoding) {
883     htmlNodeDumpFormatOutput(buf, doc, cur, encoding, 1);
884 }
885
886 /**
887  * htmlDocContentDumpFormatOutput:
888  * @buf:  the HTML buffer output
889  * @cur:  the document
890  * @encoding:  the encoding string
891  * @format:  should formatting spaces been added
892  *
893  * Dump an HTML document.
894  */
895 void
896 htmlDocContentDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
897                                const char *encoding, int format) {
898     int type;
899
900     xmlInitParser();
901
902     /*
903      * force to output the stuff as HTML, especially for entities
904      */
905     type = cur->type;
906     cur->type = XML_HTML_DOCUMENT_NODE;
907     if (cur->intSubset != NULL) {
908         htmlDtdDumpOutput(buf, cur, NULL);
909     }
910     if (cur->children != NULL) {
911         htmlNodeListDumpOutput(buf, cur, cur->children, encoding, format);
912     }
913     xmlOutputBufferWriteString(buf, "\n");
914     cur->type = (xmlElementType) type;
915 }
916
917 /**
918  * htmlDocContentDumpOutput:
919  * @buf:  the HTML buffer output
920  * @cur:  the document
921  * @encoding:  the encoding string
922  *
923  * Dump an HTML document. Formating return/spaces are added.
924  */
925 void
926 htmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
927                          const char *encoding) {
928     htmlDocContentDumpFormatOutput(buf, cur, encoding, 1);
929 }
930
931 /************************************************************************
932  *                                                                      *
933  *              Saving functions front-ends                             *
934  *                                                                      *
935  ************************************************************************/
936
937 /**
938  * htmlDocDump:
939  * @f:  the FILE*
940  * @cur:  the document
941  *
942  * Dump an HTML document to an open FILE.
943  *
944  * returns: the number of byte written or -1 in case of failure.
945  */
946 int
947 htmlDocDump(FILE *f, xmlDocPtr cur) {
948     xmlOutputBufferPtr buf;
949     xmlCharEncodingHandlerPtr handler = NULL;
950     const char *encoding;
951     int ret;
952
953     xmlInitParser();
954
955     if (cur == NULL) {
956 #ifdef DEBUG_TREE
957         xmlGenericError(xmlGenericErrorContext,
958                 "htmlDocDump : document == NULL\n");
959 #endif
960         return(-1);
961     }
962
963     encoding = (const char *) htmlGetMetaEncoding(cur);
964
965     if (encoding != NULL) {
966         xmlCharEncoding enc;
967
968         enc = xmlParseCharEncoding(encoding);
969         if (enc != cur->charset) {
970             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
971                 /*
972                  * Not supported yet
973                  */
974                 return(-1);
975             }
976
977             handler = xmlFindCharEncodingHandler(encoding);
978             if (handler == NULL)
979                 return(-1);
980         }
981     }
982
983     /*
984      * Fallback to HTML or ASCII when the encoding is unspecified
985      */
986     if (handler == NULL)
987         handler = xmlFindCharEncodingHandler("HTML");
988     if (handler == NULL)
989         handler = xmlFindCharEncodingHandler("ascii");
990
991     buf = xmlOutputBufferCreateFile(f, handler);
992     if (buf == NULL) return(-1);
993     htmlDocContentDumpOutput(buf, cur, NULL);
994
995     ret = xmlOutputBufferClose(buf);
996     return(ret);
997 }
998
999 /**
1000  * htmlSaveFile:
1001  * @filename:  the filename (or URL)
1002  * @cur:  the document
1003  *
1004  * Dump an HTML document to a file. If @filename is "-" the stdout file is
1005  * used.
1006  * returns: the number of byte written or -1 in case of failure.
1007  */
1008 int
1009 htmlSaveFile(const char *filename, xmlDocPtr cur) {
1010     xmlOutputBufferPtr buf;
1011     xmlCharEncodingHandlerPtr handler = NULL;
1012     const char *encoding;
1013     int ret;
1014
1015     xmlInitParser();
1016
1017     encoding = (const char *) htmlGetMetaEncoding(cur);
1018
1019     if (encoding != NULL) {
1020         xmlCharEncoding enc;
1021
1022         enc = xmlParseCharEncoding(encoding);
1023         if (enc != cur->charset) {
1024             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1025                 /*
1026                  * Not supported yet
1027                  */
1028                 return(-1);
1029             }
1030
1031             handler = xmlFindCharEncodingHandler(encoding);
1032             if (handler == NULL)
1033                 return(-1);
1034         }
1035     }
1036
1037     /*
1038      * Fallback to HTML or ASCII when the encoding is unspecified
1039      */
1040     if (handler == NULL)
1041         handler = xmlFindCharEncodingHandler("HTML");
1042     if (handler == NULL)
1043         handler = xmlFindCharEncodingHandler("ascii");
1044
1045     /* 
1046      * save the content to a temp buffer.
1047      */
1048     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1049     if (buf == NULL) return(0);
1050
1051     htmlDocContentDumpOutput(buf, cur, NULL);
1052
1053     ret = xmlOutputBufferClose(buf);
1054     return(ret);
1055 }
1056
1057 /**
1058  * htmlSaveFileFormat:
1059  * @filename:  the filename
1060  * @cur:  the document
1061  * @format:  should formatting spaces been added
1062  * @encoding: the document encoding
1063  *
1064  * Dump an HTML document to a file using a given encoding.
1065  * 
1066  * returns: the number of byte written or -1 in case of failure.
1067  */
1068 int
1069 htmlSaveFileFormat(const char *filename, xmlDocPtr cur,
1070                    const char *encoding, int format) {
1071     xmlOutputBufferPtr buf;
1072     xmlCharEncodingHandlerPtr handler = NULL;
1073     int ret;
1074
1075     xmlInitParser();
1076
1077     if (encoding != NULL) {
1078         xmlCharEncoding enc;
1079
1080         enc = xmlParseCharEncoding(encoding);
1081         if (enc != cur->charset) {
1082             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1083                 /*
1084                  * Not supported yet
1085                  */
1086                 return(-1);
1087             }
1088
1089             handler = xmlFindCharEncodingHandler(encoding);
1090             if (handler == NULL)
1091                 return(-1);
1092             htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1093         }
1094     } else {
1095         htmlSetMetaEncoding(cur, (const xmlChar *) "UTF-8");
1096     }
1097
1098     /*
1099      * Fallback to HTML or ASCII when the encoding is unspecified
1100      */
1101     if (handler == NULL)
1102         handler = xmlFindCharEncodingHandler("HTML");
1103     if (handler == NULL)
1104         handler = xmlFindCharEncodingHandler("ascii");
1105
1106     /* 
1107      * save the content to a temp buffer.
1108      */
1109     buf = xmlOutputBufferCreateFilename(filename, handler, 0);
1110     if (buf == NULL) return(0);
1111
1112     htmlDocContentDumpFormatOutput(buf, cur, encoding, format);
1113
1114     ret = xmlOutputBufferClose(buf);
1115     return(ret);
1116 }
1117
1118 /**
1119  * htmlSaveFileEnc:
1120  * @filename:  the filename
1121  * @cur:  the document
1122  * @encoding: the document encoding
1123  *
1124  * Dump an HTML document to a file using a given encoding
1125  * and formatting returns/spaces are added.
1126  * 
1127  * returns: the number of byte written or -1 in case of failure.
1128  */
1129 int
1130 htmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1131     return(htmlSaveFileFormat(filename, cur, encoding, 1));
1132 }
1133
1134
1135
1136 #endif /* LIBXML_HTML_ENABLED */