tizen 2.3.1 release
[external/libxml2.git] / 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 = NULL, head = NULL;
164     const xmlChar *content = NULL;
165     char newcontent[100];
166
167
168     if (doc == NULL)
169         return(-1);
170
171     /* html isn't a real encoding it's just libxml2 way to get entities */
172     if (!xmlStrcasecmp(encoding, BAD_CAST "html"))
173         return(-1);
174
175     if (encoding != NULL) {
176         snprintf(newcontent, sizeof(newcontent), "text/html; charset=%s",
177                 (char *)encoding);
178         newcontent[sizeof(newcontent) - 1] = 0;
179     }
180
181     cur = doc->children;
182
183     /*
184      * Search the html
185      */
186     while (cur != NULL) {
187         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
188             if (xmlStrcasecmp(cur->name, BAD_CAST"html") == 0)
189                 break;
190             if (xmlStrcasecmp(cur->name, BAD_CAST"head") == 0)
191                 goto found_head;
192             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0)
193                 goto found_meta;
194         }
195         cur = cur->next;
196     }
197     if (cur == NULL)
198         return(-1);
199     cur = cur->children;
200
201     /*
202      * Search the head
203      */
204     while (cur != NULL) {
205         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
206             if (xmlStrcasecmp(cur->name, BAD_CAST"head") == 0)
207                 break;
208             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0) {
209                 head = cur->parent;
210                 goto found_meta;
211             }
212         }
213         cur = cur->next;
214     }
215     if (cur == NULL)
216         return(-1);
217 found_head:
218     head = cur;
219     if (cur->children == NULL)
220         goto create;
221     cur = cur->children;
222
223 found_meta:
224     /*
225      * Search and update all the remaining the meta elements carrying
226      * encoding informations
227      */
228     while (cur != NULL) {
229         if ((cur->type == XML_ELEMENT_NODE) && (cur->name != NULL)) {
230             if (xmlStrcasecmp(cur->name, BAD_CAST"meta") == 0) {
231                 xmlAttrPtr attr = cur->properties;
232                 int http;
233                 const xmlChar *value;
234
235                 content = NULL;
236                 http = 0;
237                 while (attr != NULL) {
238                     if ((attr->children != NULL) &&
239                         (attr->children->type == XML_TEXT_NODE) &&
240                         (attr->children->next == NULL)) {
241                         value = attr->children->content;
242                         if ((!xmlStrcasecmp(attr->name, BAD_CAST"http-equiv"))
243                          && (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
244                             http = 1;
245                         else
246                         {
247                            if ((value != NULL) && 
248                                (!xmlStrcasecmp(attr->name, BAD_CAST"content")))
249                                content = value;
250                         }
251                         if ((http != 0) && (content != NULL))
252                             break;
253                     }
254                     attr = attr->next;
255                 }
256                 if ((http != 0) && (content != NULL)) {
257                     meta = cur;
258                     break;
259                 }
260
261             }
262         }
263         cur = cur->next;
264     }
265 create:
266     if (meta == NULL) {
267         if ((encoding != NULL) && (head != NULL)) {
268             /*
269              * Create a new Meta element with the right attributes
270              */
271
272             meta = xmlNewDocNode(doc, NULL, BAD_CAST"meta", NULL);
273             if (head->children == NULL)
274                 xmlAddChild(head, meta);
275             else
276                 xmlAddPrevSibling(head->children, meta);
277             xmlNewProp(meta, BAD_CAST"http-equiv", BAD_CAST"Content-Type");
278             xmlNewProp(meta, BAD_CAST"content", BAD_CAST newcontent);
279         }
280     } else {
281         /* change the document only if there is a real encoding change */
282         if (xmlStrcasestr(content, encoding) == NULL) {
283             xmlSetProp(meta, BAD_CAST"content", BAD_CAST newcontent);
284         }
285     }
286
287
288     return(0);
289 }
290
291 /**
292  * booleanHTMLAttrs:
293  *
294  * These are the HTML attributes which will be output
295  * in minimized form, i.e. <option selected="selected"> will be
296  * output as <option selected>, as per XSLT 1.0 16.2 "HTML Output Method"
297  *
298  */
299 static const char* htmlBooleanAttrs[] = {
300   "checked", "compact", "declare", "defer", "disabled", "ismap",
301   "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly",
302   "selected", NULL
303 };
304
305
306 /**
307  * htmlIsBooleanAttr:
308  * @name:  the name of the attribute to check
309  *
310  * Determine if a given attribute is a boolean attribute.
311  * 
312  * returns: false if the attribute is not boolean, true otherwise.
313  */
314 int
315 htmlIsBooleanAttr(const xmlChar *name)
316 {
317     int i = 0;
318
319     while (htmlBooleanAttrs[i] != NULL) {
320         if (xmlStrcasecmp((const xmlChar *)htmlBooleanAttrs[i], name) == 0)
321             return 1;
322         i++;
323     }
324     return 0;
325 }
326
327 #ifdef LIBXML_OUTPUT_ENABLED
328 /*
329  * private routine exported from xmlIO.c
330  */
331 xmlOutputBufferPtr
332 xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder);
333 /************************************************************************
334  *                                                                      *
335  *                      Output error handlers                           *
336  *                                                                      *
337  ************************************************************************/
338 /**
339  * htmlSaveErrMemory:
340  * @extra:  extra informations
341  *
342  * Handle an out of memory condition
343  */
344 static void
345 htmlSaveErrMemory(const char *extra)
346 {
347     __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
348 }
349
350 /**
351  * htmlSaveErr:
352  * @code:  the error number
353  * @node:  the location of the error.
354  * @extra:  extra informations
355  *
356  * Handle an out of memory condition
357  */
358 static void
359 htmlSaveErr(int code, xmlNodePtr node, const char *extra)
360 {
361     const char *msg = NULL;
362
363     switch(code) {
364         case XML_SAVE_NOT_UTF8:
365             msg = "string is not in UTF-8\n";
366             break;
367         case XML_SAVE_CHAR_INVALID:
368             msg = "invalid character value\n";
369             break;
370         case XML_SAVE_UNKNOWN_ENCODING:
371             msg = "unknown encoding %s\n";
372             break;
373         case XML_SAVE_NO_DOCTYPE:
374             msg = "HTML has no DOCTYPE\n";
375             break;
376         default:
377             msg = "unexpected error number\n";
378     }
379     __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
380 }
381
382 /************************************************************************
383  *                                                                      *
384  *              Dumping HTML tree content to a simple buffer            *
385  *                                                                      *
386  ************************************************************************/
387
388 static int
389 htmlNodeDumpFormat(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
390                    int format);
391
392 /**
393  * htmlNodeDumpFormat:
394  * @buf:  the HTML buffer output
395  * @doc:  the document
396  * @cur:  the current node
397  * @format:  should formatting spaces been added
398  *
399  * Dump an HTML node, recursive behaviour,children are printed too.
400  *
401  * Returns the number of byte written or -1 in case of error
402  */
403 static int
404 htmlNodeDumpFormat(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
405                    int format) {
406     unsigned int use;
407     int ret;
408     xmlOutputBufferPtr outbuf;
409
410     if (cur == NULL) {
411         return (-1);
412     }
413     if (buf == NULL) {
414         return (-1);
415     }
416     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
417     if (outbuf == NULL) {
418         htmlSaveErrMemory("allocating HTML output buffer");
419         return (-1);
420     }
421     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
422     outbuf->buffer = buf;
423     outbuf->encoder = NULL;
424     outbuf->writecallback = NULL;
425     outbuf->closecallback = NULL;
426     outbuf->context = NULL;
427     outbuf->written = 0;
428
429     use = buf->use;
430     htmlNodeDumpFormatOutput(outbuf, doc, cur, NULL, format);
431     xmlFree(outbuf);
432     ret = buf->use - use;
433     return (ret);
434 }
435
436 /**
437  * htmlNodeDump:
438  * @buf:  the HTML buffer output
439  * @doc:  the document
440  * @cur:  the current node
441  *
442  * Dump an HTML node, recursive behaviour,children are printed too,
443  * and formatting returns are added.
444  *
445  * Returns the number of byte written or -1 in case of error
446  */
447 int
448 htmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur) {
449     xmlInitParser();
450
451     return(htmlNodeDumpFormat(buf, doc, cur, 1));
452 }
453
454 /**
455  * htmlNodeDumpFileFormat:
456  * @out:  the FILE pointer
457  * @doc:  the document
458  * @cur:  the current node
459  * @encoding: the document encoding
460  * @format:  should formatting spaces been added
461  *
462  * Dump an HTML node, recursive behaviour,children are printed too.
463  *
464  * TODO: if encoding == NULL try to save in the doc encoding
465  *
466  * returns: the number of byte written or -1 in case of failure.
467  */
468 int
469 htmlNodeDumpFileFormat(FILE *out, xmlDocPtr doc,
470                        xmlNodePtr cur, const char *encoding, int format) {
471     xmlOutputBufferPtr buf;
472     xmlCharEncodingHandlerPtr handler = NULL;
473     int ret;
474
475     xmlInitParser();
476
477     if (encoding != NULL) {
478         xmlCharEncoding enc;
479
480         enc = xmlParseCharEncoding(encoding);
481         if (enc != XML_CHAR_ENCODING_UTF8) {
482             handler = xmlFindCharEncodingHandler(encoding);
483             if (handler == NULL)
484                 return(-1);
485         }
486     }
487
488     /*
489      * Fallback to HTML or ASCII when the encoding is unspecified
490      */
491     if (handler == NULL)
492         handler = xmlFindCharEncodingHandler("HTML");
493     if (handler == NULL)
494         handler = xmlFindCharEncodingHandler("ascii");
495
496     /* 
497      * save the content to a temp buffer.
498      */
499     buf = xmlOutputBufferCreateFile(out, handler);
500     if (buf == NULL) return(0);
501
502     htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
503
504     ret = xmlOutputBufferClose(buf);
505     return(ret);
506 }
507
508 /**
509  * htmlNodeDumpFile:
510  * @out:  the FILE pointer
511  * @doc:  the document
512  * @cur:  the current node
513  *
514  * Dump an HTML node, recursive behaviour,children are printed too,
515  * and formatting returns are added.
516  */
517 void
518 htmlNodeDumpFile(FILE *out, xmlDocPtr doc, xmlNodePtr cur) {
519     htmlNodeDumpFileFormat(out, doc, cur, NULL, 1);
520 }
521
522 /**
523  * htmlDocDumpMemoryFormat:
524  * @cur:  the document
525  * @mem:  OUT: the memory pointer
526  * @size:  OUT: the memory length
527  * @format:  should formatting spaces been added
528  *
529  * Dump an HTML document in memory and return the xmlChar * and it's size.
530  * It's up to the caller to free the memory.
531  */
532 void
533 htmlDocDumpMemoryFormat(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
534     xmlOutputBufferPtr buf;
535     xmlCharEncodingHandlerPtr handler = NULL;
536     const char *encoding;
537
538     xmlInitParser();
539
540     if ((mem == NULL) || (size == NULL))
541         return;
542     if (cur == NULL) {
543         *mem = NULL;
544         *size = 0;
545         return;
546     }
547
548     encoding = (const char *) htmlGetMetaEncoding(cur);
549
550     if (encoding != NULL) {
551         xmlCharEncoding enc;
552
553         enc = xmlParseCharEncoding(encoding);
554         if (enc != cur->charset) {
555             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
556                 /*
557                  * Not supported yet
558                  */
559                 *mem = NULL;
560                 *size = 0;
561                 return;
562             }
563
564             handler = xmlFindCharEncodingHandler(encoding);
565             if (handler == NULL) {
566                 *mem = NULL;
567                 *size = 0;
568                 return;
569             }
570         } else {
571             handler = xmlFindCharEncodingHandler(encoding);
572         }
573     }
574
575     /*
576      * Fallback to HTML or ASCII when the encoding is unspecified
577      */
578     if (handler == NULL)
579         handler = xmlFindCharEncodingHandler("HTML");
580     if (handler == NULL)
581         handler = xmlFindCharEncodingHandler("ascii");
582
583     buf = xmlAllocOutputBufferInternal(handler);
584     if (buf == NULL) {
585         *mem = NULL;
586         *size = 0;
587         return;
588     }
589
590         htmlDocContentDumpFormatOutput(buf, cur, NULL, format);
591
592     xmlOutputBufferFlush(buf);
593     if (buf->conv != NULL) {
594         *size = buf->conv->use;
595         *mem = xmlStrndup(buf->conv->content, *size);
596     } else {
597         *size = buf->buffer->use;
598         *mem = xmlStrndup(buf->buffer->content, *size);
599     }
600     (void)xmlOutputBufferClose(buf);
601 }
602
603 /**
604  * htmlDocDumpMemory:
605  * @cur:  the document
606  * @mem:  OUT: the memory pointer
607  * @size:  OUT: the memory length
608  *
609  * Dump an HTML document in memory and return the xmlChar * and it's size.
610  * It's up to the caller to free the memory.
611  */
612 void
613 htmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
614         htmlDocDumpMemoryFormat(cur, mem, size, 1);
615 }
616
617
618 /************************************************************************
619  *                                                                      *
620  *              Dumping HTML tree content to an I/O output buffer       *
621  *                                                                      *
622  ************************************************************************/
623
624 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
625
626 /**
627  * htmlDtdDumpOutput:
628  * @buf:  the HTML buffer output
629  * @doc:  the document
630  * @encoding:  the encoding string
631  * 
632  * TODO: check whether encoding is needed
633  *
634  * Dump the HTML document DTD, if any.
635  */
636 static void
637 htmlDtdDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
638                   const char *encoding ATTRIBUTE_UNUSED) {
639     xmlDtdPtr cur = doc->intSubset;
640
641     if (cur == NULL) {
642         htmlSaveErr(XML_SAVE_NO_DOCTYPE, (xmlNodePtr) doc, NULL);
643         return;
644     }
645     xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
646     xmlOutputBufferWriteString(buf, (const char *)cur->name);
647     if (cur->ExternalID != NULL) {
648         xmlOutputBufferWriteString(buf, " PUBLIC ");
649         xmlBufferWriteQuotedString(buf->buffer, cur->ExternalID);
650         if (cur->SystemID != NULL) {
651             xmlOutputBufferWriteString(buf, " ");
652             xmlBufferWriteQuotedString(buf->buffer, cur->SystemID);
653         } 
654     }  else if (cur->SystemID != NULL) {
655         xmlOutputBufferWriteString(buf, " SYSTEM ");
656         xmlBufferWriteQuotedString(buf->buffer, cur->SystemID);
657     }
658     xmlOutputBufferWriteString(buf, ">\n");
659 }
660
661 /**
662  * htmlAttrDumpOutput:
663  * @buf:  the HTML buffer output
664  * @doc:  the document
665  * @cur:  the attribute pointer
666  * @encoding:  the encoding string
667  *
668  * Dump an HTML attribute
669  */
670 static void
671 htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur,
672                    const char *encoding ATTRIBUTE_UNUSED) {
673     xmlChar *value;
674
675     /*
676      * TODO: The html output method should not escape a & character
677      *       occurring in an attribute value immediately followed by
678      *       a { character (see Section B.7.1 of the HTML 4.0 Recommendation).
679      */
680
681     if (cur == NULL) {
682         return;
683     }
684     xmlOutputBufferWriteString(buf, " ");
685     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
686         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
687         xmlOutputBufferWriteString(buf, ":");
688     }
689     xmlOutputBufferWriteString(buf, (const char *)cur->name);
690     if ((cur->children != NULL) && (!htmlIsBooleanAttr(cur->name))) {
691         value = xmlNodeListGetString(doc, cur->children, 0);
692         if (value) {
693             xmlOutputBufferWriteString(buf, "=");
694             if ((cur->ns == NULL) && (cur->parent != NULL) &&
695                 (cur->parent->ns == NULL) &&
696                 ((!xmlStrcasecmp(cur->name, BAD_CAST "href")) ||
697                  (!xmlStrcasecmp(cur->name, BAD_CAST "action")) ||
698                  (!xmlStrcasecmp(cur->name, BAD_CAST "src")) ||
699                  ((!xmlStrcasecmp(cur->name, BAD_CAST "name")) &&
700                   (!xmlStrcasecmp(cur->parent->name, BAD_CAST "a"))))) {
701                 xmlChar *escaped;
702                 xmlChar *tmp = value;
703
704                 while (IS_BLANK_CH(*tmp)) tmp++;
705
706                 escaped = xmlURIEscapeStr(tmp, BAD_CAST"@/:=?;#%&,+");
707                 if (escaped != NULL) {
708                     xmlBufferWriteQuotedString(buf->buffer, escaped);
709                     xmlFree(escaped);
710                 } else {
711                     xmlBufferWriteQuotedString(buf->buffer, value);
712                 }
713             } else {
714                 xmlBufferWriteQuotedString(buf->buffer, value);
715             }
716             xmlFree(value);
717         } else  {
718             xmlOutputBufferWriteString(buf, "=\"\"");
719         }
720     }
721 }
722
723 /**
724  * htmlAttrListDumpOutput:
725  * @buf:  the HTML buffer output
726  * @doc:  the document
727  * @cur:  the first attribute pointer
728  * @encoding:  the encoding string
729  *
730  * Dump a list of HTML attributes
731  */
732 static void
733 htmlAttrListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlAttrPtr cur, const char *encoding) {
734     if (cur == NULL) {
735         return;
736     }
737     while (cur != NULL) {
738         htmlAttrDumpOutput(buf, doc, cur, encoding);
739         cur = cur->next;
740     }
741 }
742
743
744
745 /**
746  * htmlNodeListDumpOutput:
747  * @buf:  the HTML buffer output
748  * @doc:  the document
749  * @cur:  the first node
750  * @encoding:  the encoding string
751  * @format:  should formatting spaces been added
752  *
753  * Dump an HTML node list, recursive behaviour,children are printed too.
754  */
755 static void
756 htmlNodeListDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
757                        xmlNodePtr cur, const char *encoding, int format) {
758     if (cur == NULL) {
759         return;
760     }
761     while (cur != NULL) {
762         htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
763         cur = cur->next;
764     }
765 }
766
767 /**
768  * htmlNodeDumpFormatOutput:
769  * @buf:  the HTML buffer output
770  * @doc:  the document
771  * @cur:  the current node
772  * @encoding:  the encoding string
773  * @format:  should formatting spaces been added
774  *
775  * Dump an HTML node, recursive behaviour,children are printed too.
776  */
777 void
778 htmlNodeDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
779                          xmlNodePtr cur, const char *encoding, int format) {
780     const htmlElemDesc * info;
781
782     xmlInitParser();
783
784     if ((cur == NULL) || (buf == NULL)) {
785         return;
786     }
787     /*
788      * Special cases.
789      */
790     if (cur->type == XML_DTD_NODE)
791         return;
792     if ((cur->type == XML_HTML_DOCUMENT_NODE) ||
793         (cur->type == XML_DOCUMENT_NODE)){
794         htmlDocContentDumpOutput(buf, (xmlDocPtr) cur, encoding);
795         return;
796     }
797     if (cur->type == XML_ATTRIBUTE_NODE) {
798         htmlAttrDumpOutput(buf, doc, (xmlAttrPtr) cur, encoding);
799         return;
800     }
801     if (cur->type == HTML_TEXT_NODE) {
802         if (cur->content != NULL) {
803             if (((cur->name == (const xmlChar *)xmlStringText) ||
804                  (cur->name != (const xmlChar *)xmlStringTextNoenc)) &&
805                 ((cur->parent == NULL) ||
806                  ((xmlStrcasecmp(cur->parent->name, BAD_CAST "script")) &&
807                   (xmlStrcasecmp(cur->parent->name, BAD_CAST "style"))))) {
808                 xmlChar *buffer;
809
810                 buffer = xmlEncodeEntitiesReentrant(doc, cur->content);
811                 if (buffer != NULL) {
812                     xmlOutputBufferWriteString(buf, (const char *)buffer);
813                     xmlFree(buffer);
814                 }
815             } else {
816                 xmlOutputBufferWriteString(buf, (const char *)cur->content);
817             }
818         }
819         return;
820     }
821     if (cur->type == HTML_COMMENT_NODE) {
822         if (cur->content != NULL) {
823             xmlOutputBufferWriteString(buf, "<!--");
824             xmlOutputBufferWriteString(buf, (const char *)cur->content);
825             xmlOutputBufferWriteString(buf, "-->");
826         }
827         return;
828     }
829     if (cur->type == HTML_PI_NODE) {
830         if (cur->name == NULL)
831             return;
832         xmlOutputBufferWriteString(buf, "<?");
833         xmlOutputBufferWriteString(buf, (const char *)cur->name);
834         if (cur->content != NULL) {
835             xmlOutputBufferWriteString(buf, " ");
836             xmlOutputBufferWriteString(buf, (const char *)cur->content);
837         }
838         xmlOutputBufferWriteString(buf, ">");
839         return;
840     }
841     if (cur->type == HTML_ENTITY_REF_NODE) {
842         xmlOutputBufferWriteString(buf, "&");
843         xmlOutputBufferWriteString(buf, (const char *)cur->name);
844         xmlOutputBufferWriteString(buf, ";");
845         return;
846     }
847     if (cur->type == HTML_PRESERVE_NODE) {
848         if (cur->content != NULL) {
849             xmlOutputBufferWriteString(buf, (const char *)cur->content);
850         }
851         return;
852     }
853
854     /*
855      * Get specific HTML info for that node.
856      */
857     if (cur->ns == NULL)
858         info = htmlTagLookup(cur->name);
859     else
860         info = NULL;
861
862     xmlOutputBufferWriteString(buf, "<");
863     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
864         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
865         xmlOutputBufferWriteString(buf, ":");
866     }
867     xmlOutputBufferWriteString(buf, (const char *)cur->name);
868     if (cur->nsDef)
869         xmlNsListDumpOutput(buf, cur->nsDef);
870     if (cur->properties != NULL)
871         htmlAttrListDumpOutput(buf, doc, cur->properties, encoding);
872
873     if ((info != NULL) && (info->empty)) {
874         xmlOutputBufferWriteString(buf, ">");
875         if ((format) && (!info->isinline) && (cur->next != NULL)) {
876             if ((cur->next->type != HTML_TEXT_NODE) &&
877                 (cur->next->type != HTML_ENTITY_REF_NODE) &&
878                 (cur->parent != NULL) &&
879                 (cur->parent->name != NULL) &&
880                 (cur->parent->name[0] != 'p')) /* p, pre, param */
881                 xmlOutputBufferWriteString(buf, "\n");
882         }
883         return;
884     }
885     if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
886         (cur->children == NULL)) {
887         if ((info != NULL) && (info->saveEndTag != 0) &&
888             (xmlStrcmp(BAD_CAST info->name, BAD_CAST "html")) &&
889             (xmlStrcmp(BAD_CAST info->name, BAD_CAST "body"))) {
890             xmlOutputBufferWriteString(buf, ">");
891         } else {
892             xmlOutputBufferWriteString(buf, "></");
893             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
894                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
895                 xmlOutputBufferWriteString(buf, ":");
896             }
897             xmlOutputBufferWriteString(buf, (const char *)cur->name);
898             xmlOutputBufferWriteString(buf, ">");
899         }
900         if ((format) && (cur->next != NULL) &&
901             (info != NULL) && (!info->isinline)) {
902             if ((cur->next->type != HTML_TEXT_NODE) &&
903                 (cur->next->type != HTML_ENTITY_REF_NODE) &&
904                 (cur->parent != NULL) &&
905                 (cur->parent->name != NULL) &&
906                 (cur->parent->name[0] != 'p')) /* p, pre, param */
907                 xmlOutputBufferWriteString(buf, "\n");
908         }
909         return;
910     }
911     xmlOutputBufferWriteString(buf, ">");
912     if ((cur->type != XML_ELEMENT_NODE) &&
913         (cur->content != NULL)) {
914             /*
915              * Uses the OutputBuffer property to automatically convert
916              * invalids to charrefs
917              */
918
919             xmlOutputBufferWriteString(buf, (const char *) cur->content);
920     }
921     if (cur->children != NULL) {
922         if ((format) && (info != NULL) && (!info->isinline) &&
923             (cur->children->type != HTML_TEXT_NODE) &&
924             (cur->children->type != HTML_ENTITY_REF_NODE) &&
925             (cur->children != cur->last) &&
926             (cur->name != NULL) &&
927             (cur->name[0] != 'p')) /* p, pre, param */
928             xmlOutputBufferWriteString(buf, "\n");
929         htmlNodeListDumpOutput(buf, doc, cur->children, encoding, format);
930         if ((format) && (info != NULL) && (!info->isinline) &&
931             (cur->last->type != HTML_TEXT_NODE) &&
932             (cur->last->type != HTML_ENTITY_REF_NODE) &&
933             (cur->children != cur->last) &&
934             (cur->name != NULL) &&
935             (cur->name[0] != 'p')) /* p, pre, param */
936             xmlOutputBufferWriteString(buf, "\n");
937     }
938     xmlOutputBufferWriteString(buf, "</");
939     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
940         xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
941         xmlOutputBufferWriteString(buf, ":");
942     }
943     xmlOutputBufferWriteString(buf, (const char *)cur->name);
944     xmlOutputBufferWriteString(buf, ">");
945     if ((format) && (info != NULL) && (!info->isinline) &&
946         (cur->next != NULL)) {
947         if ((cur->next->type != HTML_TEXT_NODE) &&
948             (cur->next->type != HTML_ENTITY_REF_NODE) &&
949             (cur->parent != NULL) &&
950             (cur->parent->name != NULL) &&
951             (cur->parent->name[0] != 'p')) /* p, pre, param */
952             xmlOutputBufferWriteString(buf, "\n");
953     }
954 }
955
956 /**
957  * htmlNodeDumpOutput:
958  * @buf:  the HTML buffer output
959  * @doc:  the document
960  * @cur:  the current node
961  * @encoding:  the encoding string
962  *
963  * Dump an HTML node, recursive behaviour,children are printed too,
964  * and formatting returns/spaces are added.
965  */
966 void
967 htmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc,
968                    xmlNodePtr cur, const char *encoding) {
969     htmlNodeDumpFormatOutput(buf, doc, cur, encoding, 1);
970 }
971
972 /**
973  * htmlDocContentDumpFormatOutput:
974  * @buf:  the HTML buffer output
975  * @cur:  the document
976  * @encoding:  the encoding string
977  * @format:  should formatting spaces been added
978  *
979  * Dump an HTML document.
980  */
981 void
982 htmlDocContentDumpFormatOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
983                                const char *encoding, int format) {
984     int type;
985
986     xmlInitParser();
987
988     if ((buf == NULL) || (cur == NULL))
989         return;
990
991     /*
992      * force to output the stuff as HTML, especially for entities
993      */
994     type = cur->type;
995     cur->type = XML_HTML_DOCUMENT_NODE;
996     if (cur->intSubset != NULL) {
997         htmlDtdDumpOutput(buf, cur, NULL);
998     }
999     if (cur->children != NULL) {
1000         htmlNodeListDumpOutput(buf, cur, cur->children, encoding, format);
1001     }
1002     xmlOutputBufferWriteString(buf, "\n");
1003     cur->type = (xmlElementType) type;
1004 }
1005
1006 /**
1007  * htmlDocContentDumpOutput:
1008  * @buf:  the HTML buffer output
1009  * @cur:  the document
1010  * @encoding:  the encoding string
1011  *
1012  * Dump an HTML document. Formating return/spaces are added.
1013  */
1014 void
1015 htmlDocContentDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr cur,
1016                          const char *encoding) {
1017     htmlDocContentDumpFormatOutput(buf, cur, encoding, 1);
1018 }
1019
1020 /************************************************************************
1021  *                                                                      *
1022  *              Saving functions front-ends                             *
1023  *                                                                      *
1024  ************************************************************************/
1025
1026 /**
1027  * htmlDocDump:
1028  * @f:  the FILE*
1029  * @cur:  the document
1030  *
1031  * Dump an HTML document to an open FILE.
1032  *
1033  * returns: the number of byte written or -1 in case of failure.
1034  */
1035 int
1036 htmlDocDump(FILE *f, xmlDocPtr cur) {
1037     xmlOutputBufferPtr buf;
1038     xmlCharEncodingHandlerPtr handler = NULL;
1039     const char *encoding;
1040     int ret;
1041
1042     xmlInitParser();
1043
1044     if ((cur == NULL) || (f == NULL)) {
1045         return(-1);
1046     }
1047
1048     encoding = (const char *) htmlGetMetaEncoding(cur);
1049
1050     if (encoding != NULL) {
1051         xmlCharEncoding enc;
1052
1053         enc = xmlParseCharEncoding(encoding);
1054         if (enc != cur->charset) {
1055             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1056                 /*
1057                  * Not supported yet
1058                  */
1059                 return(-1);
1060             }
1061
1062             handler = xmlFindCharEncodingHandler(encoding);
1063             if (handler == NULL)
1064                 return(-1);
1065         } else {
1066             handler = xmlFindCharEncodingHandler(encoding);
1067         }
1068     }
1069
1070     /*
1071      * Fallback to HTML or ASCII when the encoding is unspecified
1072      */
1073     if (handler == NULL)
1074         handler = xmlFindCharEncodingHandler("HTML");
1075     if (handler == NULL)
1076         handler = xmlFindCharEncodingHandler("ascii");
1077
1078     buf = xmlOutputBufferCreateFile(f, handler);
1079     if (buf == NULL) return(-1);
1080     htmlDocContentDumpOutput(buf, cur, NULL);
1081
1082     ret = xmlOutputBufferClose(buf);
1083     return(ret);
1084 }
1085
1086 /**
1087  * htmlSaveFile:
1088  * @filename:  the filename (or URL)
1089  * @cur:  the document
1090  *
1091  * Dump an HTML document to a file. If @filename is "-" the stdout file is
1092  * used.
1093  * returns: the number of byte written or -1 in case of failure.
1094  */
1095 int
1096 htmlSaveFile(const char *filename, xmlDocPtr cur) {
1097     xmlOutputBufferPtr buf;
1098     xmlCharEncodingHandlerPtr handler = NULL;
1099     const char *encoding;
1100     int ret;
1101
1102     if ((cur == NULL) || (filename == NULL))
1103         return(-1);
1104        
1105     xmlInitParser();
1106
1107     encoding = (const char *) htmlGetMetaEncoding(cur);
1108
1109     if (encoding != NULL) {
1110         xmlCharEncoding enc;
1111
1112         enc = xmlParseCharEncoding(encoding);
1113         if (enc != cur->charset) {
1114             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1115                 /*
1116                  * Not supported yet
1117                  */
1118                 return(-1);
1119             }
1120
1121             handler = xmlFindCharEncodingHandler(encoding);
1122             if (handler == NULL)
1123                 return(-1);
1124         }
1125     }
1126
1127     /*
1128      * Fallback to HTML or ASCII when the encoding is unspecified
1129      */
1130     if (handler == NULL)
1131         handler = xmlFindCharEncodingHandler("HTML");
1132     if (handler == NULL)
1133         handler = xmlFindCharEncodingHandler("ascii");
1134
1135     /* 
1136      * save the content to a temp buffer.
1137      */
1138     buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1139     if (buf == NULL) return(0);
1140
1141     htmlDocContentDumpOutput(buf, cur, NULL);
1142
1143     ret = xmlOutputBufferClose(buf);
1144     return(ret);
1145 }
1146
1147 /**
1148  * htmlSaveFileFormat:
1149  * @filename:  the filename
1150  * @cur:  the document
1151  * @format:  should formatting spaces been added
1152  * @encoding: the document encoding
1153  *
1154  * Dump an HTML document to a file using a given encoding.
1155  * 
1156  * returns: the number of byte written or -1 in case of failure.
1157  */
1158 int
1159 htmlSaveFileFormat(const char *filename, xmlDocPtr cur,
1160                    const char *encoding, int format) {
1161     xmlOutputBufferPtr buf;
1162     xmlCharEncodingHandlerPtr handler = NULL;
1163     int ret;
1164
1165     if ((cur == NULL) || (filename == NULL))
1166         return(-1);
1167
1168     xmlInitParser();
1169
1170     if (encoding != NULL) {
1171         xmlCharEncoding enc;
1172
1173         enc = xmlParseCharEncoding(encoding);
1174         if (enc != cur->charset) {
1175             if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1176                 /*
1177                  * Not supported yet
1178                  */
1179                 return(-1);
1180             }
1181
1182             handler = xmlFindCharEncodingHandler(encoding);
1183             if (handler == NULL)
1184                 return(-1);
1185         }
1186         htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1187     } else {
1188         htmlSetMetaEncoding(cur, (const xmlChar *) "UTF-8");
1189     }
1190
1191     /*
1192      * Fallback to HTML or ASCII when the encoding is unspecified
1193      */
1194     if (handler == NULL)
1195         handler = xmlFindCharEncodingHandler("HTML");
1196     if (handler == NULL)
1197         handler = xmlFindCharEncodingHandler("ascii");
1198
1199     /* 
1200      * save the content to a temp buffer.
1201      */
1202     buf = xmlOutputBufferCreateFilename(filename, handler, 0);
1203     if (buf == NULL) return(0);
1204
1205     htmlDocContentDumpFormatOutput(buf, cur, encoding, format);
1206
1207     ret = xmlOutputBufferClose(buf);
1208     return(ret);
1209 }
1210
1211 /**
1212  * htmlSaveFileEnc:
1213  * @filename:  the filename
1214  * @cur:  the document
1215  * @encoding: the document encoding
1216  *
1217  * Dump an HTML document to a file using a given encoding
1218  * and formatting returns/spaces are added.
1219  * 
1220  * returns: the number of byte written or -1 in case of failure.
1221  */
1222 int
1223 htmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1224     return(htmlSaveFileFormat(filename, cur, encoding, 1));
1225 }
1226
1227 #endif /* LIBXML_OUTPUT_ENABLED */
1228
1229 #define bottom_HTMLtree
1230 #include "elfgcchack.h"
1231 #endif /* LIBXML_HTML_ENABLED */