Initial revision
[TestXSLT.git] / libxml2 / HTMLparser.c
1 /*
2  * HTMLparser.c : an HTML 4.0 non-verifying parser
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 #ifdef LIBXML_HTML_ENABLED
12
13 #include <string.h>
14 #ifdef HAVE_CTYPE_H
15 #include <ctype.h>
16 #endif
17 #ifdef HAVE_STDLIB_H
18 #include <stdlib.h>
19 #endif
20 #ifdef HAVE_SYS_STAT_H
21 #include <sys/stat.h>
22 #endif
23 #ifdef HAVE_FCNTL_H
24 #include <fcntl.h>
25 #endif
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_ZLIB_H
30 #include <zlib.h>
31 #endif
32
33 #include <libxml/xmlmemory.h>
34 #include <libxml/tree.h>
35 #include <libxml/parser.h>
36 #include <libxml/parserInternals.h>
37 #include <libxml/xmlerror.h>
38 #include <libxml/HTMLparser.h>
39 #include <libxml/HTMLtree.h>
40 #include <libxml/entities.h>
41 #include <libxml/encoding.h>
42 #include <libxml/valid.h>
43 #include <libxml/xmlIO.h>
44 #include <libxml/globals.h>
45 #include <libxml/uri.h>
46
47 #define HTML_MAX_NAMELEN 1000
48 #define HTML_PARSER_BIG_BUFFER_SIZE 1000
49 #define HTML_PARSER_BUFFER_SIZE 100
50
51 /* #define DEBUG */
52 /* #define DEBUG_PUSH */
53
54 static int htmlOmittedDefaultValue = 1;
55
56 xmlChar * htmlDecodeEntities(htmlParserCtxtPtr ctxt, int len,
57                              xmlChar end, xmlChar  end2, xmlChar end3);
58 static void htmlParseComment(htmlParserCtxtPtr ctxt);
59
60 /************************************************************************
61  *                                                                      *
62  *              Parser stacks related functions and macros              *
63  *                                                                      *
64  ************************************************************************/
65
66 /**
67  * htmlnamePush:
68  * @ctxt:  an HTML parser context
69  * @value:  the element name
70  *
71  * Pushes a new element name on top of the name stack
72  *
73  * Returns 0 in case of error, the index in the stack otherwise
74  */
75 static int
76 htmlnamePush(htmlParserCtxtPtr ctxt, xmlChar * value)
77 {
78     if (ctxt->nameNr >= ctxt->nameMax) {
79         ctxt->nameMax *= 2;
80         ctxt->nameTab =
81             (xmlChar * *)xmlRealloc(ctxt->nameTab,
82                                     ctxt->nameMax *
83                                     sizeof(ctxt->nameTab[0]));
84         if (ctxt->nameTab == NULL) {
85             xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
86             return (0);
87         }
88     }
89     ctxt->nameTab[ctxt->nameNr] = value;
90     ctxt->name = value;
91     return (ctxt->nameNr++);
92 }
93 /**
94  * htmlnamePop:
95  * @ctxt: an HTML parser context
96  *
97  * Pops the top element name from the name stack
98  *
99  * Returns the name just removed
100  */
101 static xmlChar *
102 htmlnamePop(htmlParserCtxtPtr ctxt)
103 {
104     xmlChar *ret;
105
106     if (ctxt->nameNr <= 0)
107         return (0);
108     ctxt->nameNr--;
109     if (ctxt->nameNr < 0)
110         return (0);
111     if (ctxt->nameNr > 0)
112         ctxt->name = ctxt->nameTab[ctxt->nameNr - 1];
113     else
114         ctxt->name = NULL;
115     ret = ctxt->nameTab[ctxt->nameNr];
116     ctxt->nameTab[ctxt->nameNr] = 0;
117     return (ret);
118 }
119
120 /*
121  * Macros for accessing the content. Those should be used only by the parser,
122  * and not exported.
123  *
124  * Dirty macros, i.e. one need to make assumption on the context to use them
125  *
126  *   CUR_PTR return the current pointer to the xmlChar to be parsed.
127  *   CUR     returns the current xmlChar value, i.e. a 8 bit value if compiled
128  *           in ISO-Latin or UTF-8, and the current 16 bit value if compiled
129  *           in UNICODE mode. This should be used internally by the parser
130  *           only to compare to ASCII values otherwise it would break when
131  *           running with UTF-8 encoding.
132  *   NXT(n)  returns the n'th next xmlChar. Same as CUR is should be used only
133  *           to compare on ASCII based substring.
134  *   UPP(n)  returns the n'th next xmlChar converted to uppercase. Same as CUR
135  *           it should be used only to compare on ASCII based substring.
136  *   SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
137  *           strings within the parser.
138  *
139  * Clean macros, not dependent of an ASCII context, expect UTF-8 encoding
140  *
141  *   CURRENT Returns the current char value, with the full decoding of
142  *           UTF-8 if we are using this mode. It returns an int.
143  *   NEXT    Skip to the next character, this does the proper decoding
144  *           in UTF-8 mode. It also pop-up unfinished entities on the fly.
145  *   COPY(to) copy one char to *to, increment CUR_PTR and to accordingly
146  */
147
148 #define UPPER (toupper(*ctxt->input->cur))
149
150 #define SKIP(val) ctxt->nbChars += (val),ctxt->input->cur += (val)
151
152 #define NXT(val) ctxt->input->cur[(val)]
153
154 #define UPP(val) (toupper(ctxt->input->cur[(val)]))
155
156 #define CUR_PTR ctxt->input->cur
157
158 #define SHRINK  xmlParserInputShrink(ctxt->input)
159
160 #define GROW  xmlParserInputGrow(ctxt->input, INPUT_CHUNK)
161
162 #define CURRENT ((int) (*ctxt->input->cur))
163
164 #define SKIP_BLANKS htmlSkipBlankChars(ctxt)
165
166 /* Inported from XML */
167
168 /* #define CUR (ctxt->token ? ctxt->token : (int) (*ctxt->input->cur)) */
169 #define CUR ((int) (*ctxt->input->cur))
170 #define NEXT xmlNextChar(ctxt),ctxt->nbChars++
171
172 #define RAW (ctxt->token ? -1 : (*ctxt->input->cur))
173 #define NXT(val) ctxt->input->cur[(val)]
174 #define CUR_PTR ctxt->input->cur
175
176
177 #define NEXTL(l) do {                                                   \
178     if (*(ctxt->input->cur) == '\n') {                                  \
179         ctxt->input->line++; ctxt->input->col = 1;                      \
180     } else ctxt->input->col++;                                          \
181     ctxt->token = 0; ctxt->input->cur += l; ctxt->nbChars++;            \
182   } while (0)
183     
184 /************
185     \
186     if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt);     \
187     if (*ctxt->input->cur == '&') xmlParserHandleReference(ctxt);
188  ************/
189
190 #define CUR_CHAR(l) htmlCurrentChar(ctxt, &l)
191 #define CUR_SCHAR(s, l) xmlStringCurrentChar(ctxt, s, &l)
192
193 #define COPY_BUF(l,b,i,v)                                               \
194     if (l == 1) b[i++] = (xmlChar) v;                                   \
195     else i += xmlCopyChar(l,&b[i],v)
196
197 /**
198  * htmlCurrentChar:
199  * @ctxt:  the HTML parser context
200  * @len:  pointer to the length of the char read
201  *
202  * The current char value, if using UTF-8 this may actually span multiple
203  * bytes in the input buffer. Implement the end of line normalization:
204  * 2.11 End-of-Line Handling
205  * If the encoding is unspecified, in the case we find an ISO-Latin-1
206  * char, then the encoding converter is plugged in automatically.
207  *
208  * Returns the current char value and its length
209  */
210
211 static int
212 htmlCurrentChar(xmlParserCtxtPtr ctxt, int *len) {
213     if (ctxt->instate == XML_PARSER_EOF)
214         return(0);
215
216     if (ctxt->token != 0) {
217         *len = 0;
218         return(ctxt->token);
219     }   
220     if (ctxt->charset == XML_CHAR_ENCODING_UTF8) {
221         /*
222          * We are supposed to handle UTF8, check it's valid
223          * From rfc2044: encoding of the Unicode values on UTF-8:
224          *
225          * UCS-4 range (hex.)           UTF-8 octet sequence (binary)
226          * 0000 0000-0000 007F   0xxxxxxx
227          * 0000 0080-0000 07FF   110xxxxx 10xxxxxx
228          * 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx 
229          *
230          * Check for the 0x110000 limit too
231          */
232         const unsigned char *cur = ctxt->input->cur;
233         unsigned char c;
234         unsigned int val;
235
236         c = *cur;
237         if (c & 0x80) {
238             if (cur[1] == 0)
239                 xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
240             if ((cur[1] & 0xc0) != 0x80)
241                 goto encoding_error;
242             if ((c & 0xe0) == 0xe0) {
243
244                 if (cur[2] == 0)
245                     xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
246                 if ((cur[2] & 0xc0) != 0x80)
247                     goto encoding_error;
248                 if ((c & 0xf0) == 0xf0) {
249                     if (cur[3] == 0)
250                         xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
251                     if (((c & 0xf8) != 0xf0) ||
252                         ((cur[3] & 0xc0) != 0x80))
253                         goto encoding_error;
254                     /* 4-byte code */
255                     *len = 4;
256                     val = (cur[0] & 0x7) << 18;
257                     val |= (cur[1] & 0x3f) << 12;
258                     val |= (cur[2] & 0x3f) << 6;
259                     val |= cur[3] & 0x3f;
260                 } else {
261                   /* 3-byte code */
262                     *len = 3;
263                     val = (cur[0] & 0xf) << 12;
264                     val |= (cur[1] & 0x3f) << 6;
265                     val |= cur[2] & 0x3f;
266                 }
267             } else {
268               /* 2-byte code */
269                 *len = 2;
270                 val = (cur[0] & 0x1f) << 6;
271                 val |= cur[1] & 0x3f;
272             }
273             if (!IS_CHAR(val)) {
274                 ctxt->errNo = XML_ERR_INVALID_ENCODING;
275                 if ((ctxt->sax != NULL) &&
276                     (ctxt->sax->error != NULL))
277                     ctxt->sax->error(ctxt->userData, 
278                                      "Char 0x%X out of allowed range\n", val);
279                 ctxt->wellFormed = 0;
280                 if (ctxt->recovery == 0) ctxt->disableSAX = 1;
281             }    
282             return(val);
283         } else {
284             /* 1-byte code */
285             *len = 1;
286             return((int) *ctxt->input->cur);
287         }
288     }
289     /*
290      * Assume it's a fixed length encoding (1) with
291      * a compatible encoding for the ASCII set, since
292      * XML constructs only use < 128 chars
293      */
294     *len = 1;
295     if ((int) *ctxt->input->cur < 0x80)
296         return((int) *ctxt->input->cur);
297
298     /*
299      * Humm this is bad, do an automatic flow conversion
300      */
301     xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
302     ctxt->charset = XML_CHAR_ENCODING_UTF8;
303     return(xmlCurrentChar(ctxt, len));
304
305 encoding_error:
306     /*
307      * If we detect an UTF8 error that probably mean that the
308      * input encoding didn't get properly advertized in the
309      * declaration header. Report the error and switch the encoding
310      * to ISO-Latin-1 (if you don't like this policy, just declare the
311      * encoding !)
312      */
313     ctxt->errNo = XML_ERR_INVALID_ENCODING;
314     if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) {
315         ctxt->sax->error(ctxt->userData, 
316                          "Input is not proper UTF-8, indicate encoding !\n");
317         ctxt->sax->error(ctxt->userData, "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n",
318                         ctxt->input->cur[0], ctxt->input->cur[1],
319                         ctxt->input->cur[2], ctxt->input->cur[3]);
320     }
321
322     ctxt->charset = XML_CHAR_ENCODING_8859_1; 
323     *len = 1;
324     return((int) *ctxt->input->cur);
325 }
326
327 /**
328  * htmlSkipBlankChars:
329  * @ctxt:  the HTML parser context
330  *
331  * skip all blanks character found at that point in the input streams.
332  *
333  * Returns the number of space chars skipped
334  */
335
336 static int
337 htmlSkipBlankChars(xmlParserCtxtPtr ctxt) {
338     int res = 0;
339
340     while (IS_BLANK(*(ctxt->input->cur))) {
341         if ((*ctxt->input->cur == 0) &&
342             (xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0)) {
343                 xmlPopInput(ctxt);
344         } else {
345             if (*(ctxt->input->cur) == '\n') {
346                 ctxt->input->line++; ctxt->input->col = 1;
347             } else ctxt->input->col++;
348             ctxt->input->cur++;
349             ctxt->nbChars++;
350             if (*ctxt->input->cur == 0)
351                 xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
352         }
353         res++;
354     }
355     return(res);
356 }
357
358
359
360 /************************************************************************
361  *                                                                      *
362  *              The list of HTML elements and their properties          *
363  *                                                                      *
364  ************************************************************************/
365
366 /*
367  *  Start Tag: 1 means the start tag can be ommited
368  *  End Tag:   1 means the end tag can be ommited
369  *             2 means it's forbidden (empty elements)
370  *             3 means the tag is stylistic and should be closed easily
371  *  Depr:      this element is deprecated
372  *  DTD:       1 means that this element is valid only in the Loose DTD
373  *             2 means that this element is valid only in the Frameset DTD
374  *
375  * Name,Start Tag,End Tag,Save End,Empty,Deprecated,DTD,inline,Description
376         , subElements , impliedsubelt , Attributes, userdata
377  */
378
379 /* Definitions and a couple of vars for HTML Elements */
380
381 #define FONTSTYLE "tt", "i", "b", "u", "s", "strike", "big", "small"
382 #define PHRASE "em", "strong", "dfn", "code", "samp", "kbd", "var", "cite", "abbr", "acronym"
383 #define SPECIAL "a", "img", "applet", "object", "font", "basefont", "br", "script", "map", "q", "sub", "sup", "span", "bdo", "iframe"
384 #define INLINE PCDATA FONTSTYLE PHRASE SPECIAL FORMCTRL
385 #define BLOCK HEADING LIST "pre", "p", "dl", "div", "center", "noscript", "noframes", "blockquote", "form", "isindex", "hr", "table", "fieldset", "address"
386 #define FORMCTRL "input", "select", "textarea", "label", "button"
387 #define PCDATA
388 #define HEADING "h1", "h2", "h3", "h4", "h5", "h6"
389 #define LIST "ul", "ol", "dir", "menu"
390 #define MODIFIER
391 #define FLOW BLOCK,INLINE
392 #define EMPTY NULL
393
394
395 static const char* html_flow[] = { FLOW, NULL } ;
396 static const char* html_inline[] = { INLINE, NULL } ;
397
398 /* placeholders: elts with content but no subelements */
399 static const char* html_pcdata[] = { NULL } ;
400 #define html_cdata html_pcdata
401
402
403 /* ... and for HTML Attributes */
404
405 #define COREATTRS "id", "class", "style", "title"
406 #define I18N "lang", "dir"
407 #define EVENTS "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover", "onmouseout", "onkeypress", "onkeydown", "onkeyup"
408 #define ATTRS COREATTRS,I18N,EVENTS
409 #define CELLHALIGN "align", "char", "charoff"
410 #define CELLVALIGN "valign"
411
412 static const char* html_attrs[] = { ATTRS, NULL } ;
413 static const char* core_i18n_attrs[] = { COREATTRS, I18N, NULL } ;
414 static const char* core_attrs[] = { COREATTRS, NULL } ;
415 static const char* i18n_attrs[] = { I18N, NULL } ;
416
417
418 /* Other declarations that should go inline ... */
419 static const char* a_attrs[] = { ATTRS, "charset", "type", "name",
420         "href", "hreflang", "rel", "rev", "accesskey", "shape", "coords",
421         "tabindex", "onfocus", "onblur", NULL } ;
422 static const char* target_attr[] = { "target", NULL } ;
423 static const char* rows_cols_attr[] = { "rows", "cols", NULL } ;
424 static const char* alt_attr[] = { "alt", NULL } ;
425 static const char* src_alt_attrs[] = { "src", "alt", NULL } ;
426 static const char* href_attrs[] = { "href", NULL } ;
427 static const char* clear_attrs[] = { "clear", NULL } ;
428 static const char* inline_p[] = { INLINE, "p", NULL } ;
429 static const char* flow_param[] = { FLOW, "param", NULL } ;
430 static const char* applet_attrs[] = { COREATTRS , "codebase",
431                 "archive", "alt", "name", "height", "width", "align",
432                 "hspace", "vspace", NULL } ;
433 static const char* area_attrs[] = { "shape", "coords", "href", "nohref",
434         "tabindex", "accesskey", "onfocus", "onblur", NULL } ;
435 static const char* basefont_attrs[] =
436         { "id", "size", "color", "face", NULL } ;
437 static const char* quote_attrs[] = { ATTRS, "cite", NULL } ;
438 static const char* body_contents[] = { FLOW, "ins", "del", NULL } ;
439 static const char* body_attrs[] = { ATTRS, "onload", "onunload", NULL } ;
440 static const char* body_depr[] = { "background", "bgcolor", "text",
441         "link", "vlink", "alink", NULL } ;
442 static const char* button_attrs[] = { ATTRS, "name", "value", "type",
443         "disabled", "tabindex", "accesskey", "onfocus", "onblur", NULL } ;
444
445
446 static const char* col_attrs[] = { ATTRS, "span", "width", CELLHALIGN, CELLVALIGN, NULL } ;
447 static const char* col_elt[] = { "col", NULL } ;
448 static const char* edit_attrs[] = { ATTRS, "datetime", "cite", NULL } ;
449 static const char* compact_attrs[] = { ATTRS, "compact", NULL } ;
450 static const char* dl_contents[] = { "dt", "dd", NULL } ;
451 static const char* compact_attr[] = { "compact", NULL } ;
452 static const char* label_attr[] = { "label", NULL } ;
453 static const char* fieldset_contents[] = { FLOW, "legend" } ;
454 static const char* font_attrs[] = { COREATTRS, I18N, "size", "color", "face" , NULL } ;
455 static const char* form_contents[] = { HEADING, LIST, INLINE, "pre", "p", "div", "center", "noscript", "noframes", "blockquote", "isindex", "hr", "table", "fieldset", "address", NULL } ;
456 static const char* form_attrs[] = { ATTRS, "method", "enctype", "accept", "name", "onsubmit", "onreset", "accept-charset", NULL } ;
457 static const char* frame_attrs[] = { COREATTRS, "longdesc", "name", "src", "frameborder", "marginwidth", "marginheight", "noresize", "scrolling" , NULL } ;
458 static const char* frameset_attrs[] = { COREATTRS, "rows", "cols", "onload", "onunload", NULL } ;
459 static const char* frameset_contents[] = { "frameset", "frame", "noframes", NULL } ;
460 static const char* head_attrs[] = { I18N, "profile", NULL } ;
461 static const char* head_contents[] = { "title", "isindex", "base", "script", "style", "meta", "link", "object", NULL } ;
462 static const char* hr_depr[] = { "align", "noshade", "size", "width", NULL } ;
463 static const char* version_attr[] = { "version", NULL } ;
464 static const char* html_content[] = { "head", "body", "frameset", NULL } ;
465 static const char* iframe_attrs[] = { COREATTRS, "longdesc", "name", "src", "frameborder", "marginwidth", "marginheight", "scrolling", "align", "height", "width", NULL } ;
466 static const char* img_attrs[] = { ATTRS, "longdesc", "name", "height", "width", "usemap", "ismap", NULL } ;
467 static const char* input_attrs[] = { ATTRS, "type", "name", "value", "checked", "disabled", "readonly", "size", "maxlength", "src", "alt", "usemap", "ismap", "tabindex", "accesskey", "onfocus", "onblur", "onselect", "onchange", "accept", NULL } ;
468 static const char* prompt_attrs[] = { COREATTRS, I18N, "prompt", NULL } ;
469 static const char* label_attrs[] = { ATTRS, "for", "accesskey", "onfocus", "onblur", NULL } ;
470 static const char* legend_attrs[] = { ATTRS, "accesskey", NULL } ;
471 static const char* align_attr[] = { "align", NULL } ;
472 static const char* link_attrs[] = { ATTRS, "charset", "href", "hreflang", "type", "rel", "rev", "media", NULL } ;
473 static const char* map_contents[] = { BLOCK, "area", NULL } ;
474 static const char* name_attr[] = { "name", NULL } ;
475 static const char* action_attr[] = { "action", NULL } ;
476 static const char* blockli_elt[] = { BLOCK, "li", NULL } ;
477 static const char* meta_attrs[] = { I18N, "http-equiv", "name", "scheme", NULL } ;
478 static const char* content_attr[] = { "content", NULL } ;
479 static const char* type_attr[] = { "type", NULL } ;
480 static const char* noframes_content[] = { "body", FLOW MODIFIER, NULL } ;
481 static const char* object_contents[] = { FLOW, "param", NULL } ;
482 static const char* object_attrs[] = { ATTRS, "declare", "classid", "codebase", "data", "type", "codetype", "archive", "standby", "height", "width", "usemap", "name", "tabindex", NULL } ;
483 static const char* object_depr[] = { "align", "border", "hspace", "vspace", NULL } ;
484 static const char* ol_attrs[] = { "type", "compact", "start", NULL} ;
485 static const char* option_elt[] = { "option", NULL } ;
486 static const char* optgroup_attrs[] = { ATTRS, "disabled", NULL } ;
487 static const char* option_attrs[] = { ATTRS, "disabled", "label", "selected", "value", NULL } ;
488 static const char* param_attrs[] = { "id", "value", "valuetype", "type", NULL } ;
489 static const char* width_attr[] = { "width", NULL } ;
490 static const char* pre_content[] = { PHRASE, "tt", "i", "b", "u", "s", "strike", "a", "br", "script", "map", "q", "span", "bdo", "iframe", NULL } ;
491 static const char* script_attrs[] = { "charset", "src", "defer", "event", "for", NULL } ;
492 static const char* language_attr[] = { "language", NULL } ;
493 static const char* select_content[] = { "optgroup", "option", NULL } ;
494 static const char* select_attrs[] = { ATTRS, "name", "size", "multiple", "disabled", "tabindex", "onfocus", "onblur", "onchange", NULL } ;
495 static const char* style_attrs[] = { I18N, "media", "title", NULL } ;
496 static const char* table_attrs[] = { ATTRS "summary", "width", "border", "frame", "rules", "cellspacing", "cellpadding", "datapagesize", NULL } ;
497 static const char* table_depr[] = { "align", "bgcolor", NULL } ;
498 static const char* table_contents[] = { "caption", "col", "colgroup", "thead", "tfoot", "tbody", "tr", NULL} ;
499 static const char* tr_elt[] = { "tr", NULL } ;
500 static const char* talign_attrs[] = { ATTRS, CELLHALIGN, CELLVALIGN, NULL} ;
501 static const char* th_td_depr[] = { "nowrap", "bgcolor", "width", "height", NULL } ;
502 static const char* th_td_attr[] = { ATTRS, "abbr", "axis", "headers", "scope", "rowspan", "colspan", CELLHALIGN, CELLVALIGN, NULL } ;
503 static const char* textarea_attrs[] = { ATTRS, "name", "disabled", "readonly", "tabindex", "accesskey", "onfocus", "onblur", "onselect", "onchange", NULL } ;
504 static const char* tr_contents[] = { "th", "td", NULL } ;
505 static const char* bgcolor_attr[] = { "bgcolor", NULL } ;
506 static const char* li_elt[] = { "li", NULL } ;
507 static const char* ul_depr[] = { "type", "compact", NULL} ;
508 static const char* dir_attr[] = { "dir", NULL} ;
509
510 #define DECL (const char**)
511
512 static const htmlElemDesc
513 html40ElementTable[] = {
514 { "a",          0, 0, 0, 0, 0, 0, 1, "anchor ",
515         DECL html_inline , NULL , DECL a_attrs , DECL target_attr, NULL
516 },
517 { "abbr",       0, 0, 0, 0, 0, 0, 1, "abbreviated form",
518         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
519 },
520 { "acronym",    0, 0, 0, 0, 0, 0, 1, "",
521         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
522 },
523 { "address",    0, 0, 0, 0, 0, 0, 0, "information on author ",
524         DECL inline_p  , NULL , DECL html_attrs, NULL, NULL
525 },
526 { "applet",     0, 0, 0, 0, 1, 1, 2, "java applet ",
527         DECL flow_param , NULL , NULL , DECL applet_attrs, NULL
528 },
529 { "area",       0, 2, 2, 1, 0, 0, 0, "client-side image map area ",
530         EMPTY ,  NULL , DECL area_attrs , DECL target_attr, DECL alt_attr
531 },
532 { "b",          0, 3, 0, 0, 0, 0, 1, "bold text style",
533         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
534 },
535 { "base",       0, 2, 2, 1, 0, 0, 0, "document base uri ",
536         EMPTY , NULL , NULL , DECL target_attr, DECL href_attrs
537 },
538 { "basefont",   0, 2, 2, 1, 1, 1, 1, "base font size " ,
539         EMPTY , NULL , NULL, DECL basefont_attrs, NULL
540 },
541 { "bdo",        0, 0, 0, 0, 0, 0, 1, "i18n bidi over-ride ",
542         DECL html_inline , NULL , DECL core_i18n_attrs, NULL, DECL dir_attr
543 },
544 { "big",        0, 3, 0, 0, 0, 0, 1, "large text style",
545         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
546 },
547 { "blockquote", 0, 0, 0, 0, 0, 0, 0, "long quotation ",
548         DECL html_flow , NULL , DECL quote_attrs , NULL, NULL
549 },
550 { "body",       1, 1, 0, 0, 0, 0, 0, "document body ",
551         DECL body_contents , "div" , DECL body_attrs, DECL body_depr, NULL
552 },
553 { "br",         0, 2, 2, 1, 0, 0, 1, "forced line break ",
554         EMPTY , NULL , DECL core_attrs, DECL clear_attrs , NULL
555 },
556 { "button",     0, 0, 0, 0, 0, 0, 2, "push button ",
557         DECL html_flow MODIFIER , NULL , DECL button_attrs, NULL, NULL
558 },
559 { "caption",    0, 0, 0, 0, 0, 0, 0, "table caption ",
560         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
561 },
562 { "center",     0, 3, 0, 0, 1, 1, 0, "shorthand for div align=center ",
563         DECL html_flow , NULL , NULL, DECL html_attrs, NULL
564 },
565 { "cite",       0, 0, 0, 0, 0, 0, 1, "citation",
566         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
567 },
568 { "code",       0, 0, 0, 0, 0, 0, 1, "computer code fragment",
569         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
570 },
571 { "col",        0, 2, 2, 1, 0, 0, 0, "table column ",
572         EMPTY , NULL , DECL col_attrs , NULL, NULL
573 },
574 { "colgroup",   0, 1, 0, 0, 0, 0, 0, "table column group ",
575         DECL col_elt , "col" , DECL col_attrs , NULL, NULL
576 },
577 { "dd",         0, 1, 0, 0, 0, 0, 0, "definition description ",
578         DECL html_flow , NULL , DECL html_attrs, NULL, NULL
579 },
580 { "del",        0, 0, 0, 0, 0, 0, 2, "deleted text ",
581         DECL html_flow , NULL , DECL edit_attrs , NULL, NULL
582 },
583 { "dfn",        0, 0, 0, 0, 0, 0, 1, "instance definition",
584         DECL html_inline , NULL , DECL html_attrs, NULL, NULL
585 },
586 { "dir",        0, 0, 0, 0, 1, 1, 0, "directory list",
587         DECL blockli_elt, "li" , NULL, DECL compact_attrs, NULL
588 },
589 { "div",        0, 0, 0, 0, 0, 0, 0, "generic language/style container",
590         DECL html_flow, NULL, DECL html_attrs, DECL align_attr, NULL
591 },
592 { "dl",         0, 0, 0, 0, 0, 0, 0, "definition list ",
593         DECL dl_contents , "dd" , html_attrs, DECL compact_attr, NULL
594 },
595 { "dt",         0, 1, 0, 0, 0, 0, 0, "definition term ",
596         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
597 },
598 { "em",         0, 3, 0, 0, 0, 0, 1, "emphasis",
599         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
600 },
601 { "fieldset",   0, 0, 0, 0, 0, 0, 0, "form control group ",
602         DECL fieldset_contents , NULL, DECL html_attrs, NULL, NULL
603 },
604 { "font",       0, 3, 0, 0, 1, 1, 1, "local change to font ",
605         DECL html_inline, NULL, NULL, DECL font_attrs, NULL
606 },
607 { "form",       0, 0, 0, 0, 0, 0, 0, "interactive form ",
608         DECL form_contents, "fieldset", DECL form_attrs , DECL target_attr, DECL action_attr
609 },
610 { "frame",      0, 2, 2, 1, 0, 2, 0, "subwindow " ,
611         EMPTY, NULL, NULL, DECL frame_attrs, NULL
612 },
613 { "frameset",   0, 0, 0, 0, 0, 2, 0, "window subdivision" ,
614         DECL frameset_contents, "noframes" , NULL , DECL frameset_attrs, NULL
615 },
616 { "h1",         0, 0, 0, 0, 0, 0, 0, "heading ",
617         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
618 },
619 { "h2",         0, 0, 0, 0, 0, 0, 0, "heading ",
620         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
621 },
622 { "h3",         0, 0, 0, 0, 0, 0, 0, "heading ",
623         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
624 },
625 { "h4",         0, 0, 0, 0, 0, 0, 0, "heading ",
626         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
627 },
628 { "h5",         0, 0, 0, 0, 0, 0, 0, "heading ",
629         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
630 },
631 { "h6",         0, 0, 0, 0, 0, 0, 0, "heading ",
632         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
633 },
634 { "head",       1, 1, 0, 0, 0, 0, 0, "document head ",
635         DECL head_contents, NULL, DECL head_attrs, NULL, NULL
636 },
637 { "hr",         0, 2, 2, 1, 0, 0, 0, "horizontal rule " ,
638         EMPTY, NULL, DECL html_attrs, DECL hr_depr, NULL
639 },
640 { "html",       1, 1, 0, 0, 0, 0, 0, "document root element ",
641         DECL html_content , NULL , DECL i18n_attrs, DECL version_attr, NULL
642 },
643 { "i",          0, 3, 0, 0, 0, 0, 1, "italic text style",
644         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
645 },
646 { "iframe",     0, 0, 0, 0, 0, 1, 2, "inline subwindow ",
647         DECL html_flow, NULL, NULL, DECL iframe_attrs, NULL
648 },
649 { "img",        0, 2, 2, 1, 0, 0, 1, "embedded image ",
650         EMPTY, NULL, DECL img_attrs, DECL align_attr, src_alt_attrs
651 },
652 { "input",      0, 2, 2, 1, 0, 0, 1, "form control ",
653         EMPTY, NULL, DECL input_attrs , DECL align_attr, NULL
654 },
655 { "ins",        0, 0, 0, 0, 0, 0, 2, "inserted text",
656         DECL html_flow, NULL, DECL edit_attrs, NULL, NULL
657 },
658 { "isindex",    0, 2, 2, 1, 1, 1, 0, "single line prompt ",
659         EMPTY, NULL, NULL, DECL prompt_attrs, NULL
660 },
661 { "kbd",        0, 0, 0, 0, 0, 0, 1, "text to be entered by the user",
662         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
663 },
664 { "label",      0, 0, 0, 0, 0, 0, 1, "form field label text ",
665         DECL html_inline MODIFIER, NULL, DECL label_attrs , NULL, NULL
666 },
667 { "legend",     0, 0, 0, 0, 0, 0, 0, "fieldset legend ",
668         DECL html_inline, NULL, DECL legend_attrs , DECL align_attr, NULL
669 },
670 { "li",         0, 1, 1, 0, 0, 0, 0, "list item ",
671         DECL html_flow, NULL, DECL html_attrs, NULL, NULL
672 },
673 { "link",       0, 2, 2, 1, 0, 0, 0, "a media-independent link ",
674         EMPTY, NULL, DECL link_attrs, DECL target_attr, NULL
675 },
676 { "map",        0, 0, 0, 0, 0, 0, 2, "client-side image map ",
677         DECL map_contents , NULL, DECL html_attrs , NULL, name_attr
678 },
679 { "menu",       0, 0, 0, 0, 1, 1, 0, "menu list ",
680         DECL blockli_elt , NULL, NULL, DECL compact_attrs, NULL
681 },
682 { "meta",       0, 2, 2, 1, 0, 0, 0, "generic metainformation ",
683         EMPTY, NULL, DECL meta_attrs , NULL , DECL content_attr
684 },
685 { "noframes",   0, 0, 0, 0, 0, 2, 0, "alternate content container for non frame-based rendering ",
686         DECL noframes_content, "body" , DECL html_attrs, NULL, NULL
687 },
688 { "noscript",   0, 0, 0, 0, 0, 0, 0, "alternate content container for non script-based rendering ",
689         DECL html_flow, "div", DECL html_attrs, NULL, NULL
690 },
691 { "object",     0, 0, 0, 0, 0, 0, 2, "generic embedded object ",
692         DECL object_contents , "div" , DECL object_attrs, DECL object_depr, NULL
693 },
694 { "ol",         0, 0, 0, 0, 0, 0, 0, "ordered list ",
695         DECL li_elt , "li" , DECL html_attrs, DECL ol_attrs, NULL
696 },
697 { "optgroup",   0, 0, 0, 0, 0, 0, 0, "option group ",
698         option_elt , "option", DECL optgroup_attrs, NULL, DECL label_attr
699 },
700 { "option",     0, 1, 0, 0, 0, 0, 0, "selectable choice " ,
701         DECL html_pcdata, NULL, DECL option_attrs, NULL, NULL
702 },
703 { "p",          0, 1, 0, 0, 0, 0, 0, "paragraph ",
704         DECL html_inline, NULL, DECL html_attrs, DECL align_attr, NULL
705 },
706 { "param",      0, 2, 2, 1, 0, 0, 0, "named property value ",
707         EMPTY, NULL, DECL param_attrs, NULL, name_attr
708 },
709 { "pre",        0, 0, 0, 0, 0, 0, 0, "preformatted text ",
710         DECL pre_content, NULL, DECL html_attrs, DECL width_attr, NULL
711 },
712 { "q",          0, 0, 0, 0, 0, 0, 1, "short inline quotation ",
713         DECL html_inline, NULL, DECL quote_attrs, NULL, NULL
714 },
715 { "s",          0, 3, 0, 0, 1, 1, 1, "strike-through text style",
716         DECL html_inline, NULL, NULL, DECL html_attrs, NULL
717 },
718 { "samp",       0, 0, 0, 0, 0, 0, 1, "sample program output, scripts, etc.",
719         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
720 },
721 { "script",     0, 0, 0, 0, 0, 0, 2, "script statements ",
722         DECL html_cdata, NULL, DECL script_attrs, DECL language_attr, DECL type_attr
723 },
724 { "select",     0, 0, 0, 0, 0, 0, 1, "option selector ",
725         DECL select_content, NULL, DECL select_attrs, NULL, NULL
726 },
727 { "small",      0, 3, 0, 0, 0, 0, 1, "small text style",
728         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
729 },
730 { "span",       0, 0, 0, 0, 0, 0, 1, "generic language/style container ",
731         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
732 },
733 { "strike",     0, 3, 0, 0, 1, 1, 1, "strike-through text",
734         DECL html_inline, NULL, NULL, DECL html_attrs, NULL
735 },
736 { "strong",     0, 3, 0, 0, 0, 0, 1, "strong emphasis",
737         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
738 },
739 { "style",      0, 0, 0, 0, 0, 0, 0, "style info ",
740         DECL html_cdata, NULL, DECL style_attrs, NULL, DECL type_attr
741 },
742 { "sub",        0, 3, 0, 0, 0, 0, 1, "subscript",
743         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
744 },
745 { "sup",        0, 3, 0, 0, 0, 0, 1, "superscript ",
746         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
747 },
748 { "table",      0, 0, 0, 0, 0, 0, 0, "",
749         DECL table_contents , "tr" , DECL table_attrs , DECL table_depr, NULL
750 },
751 { "tbody",      1, 0, 0, 0, 0, 0, 0, "table body ",
752         DECL tr_elt , "tr" , DECL talign_attrs, NULL, NULL
753 },
754 { "td",         0, 0, 0, 0, 0, 0, 0, "table data cell",
755         DECL html_flow, NULL, DECL th_td_attr, DECL th_td_depr, NULL
756 },
757 { "textarea",   0, 0, 0, 0, 0, 0, 1, "multi-line text field ",
758         DECL html_pcdata, NULL, DECL textarea_attrs, NULL, DECL rows_cols_attr
759 },
760 { "tfoot",      0, 1, 0, 0, 0, 0, 0, "table footer ",
761         DECL tr_elt , "tr" , DECL talign_attrs, NULL, NULL
762 },
763 { "th",         0, 1, 0, 0, 0, 0, 0, "table header cell",
764         DECL html_flow, NULL, DECL th_td_attr, DECL th_td_depr, NULL
765 },
766 { "thead",      0, 1, 0, 0, 0, 0, 0, "table header ",
767         DECL tr_elt , "tr" , DECL talign_attrs, NULL, NULL
768 },
769 { "title",      0, 0, 0, 0, 0, 0, 0, "document title ",
770         DECL html_pcdata, NULL, DECL i18n_attrs, NULL, NULL
771 },
772 { "tr",         0, 0, 0, 0, 0, 0, 0, "table row ",
773         DECL tr_contents , "td" , DECL talign_attrs, DECL bgcolor_attr, NULL
774 },
775 { "tt",         0, 3, 0, 0, 0, 0, 1, "teletype or monospaced text style",
776         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
777 },
778 { "u",          0, 3, 0, 0, 1, 1, 1, "underlined text style",
779         DECL html_inline, NULL, NULL, DECL html_attrs, NULL
780 },
781 { "ul",         0, 0, 0, 0, 0, 0, 0, "unordered list ",
782         DECL li_elt , "li" , DECL html_attrs, DECL ul_depr, NULL
783 },
784 { "var",        0, 0, 0, 0, 0, 0, 1, "instance of a variable or program argument",
785         DECL html_inline, NULL, DECL html_attrs, NULL, NULL
786 }
787 };
788
789 /*
790  * start tags that imply the end of current element
791  */
792 static const char *htmlStartClose[] = {
793 "form",         "form", "p", "hr", "h1", "h2", "h3", "h4", "h5", "h6",
794                 "dl", "ul", "ol", "menu", "dir", "address", "pre",
795                 "listing", "xmp", "head", NULL,
796 "head",         "p", NULL,
797 "title",        "p", NULL,
798 "body",         "head", "style", "link", "title", "p", NULL,
799 "li",           "p", "h1", "h2", "h3", "h4", "h5", "h6", "dl", "address",
800                 "pre", "listing", "xmp", "head", "li", NULL,
801 "hr",           "p", "head", NULL,
802 "h1",           "p", "head", NULL,
803 "h2",           "p", "head", NULL,
804 "h3",           "p", "head", NULL,
805 "h4",           "p", "head", NULL,
806 "h5",           "p", "head", NULL,
807 "h6",           "p", "head", NULL,
808 "dir",          "p", "head", NULL,
809 "address",      "p", "head", "ul", NULL,
810 "pre",          "p", "head", "ul", NULL,
811 "listing",      "p", "head", NULL,
812 "xmp",          "p", "head", NULL,
813 "blockquote",   "p", "head", NULL,
814 "dl",           "p", "dt", "menu", "dir", "address", "pre", "listing",
815                 "xmp", "head", NULL,
816 "dt",           "p", "menu", "dir", "address", "pre", "listing", "xmp",
817                 "head", "dd", NULL,
818 "dd",           "p", "menu", "dir", "address", "pre", "listing", "xmp",
819                 "head", "dt", NULL,
820 "ul",           "p", "head", "ol", "menu", "dir", "address", "pre",
821                 "listing", "xmp", NULL,
822 "ol",           "p", "head", "ul", NULL,
823 "menu",         "p", "head", "ul", NULL,
824 "p",            "p", "head", "h1", "h2", "h3", "h4", "h5", "h6", NULL,
825 "div",          "p", "head", NULL,
826 "noscript",     "p", "head", NULL,
827 "center",       "font", "b", "i", "p", "head", NULL,
828 "a",            "a", NULL,
829 "caption",      "p", NULL,
830 "colgroup",     "caption", "colgroup", "col", "p", NULL,
831 "col",          "caption", "col", "p", NULL,
832 "table",        "p", "head", "h1", "h2", "h3", "h4", "h5", "h6", "pre",
833                 "listing", "xmp", "a", NULL,
834 "th",           "th", "td", "p", "span", "font", "a", "b", "i", "u", NULL,
835 "td",           "th", "td", "p", "span", "font", "a", "b", "i", "u", NULL,      
836 "tr",           "th", "td", "tr", "caption", "col", "colgroup", "p", NULL,
837 "thead",        "caption", "col", "colgroup", NULL,
838 "tfoot",        "th", "td", "tr", "caption", "col", "colgroup", "thead",
839                 "tbody", "p", NULL,
840 "tbody",        "th", "td", "tr", "caption", "col", "colgroup", "thead",
841                 "tfoot", "tbody", "p", NULL,
842 "optgroup",     "option", NULL,
843 "option",       "option", NULL,
844 "fieldset",     "legend", "p", "head", "h1", "h2", "h3", "h4", "h5", "h6",
845                 "pre", "listing", "xmp", "a", NULL,
846 NULL
847 };
848
849 /*
850  * The list of HTML elements which are supposed not to have
851  * CDATA content and where a p element will be implied
852  *
853  * TODO: extend that list by reading the HTML SGML DTD on
854  *       implied paragraph
855  */
856 static const char *htmlNoContentElements[] = {
857     "html",
858     "head",
859     "body",
860     NULL
861 };
862
863 /*
864  * The list of HTML attributes which are of content %Script;
865  * NOTE: when adding ones, check htmlIsScriptAttribute() since
866  *       it assumes the name starts with 'on'
867  */
868 static const char *htmlScriptAttributes[] = {
869     "onclick",
870     "ondblclick",
871     "onmousedown",
872     "onmouseup",
873     "onmouseover",
874     "onmousemove",
875     "onmouseout",
876     "onkeypress",
877     "onkeydown",
878     "onkeyup",
879     "onload",
880     "onunload",
881     "onfocus",
882     "onblur",
883     "onsubmit",
884     "onrest",
885     "onchange",
886     "onselect"
887 };
888
889 /*
890  * This table is used by the htmlparser to know what to do with
891  * broken html pages. By assigning different priorities to different
892  * elements the parser can decide how to handle extra endtags.
893  * Endtags are only allowed to close elements with lower or equal
894  * priority.
895  */ 
896
897 typedef struct {
898     const char *name;
899     int priority;
900 } elementPriority;
901
902 static const elementPriority htmlEndPriority[] = {
903     {"div",   150},
904     {"td",    160},
905     {"th",    160},
906     {"tr",    170},
907     {"thead", 180},
908     {"tbody", 180},
909     {"tfoot", 180},
910     {"table", 190},
911     {"head",  200},
912     {"body",  200},
913     {"html",  220},
914     {NULL,    100} /* Default priority */
915 };
916
917 static const char** htmlStartCloseIndex[100];
918 static int htmlStartCloseIndexinitialized = 0;
919
920 /************************************************************************
921  *                                                                      *
922  *              functions to handle HTML specific data                  *
923  *                                                                      *
924  ************************************************************************/
925
926 /**
927  * htmlInitAutoClose:
928  *
929  * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
930  * This is not reentrant. Call xmlInitParser() once before processing in
931  * case of use in multithreaded programs.
932  */
933 void
934 htmlInitAutoClose(void) {
935     int indx, i = 0;
936
937     if (htmlStartCloseIndexinitialized) return;
938
939     for (indx = 0;indx < 100;indx ++) htmlStartCloseIndex[indx] = NULL;
940     indx = 0;
941     while ((htmlStartClose[i] != NULL) && (indx < 100 - 1)) {
942         htmlStartCloseIndex[indx++] = &htmlStartClose[i];
943         while (htmlStartClose[i] != NULL) i++;
944         i++;
945     }
946     htmlStartCloseIndexinitialized = 1;
947 }
948
949 /**
950  * htmlTagLookup:
951  * @tag:  The tag name in lowercase
952  *
953  * Lookup the HTML tag in the ElementTable
954  *
955  * Returns the related htmlElemDescPtr or NULL if not found.
956  */
957 const htmlElemDesc *
958 htmlTagLookup(const xmlChar *tag) {
959     unsigned int i;
960
961     for (i = 0; i < (sizeof(html40ElementTable) /
962                      sizeof(html40ElementTable[0]));i++) {
963         if (!xmlStrcasecmp(tag, BAD_CAST html40ElementTable[i].name))
964             return((const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) (const htmlElemDescPtr) &html40ElementTable[i]);
965     }
966     return(NULL);
967 }
968
969 /**
970  * htmlGetEndPriority:
971  * @name: The name of the element to look up the priority for.
972  * 
973  * Return value: The "endtag" priority.
974  **/
975 static int
976 htmlGetEndPriority (const xmlChar *name) {
977         int i = 0;
978
979         while ((htmlEndPriority[i].name != NULL) &&
980                (!xmlStrEqual((const xmlChar *)htmlEndPriority[i].name, name)))
981             i++;
982
983         return(htmlEndPriority[i].priority);
984 }
985
986 /**
987  * htmlCheckAutoClose:
988  * @newtag:  The new tag name
989  * @oldtag:  The old tag name
990  *
991  * Checks whether the new tag is one of the registered valid tags for
992  * closing old.
993  * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
994  *
995  * Returns 0 if no, 1 if yes.
996  */
997 static int
998 htmlCheckAutoClose(const xmlChar *newtag, const xmlChar *oldtag) {
999     int i, indx;
1000     const char **closed = NULL;
1001
1002     if (htmlStartCloseIndexinitialized == 0) htmlInitAutoClose();
1003
1004     /* inefficient, but not a big deal */
1005     for (indx = 0; indx < 100;indx++) {
1006         closed = htmlStartCloseIndex[indx];
1007         if (closed == NULL) return(0);
1008         if (xmlStrEqual(BAD_CAST *closed, newtag)) break;
1009     }
1010
1011     i = closed - htmlStartClose;
1012     i++;
1013     while (htmlStartClose[i] != NULL) {
1014         if (xmlStrEqual(BAD_CAST htmlStartClose[i], oldtag)) {
1015             return(1);
1016         }
1017         i++;
1018     }
1019     return(0);
1020 }
1021
1022 /**
1023  * htmlAutoCloseOnClose:
1024  * @ctxt:  an HTML parser context
1025  * @newtag:  The new tag name
1026  * @force:  force the tag closure
1027  *
1028  * The HTML DTD allows an ending tag to implicitly close other tags.
1029  */
1030 static void
1031 htmlAutoCloseOnClose(htmlParserCtxtPtr ctxt, const xmlChar *newtag) {
1032     const htmlElemDesc * info;
1033     xmlChar *oldname;
1034     int i, priority;
1035
1036 #ifdef DEBUG
1037     xmlGenericError(xmlGenericErrorContext,"Close of %s stack: %d elements\n", newtag, ctxt->nameNr);
1038     for (i = 0;i < ctxt->nameNr;i++) 
1039         xmlGenericError(xmlGenericErrorContext,"%d : %s\n", i, ctxt->nameTab[i]);
1040 #endif
1041
1042     priority = htmlGetEndPriority (newtag);
1043
1044     for (i = (ctxt->nameNr - 1);i >= 0;i--) {
1045
1046         if (xmlStrEqual(newtag, ctxt->nameTab[i])) break;
1047         /*
1048          * A missplaced endtag can only close elements with lower
1049          * or equal priority, so if we find an element with higher
1050          * priority before we find an element with
1051          * matching name, we just ignore this endtag 
1052          */
1053         if (htmlGetEndPriority (ctxt->nameTab[i]) > priority) return;
1054     }
1055     if (i < 0) return;
1056
1057     while (!xmlStrEqual(newtag, ctxt->name)) {
1058         info = htmlTagLookup(ctxt->name);
1059         if ((info == NULL) || (info->endTag == 1)) {
1060 #ifdef DEBUG
1061             xmlGenericError(xmlGenericErrorContext,"htmlAutoCloseOnClose: %s closes %s\n", newtag, ctxt->name);
1062 #endif
1063         } else if (info->endTag == 3) {
1064 #ifdef DEBUG
1065             xmlGenericError(xmlGenericErrorContext,"End of tag %s: expecting %s\n", newtag, ctxt->name);
1066
1067 #endif
1068             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1069                 ctxt->sax->error(ctxt->userData,
1070                  "Opening and ending tag mismatch: %s and %s\n",
1071                                  newtag, ctxt->name);
1072             ctxt->wellFormed = 0;
1073         }
1074         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1075             ctxt->sax->endElement(ctxt->userData, ctxt->name);
1076         oldname = htmlnamePop(ctxt);
1077         if (oldname != NULL) {
1078 #ifdef DEBUG
1079             xmlGenericError(xmlGenericErrorContext,"htmlAutoCloseOnClose: popped %s\n", oldname);
1080 #endif
1081             xmlFree(oldname);
1082         }       
1083     }
1084 }
1085
1086 /**
1087  * htmlAutoCloseOnEnd:
1088  * @ctxt:  an HTML parser context
1089  *
1090  * Close all remaining tags at the end of the stream
1091  */
1092 static void
1093 htmlAutoCloseOnEnd(htmlParserCtxtPtr ctxt) {
1094     xmlChar *oldname;
1095     int i;
1096
1097     if (ctxt->nameNr == 0)
1098         return;
1099 #ifdef DEBUG
1100     xmlGenericError(xmlGenericErrorContext,"Close of stack: %d elements\n", ctxt->nameNr);
1101 #endif
1102
1103     for (i = (ctxt->nameNr - 1);i >= 0;i--) {
1104 #ifdef DEBUG
1105         xmlGenericError(xmlGenericErrorContext,"%d : %s\n", i, ctxt->nameTab[i]);
1106 #endif
1107         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1108             ctxt->sax->endElement(ctxt->userData, ctxt->name);
1109         oldname = htmlnamePop(ctxt);
1110         if (oldname != NULL) {
1111 #ifdef DEBUG
1112             xmlGenericError(xmlGenericErrorContext,"htmlAutoCloseOnEnd: popped %s\n", oldname);
1113 #endif
1114             xmlFree(oldname);
1115         }       
1116     }
1117 }
1118
1119 /**
1120  * htmlAutoClose:
1121  * @ctxt:  an HTML parser context
1122  * @newtag:  The new tag name or NULL
1123  *
1124  * The HTML DTD allows a tag to implicitly close other tags.
1125  * The list is kept in htmlStartClose array. This function is
1126  * called when a new tag has been detected and generates the
1127  * appropriates closes if possible/needed.
1128  * If newtag is NULL this mean we are at the end of the resource
1129  * and we should check 
1130  */
1131 static void
1132 htmlAutoClose(htmlParserCtxtPtr ctxt, const xmlChar *newtag) {
1133     xmlChar *oldname;
1134     while ((newtag != NULL) && (ctxt->name != NULL) && 
1135            (htmlCheckAutoClose(newtag, ctxt->name))) {
1136 #ifdef DEBUG
1137         xmlGenericError(xmlGenericErrorContext,"htmlAutoClose: %s closes %s\n", newtag, ctxt->name);
1138 #endif
1139         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1140             ctxt->sax->endElement(ctxt->userData, ctxt->name);
1141         oldname = htmlnamePop(ctxt);
1142         if (oldname != NULL) {
1143 #ifdef DEBUG
1144             xmlGenericError(xmlGenericErrorContext,"htmlAutoClose: popped %s\n", oldname);
1145 #endif
1146             xmlFree(oldname);
1147         }
1148     }
1149     if (newtag == NULL) {
1150         htmlAutoCloseOnEnd(ctxt);
1151         return;
1152     }
1153     while ((newtag == NULL) && (ctxt->name != NULL) &&
1154            ((xmlStrEqual(ctxt->name, BAD_CAST"head")) ||
1155             (xmlStrEqual(ctxt->name, BAD_CAST"body")) ||
1156             (xmlStrEqual(ctxt->name, BAD_CAST"html")))) {
1157 #ifdef DEBUG
1158         xmlGenericError(xmlGenericErrorContext,"htmlAutoClose: EOF closes %s\n", ctxt->name);
1159 #endif
1160         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1161             ctxt->sax->endElement(ctxt->userData, ctxt->name);
1162         oldname = htmlnamePop(ctxt);
1163         if (oldname != NULL) {
1164 #ifdef DEBUG
1165             xmlGenericError(xmlGenericErrorContext,"htmlAutoClose: popped %s\n", oldname);
1166 #endif
1167             xmlFree(oldname);
1168         }
1169    }
1170
1171 }
1172
1173 /**
1174  * htmlAutoCloseTag:
1175  * @doc:  the HTML document
1176  * @name:  The tag name
1177  * @elem:  the HTML element
1178  *
1179  * The HTML DTD allows a tag to implicitly close other tags.
1180  * The list is kept in htmlStartClose array. This function checks
1181  * if the element or one of it's children would autoclose the
1182  * given tag.
1183  *
1184  * Returns 1 if autoclose, 0 otherwise
1185  */
1186 int
1187 htmlAutoCloseTag(htmlDocPtr doc, const xmlChar *name, htmlNodePtr elem) {
1188     htmlNodePtr child;
1189
1190     if (elem == NULL) return(1);
1191     if (xmlStrEqual(name, elem->name)) return(0);
1192     if (htmlCheckAutoClose(elem->name, name)) return(1);
1193     child = elem->children;
1194     while (child != NULL) {
1195         if (htmlAutoCloseTag(doc, name, child)) return(1);
1196         child = child->next;
1197     }
1198     return(0);
1199 }
1200
1201 /**
1202  * htmlIsAutoClosed:
1203  * @doc:  the HTML document
1204  * @elem:  the HTML element
1205  *
1206  * The HTML DTD allows a tag to implicitly close other tags.
1207  * The list is kept in htmlStartClose array. This function checks
1208  * if a tag is autoclosed by one of it's child
1209  *
1210  * Returns 1 if autoclosed, 0 otherwise
1211  */
1212 int
1213 htmlIsAutoClosed(htmlDocPtr doc, htmlNodePtr elem) {
1214     htmlNodePtr child;
1215
1216     if (elem == NULL) return(1);
1217     child = elem->children;
1218     while (child != NULL) {
1219         if (htmlAutoCloseTag(doc, elem->name, child)) return(1);
1220         child = child->next;
1221     }
1222     return(0);
1223 }
1224
1225 /**
1226  * htmlCheckImplied:
1227  * @ctxt:  an HTML parser context
1228  * @newtag:  The new tag name
1229  *
1230  * The HTML DTD allows a tag to exists only implicitly
1231  * called when a new tag has been detected and generates the
1232  * appropriates implicit tags if missing
1233  */
1234 static void
1235 htmlCheckImplied(htmlParserCtxtPtr ctxt, const xmlChar *newtag) {
1236     if (!htmlOmittedDefaultValue)
1237         return;
1238     if (xmlStrEqual(newtag, BAD_CAST"html"))
1239         return;
1240     if (ctxt->nameNr <= 0) {
1241 #ifdef DEBUG
1242         xmlGenericError(xmlGenericErrorContext,"Implied element html: pushed html\n");
1243 #endif    
1244         htmlnamePush(ctxt, xmlStrdup(BAD_CAST"html"));
1245         if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1246             ctxt->sax->startElement(ctxt->userData, BAD_CAST"html", NULL);
1247     }
1248     if ((xmlStrEqual(newtag, BAD_CAST"body")) || (xmlStrEqual(newtag, BAD_CAST"head")))
1249         return;
1250     if ((ctxt->nameNr <= 1) && 
1251         ((xmlStrEqual(newtag, BAD_CAST"script")) ||
1252          (xmlStrEqual(newtag, BAD_CAST"style")) ||
1253          (xmlStrEqual(newtag, BAD_CAST"meta")) ||
1254          (xmlStrEqual(newtag, BAD_CAST"link")) ||
1255          (xmlStrEqual(newtag, BAD_CAST"title")) ||
1256          (xmlStrEqual(newtag, BAD_CAST"base")))) {
1257             /* 
1258              * dropped OBJECT ... i you put it first BODY will be
1259              * assumed !
1260              */
1261 #ifdef DEBUG
1262             xmlGenericError(xmlGenericErrorContext,"Implied element head: pushed head\n");
1263 #endif    
1264             htmlnamePush(ctxt, xmlStrdup(BAD_CAST"head"));
1265             if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1266                 ctxt->sax->startElement(ctxt->userData, BAD_CAST"head", NULL);
1267     } else if ((!xmlStrEqual(newtag, BAD_CAST"noframes")) &&
1268                (!xmlStrEqual(newtag, BAD_CAST"frame")) &&
1269                (!xmlStrEqual(newtag, BAD_CAST"frameset"))) {
1270         int i;
1271         for (i = 0;i < ctxt->nameNr;i++) {
1272             if (xmlStrEqual(ctxt->nameTab[i], BAD_CAST"body")) {
1273                 return;
1274             }
1275             if (xmlStrEqual(ctxt->nameTab[i], BAD_CAST"head")) {
1276                 return;
1277             }
1278         }
1279             
1280 #ifdef DEBUG
1281         xmlGenericError(xmlGenericErrorContext,"Implied element body: pushed body\n");
1282 #endif    
1283         htmlnamePush(ctxt, xmlStrdup(BAD_CAST"body"));
1284         if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1285             ctxt->sax->startElement(ctxt->userData, BAD_CAST"body", NULL);
1286     }
1287 }
1288
1289 /**
1290  * htmlCheckParagraph
1291  * @ctxt:  an HTML parser context
1292  *
1293  * Check whether a p element need to be implied before inserting
1294  * characters in the current element.
1295  *
1296  * Returns 1 if a paragraph has been inserted, 0 if not and -1
1297  *         in case of error.
1298  */
1299
1300 static int
1301 htmlCheckParagraph(htmlParserCtxtPtr ctxt) {
1302     const xmlChar *tag;
1303     int i;
1304
1305     if (ctxt == NULL)
1306         return(-1);
1307     tag = ctxt->name;
1308     if (tag == NULL) {
1309         htmlAutoClose(ctxt, BAD_CAST"p");
1310         htmlCheckImplied(ctxt, BAD_CAST"p");
1311         htmlnamePush(ctxt, xmlStrdup(BAD_CAST"p"));
1312         if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1313             ctxt->sax->startElement(ctxt->userData, BAD_CAST"p", NULL);
1314         return(1);
1315     }
1316     if (!htmlOmittedDefaultValue)
1317         return(0);
1318     for (i = 0; htmlNoContentElements[i] != NULL; i++) {
1319         if (xmlStrEqual(tag, BAD_CAST htmlNoContentElements[i])) {
1320 #ifdef DEBUG
1321             xmlGenericError(xmlGenericErrorContext,"Implied element paragraph\n");
1322 #endif    
1323             htmlAutoClose(ctxt, BAD_CAST"p");
1324             htmlCheckImplied(ctxt, BAD_CAST"p");
1325             htmlnamePush(ctxt, xmlStrdup(BAD_CAST"p"));
1326             if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1327                 ctxt->sax->startElement(ctxt->userData, BAD_CAST"p", NULL);
1328             return(1);
1329         }
1330     }
1331     return(0);
1332 }
1333
1334 /**
1335  * htmlIsScriptAttribute:
1336  * @name:  an attribute name
1337  *
1338  * Check if an attribute is of content type Script
1339  *
1340  * Returns 1 is the attribute is a script 0 otherwise
1341  */
1342 int
1343 htmlIsScriptAttribute(const xmlChar *name) {
1344     unsigned int i;
1345
1346     if (name == NULL)
1347         return(0);
1348     /*
1349      * all script attributes start with 'on'
1350      */
1351     if ((name[0] != 'o') || (name[1] != 'n'))
1352         return(0);
1353     for (i = 0;
1354          i < sizeof(htmlScriptAttributes)/sizeof(htmlScriptAttributes[0]);
1355          i++) {
1356         if (xmlStrEqual(name, (const xmlChar *) htmlScriptAttributes[i]))
1357             return(1);
1358     }
1359     return(0);
1360 }
1361
1362 /************************************************************************
1363  *                                                                      *
1364  *              The list of HTML predefined entities                    *
1365  *                                                                      *
1366  ************************************************************************/
1367
1368
1369 static const htmlEntityDesc  html40EntitiesTable[] = {
1370 /*
1371  * the 4 absolute ones, plus apostrophe.
1372  */
1373 { 34,   "quot", "quotation mark = APL quote, U+0022 ISOnum" },
1374 { 38,   "amp",  "ampersand, U+0026 ISOnum" },
1375 { 39,   "apos", "single quote" },
1376 { 60,   "lt",   "less-than sign, U+003C ISOnum" },
1377 { 62,   "gt",   "greater-than sign, U+003E ISOnum" },
1378
1379 /*
1380  * A bunch still in the 128-255 range
1381  * Replacing them depend really on the charset used.
1382  */
1383 { 160,  "nbsp", "no-break space = non-breaking space, U+00A0 ISOnum" },
1384 { 161,  "iexcl","inverted exclamation mark, U+00A1 ISOnum" },
1385 { 162,  "cent", "cent sign, U+00A2 ISOnum" },
1386 { 163,  "pound","pound sign, U+00A3 ISOnum" },
1387 { 164,  "curren","currency sign, U+00A4 ISOnum" },
1388 { 165,  "yen",  "yen sign = yuan sign, U+00A5 ISOnum" },
1389 { 166,  "brvbar","broken bar = broken vertical bar, U+00A6 ISOnum" },
1390 { 167,  "sect", "section sign, U+00A7 ISOnum" },
1391 { 168,  "uml",  "diaeresis = spacing diaeresis, U+00A8 ISOdia" },
1392 { 169,  "copy", "copyright sign, U+00A9 ISOnum" },
1393 { 170,  "ordf", "feminine ordinal indicator, U+00AA ISOnum" },
1394 { 171,  "laquo","left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum" },
1395 { 172,  "not",  "not sign, U+00AC ISOnum" },
1396 { 173,  "shy",  "soft hyphen = discretionary hyphen, U+00AD ISOnum" },
1397 { 174,  "reg",  "registered sign = registered trade mark sign, U+00AE ISOnum" },
1398 { 175,  "macr", "macron = spacing macron = overline = APL overbar, U+00AF ISOdia" },
1399 { 176,  "deg",  "degree sign, U+00B0 ISOnum" },
1400 { 177,  "plusmn","plus-minus sign = plus-or-minus sign, U+00B1 ISOnum" },
1401 { 178,  "sup2", "superscript two = superscript digit two = squared, U+00B2 ISOnum" },
1402 { 179,  "sup3", "superscript three = superscript digit three = cubed, U+00B3 ISOnum" },
1403 { 180,  "acute","acute accent = spacing acute, U+00B4 ISOdia" },
1404 { 181,  "micro","micro sign, U+00B5 ISOnum" },
1405 { 182,  "para", "pilcrow sign = paragraph sign, U+00B6 ISOnum" },
1406 { 183,  "middot","middle dot = Georgian comma Greek middle dot, U+00B7 ISOnum" },
1407 { 184,  "cedil","cedilla = spacing cedilla, U+00B8 ISOdia" },
1408 { 185,  "sup1", "superscript one = superscript digit one, U+00B9 ISOnum" },
1409 { 186,  "ordm", "masculine ordinal indicator, U+00BA ISOnum" },
1410 { 187,  "raquo","right-pointing double angle quotation mark right pointing guillemet, U+00BB ISOnum" },
1411 { 188,  "frac14","vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum" },
1412 { 189,  "frac12","vulgar fraction one half = fraction one half, U+00BD ISOnum" },
1413 { 190,  "frac34","vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum" },
1414 { 191,  "iquest","inverted question mark = turned question mark, U+00BF ISOnum" },
1415 { 192,  "Agrave","latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1" },
1416 { 193,  "Aacute","latin capital letter A with acute, U+00C1 ISOlat1" },
1417 { 194,  "Acirc","latin capital letter A with circumflex, U+00C2 ISOlat1" },
1418 { 195,  "Atilde","latin capital letter A with tilde, U+00C3 ISOlat1" },
1419 { 196,  "Auml", "latin capital letter A with diaeresis, U+00C4 ISOlat1" },
1420 { 197,  "Aring","latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1" },
1421 { 198,  "AElig","latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1" },
1422 { 199,  "Ccedil","latin capital letter C with cedilla, U+00C7 ISOlat1" },
1423 { 200,  "Egrave","latin capital letter E with grave, U+00C8 ISOlat1" },
1424 { 201,  "Eacute","latin capital letter E with acute, U+00C9 ISOlat1" },
1425 { 202,  "Ecirc","latin capital letter E with circumflex, U+00CA ISOlat1" },
1426 { 203,  "Euml", "latin capital letter E with diaeresis, U+00CB ISOlat1" },
1427 { 204,  "Igrave","latin capital letter I with grave, U+00CC ISOlat1" },
1428 { 205,  "Iacute","latin capital letter I with acute, U+00CD ISOlat1" },
1429 { 206,  "Icirc","latin capital letter I with circumflex, U+00CE ISOlat1" },
1430 { 207,  "Iuml", "latin capital letter I with diaeresis, U+00CF ISOlat1" },
1431 { 208,  "ETH",  "latin capital letter ETH, U+00D0 ISOlat1" },
1432 { 209,  "Ntilde","latin capital letter N with tilde, U+00D1 ISOlat1" },
1433 { 210,  "Ograve","latin capital letter O with grave, U+00D2 ISOlat1" },
1434 { 211,  "Oacute","latin capital letter O with acute, U+00D3 ISOlat1" },
1435 { 212,  "Ocirc","latin capital letter O with circumflex, U+00D4 ISOlat1" },
1436 { 213,  "Otilde","latin capital letter O with tilde, U+00D5 ISOlat1" },
1437 { 214,  "Ouml", "latin capital letter O with diaeresis, U+00D6 ISOlat1" },
1438 { 215,  "times","multiplication sign, U+00D7 ISOnum" },
1439 { 216,  "Oslash","latin capital letter O with stroke latin capital letter O slash, U+00D8 ISOlat1" },
1440 { 217,  "Ugrave","latin capital letter U with grave, U+00D9 ISOlat1" },
1441 { 218,  "Uacute","latin capital letter U with acute, U+00DA ISOlat1" },
1442 { 219,  "Ucirc","latin capital letter U with circumflex, U+00DB ISOlat1" },
1443 { 220,  "Uuml", "latin capital letter U with diaeresis, U+00DC ISOlat1" },
1444 { 221,  "Yacute","latin capital letter Y with acute, U+00DD ISOlat1" },
1445 { 222,  "THORN","latin capital letter THORN, U+00DE ISOlat1" },
1446 { 223,  "szlig","latin small letter sharp s = ess-zed, U+00DF ISOlat1" },
1447 { 224,  "agrave","latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1" },
1448 { 225,  "aacute","latin small letter a with acute, U+00E1 ISOlat1" },
1449 { 226,  "acirc","latin small letter a with circumflex, U+00E2 ISOlat1" },
1450 { 227,  "atilde","latin small letter a with tilde, U+00E3 ISOlat1" },
1451 { 228,  "auml", "latin small letter a with diaeresis, U+00E4 ISOlat1" },
1452 { 229,  "aring","latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1" },
1453 { 230,  "aelig","latin small letter ae = latin small ligature ae, U+00E6 ISOlat1" },
1454 { 231,  "ccedil","latin small letter c with cedilla, U+00E7 ISOlat1" },
1455 { 232,  "egrave","latin small letter e with grave, U+00E8 ISOlat1" },
1456 { 233,  "eacute","latin small letter e with acute, U+00E9 ISOlat1" },
1457 { 234,  "ecirc","latin small letter e with circumflex, U+00EA ISOlat1" },
1458 { 235,  "euml", "latin small letter e with diaeresis, U+00EB ISOlat1" },
1459 { 236,  "igrave","latin small letter i with grave, U+00EC ISOlat1" },
1460 { 237,  "iacute","latin small letter i with acute, U+00ED ISOlat1" },
1461 { 238,  "icirc","latin small letter i with circumflex, U+00EE ISOlat1" },
1462 { 239,  "iuml", "latin small letter i with diaeresis, U+00EF ISOlat1" },
1463 { 240,  "eth",  "latin small letter eth, U+00F0 ISOlat1" },
1464 { 241,  "ntilde","latin small letter n with tilde, U+00F1 ISOlat1" },
1465 { 242,  "ograve","latin small letter o with grave, U+00F2 ISOlat1" },
1466 { 243,  "oacute","latin small letter o with acute, U+00F3 ISOlat1" },
1467 { 244,  "ocirc","latin small letter o with circumflex, U+00F4 ISOlat1" },
1468 { 245,  "otilde","latin small letter o with tilde, U+00F5 ISOlat1" },
1469 { 246,  "ouml", "latin small letter o with diaeresis, U+00F6 ISOlat1" },
1470 { 247,  "divide","division sign, U+00F7 ISOnum" },
1471 { 248,  "oslash","latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1" },
1472 { 249,  "ugrave","latin small letter u with grave, U+00F9 ISOlat1" },
1473 { 250,  "uacute","latin small letter u with acute, U+00FA ISOlat1" },
1474 { 251,  "ucirc","latin small letter u with circumflex, U+00FB ISOlat1" },
1475 { 252,  "uuml", "latin small letter u with diaeresis, U+00FC ISOlat1" },
1476 { 253,  "yacute","latin small letter y with acute, U+00FD ISOlat1" },
1477 { 254,  "thorn","latin small letter thorn with, U+00FE ISOlat1" },
1478 { 255,  "yuml", "latin small letter y with diaeresis, U+00FF ISOlat1" },
1479
1480 { 338,  "OElig","latin capital ligature OE, U+0152 ISOlat2" },
1481 { 339,  "oelig","latin small ligature oe, U+0153 ISOlat2" },
1482 { 352,  "Scaron","latin capital letter S with caron, U+0160 ISOlat2" },
1483 { 353,  "scaron","latin small letter s with caron, U+0161 ISOlat2" },
1484 { 376,  "Yuml", "latin capital letter Y with diaeresis, U+0178 ISOlat2" },
1485
1486 /*
1487  * Anything below should really be kept as entities references
1488  */
1489 { 402,  "fnof", "latin small f with hook = function = florin, U+0192 ISOtech" },
1490
1491 { 710,  "circ", "modifier letter circumflex accent, U+02C6 ISOpub" },
1492 { 732,  "tilde","small tilde, U+02DC ISOdia" },
1493
1494 { 913,  "Alpha","greek capital letter alpha, U+0391" },
1495 { 914,  "Beta", "greek capital letter beta, U+0392" },
1496 { 915,  "Gamma","greek capital letter gamma, U+0393 ISOgrk3" },
1497 { 916,  "Delta","greek capital letter delta, U+0394 ISOgrk3" },
1498 { 917,  "Epsilon","greek capital letter epsilon, U+0395" },
1499 { 918,  "Zeta", "greek capital letter zeta, U+0396" },
1500 { 919,  "Eta",  "greek capital letter eta, U+0397" },
1501 { 920,  "Theta","greek capital letter theta, U+0398 ISOgrk3" },
1502 { 921,  "Iota", "greek capital letter iota, U+0399" },
1503 { 922,  "Kappa","greek capital letter kappa, U+039A" },
1504 { 923,  "Lambda", "greek capital letter lambda, U+039B ISOgrk3" },
1505 { 924,  "Mu",   "greek capital letter mu, U+039C" },
1506 { 925,  "Nu",   "greek capital letter nu, U+039D" },
1507 { 926,  "Xi",   "greek capital letter xi, U+039E ISOgrk3" },
1508 { 927,  "Omicron","greek capital letter omicron, U+039F" },
1509 { 928,  "Pi",   "greek capital letter pi, U+03A0 ISOgrk3" },
1510 { 929,  "Rho",  "greek capital letter rho, U+03A1" },
1511 { 931,  "Sigma","greek capital letter sigma, U+03A3 ISOgrk3" },
1512 { 932,  "Tau",  "greek capital letter tau, U+03A4" },
1513 { 933,  "Upsilon","greek capital letter upsilon, U+03A5 ISOgrk3" },
1514 { 934,  "Phi",  "greek capital letter phi, U+03A6 ISOgrk3" },
1515 { 935,  "Chi",  "greek capital letter chi, U+03A7" },
1516 { 936,  "Psi",  "greek capital letter psi, U+03A8 ISOgrk3" },
1517 { 937,  "Omega","greek capital letter omega, U+03A9 ISOgrk3" },
1518
1519 { 945,  "alpha","greek small letter alpha, U+03B1 ISOgrk3" },
1520 { 946,  "beta", "greek small letter beta, U+03B2 ISOgrk3" },
1521 { 947,  "gamma","greek small letter gamma, U+03B3 ISOgrk3" },
1522 { 948,  "delta","greek small letter delta, U+03B4 ISOgrk3" },
1523 { 949,  "epsilon","greek small letter epsilon, U+03B5 ISOgrk3" },
1524 { 950,  "zeta", "greek small letter zeta, U+03B6 ISOgrk3" },
1525 { 951,  "eta",  "greek small letter eta, U+03B7 ISOgrk3" },
1526 { 952,  "theta","greek small letter theta, U+03B8 ISOgrk3" },
1527 { 953,  "iota", "greek small letter iota, U+03B9 ISOgrk3" },
1528 { 954,  "kappa","greek small letter kappa, U+03BA ISOgrk3" },
1529 { 955,  "lambda","greek small letter lambda, U+03BB ISOgrk3" },
1530 { 956,  "mu",   "greek small letter mu, U+03BC ISOgrk3" },
1531 { 957,  "nu",   "greek small letter nu, U+03BD ISOgrk3" },
1532 { 958,  "xi",   "greek small letter xi, U+03BE ISOgrk3" },
1533 { 959,  "omicron","greek small letter omicron, U+03BF NEW" },
1534 { 960,  "pi",   "greek small letter pi, U+03C0 ISOgrk3" },
1535 { 961,  "rho",  "greek small letter rho, U+03C1 ISOgrk3" },
1536 { 962,  "sigmaf","greek small letter final sigma, U+03C2 ISOgrk3" },
1537 { 963,  "sigma","greek small letter sigma, U+03C3 ISOgrk3" },
1538 { 964,  "tau",  "greek small letter tau, U+03C4 ISOgrk3" },
1539 { 965,  "upsilon","greek small letter upsilon, U+03C5 ISOgrk3" },
1540 { 966,  "phi",  "greek small letter phi, U+03C6 ISOgrk3" },
1541 { 967,  "chi",  "greek small letter chi, U+03C7 ISOgrk3" },
1542 { 968,  "psi",  "greek small letter psi, U+03C8 ISOgrk3" },
1543 { 969,  "omega","greek small letter omega, U+03C9 ISOgrk3" },
1544 { 977,  "thetasym","greek small letter theta symbol, U+03D1 NEW" },
1545 { 978,  "upsih","greek upsilon with hook symbol, U+03D2 NEW" },
1546 { 982,  "piv",  "greek pi symbol, U+03D6 ISOgrk3" },
1547
1548 { 8194, "ensp", "en space, U+2002 ISOpub" },
1549 { 8195, "emsp", "em space, U+2003 ISOpub" },
1550 { 8201, "thinsp","thin space, U+2009 ISOpub" },
1551 { 8204, "zwnj", "zero width non-joiner, U+200C NEW RFC 2070" },
1552 { 8205, "zwj",  "zero width joiner, U+200D NEW RFC 2070" },
1553 { 8206, "lrm",  "left-to-right mark, U+200E NEW RFC 2070" },
1554 { 8207, "rlm",  "right-to-left mark, U+200F NEW RFC 2070" },
1555 { 8211, "ndash","en dash, U+2013 ISOpub" },
1556 { 8212, "mdash","em dash, U+2014 ISOpub" },
1557 { 8216, "lsquo","left single quotation mark, U+2018 ISOnum" },
1558 { 8217, "rsquo","right single quotation mark, U+2019 ISOnum" },
1559 { 8218, "sbquo","single low-9 quotation mark, U+201A NEW" },
1560 { 8220, "ldquo","left double quotation mark, U+201C ISOnum" },
1561 { 8221, "rdquo","right double quotation mark, U+201D ISOnum" },
1562 { 8222, "bdquo","double low-9 quotation mark, U+201E NEW" },
1563 { 8224, "dagger","dagger, U+2020 ISOpub" },
1564 { 8225, "Dagger","double dagger, U+2021 ISOpub" },
1565
1566 { 8226, "bull", "bullet = black small circle, U+2022 ISOpub" },
1567 { 8230, "hellip","horizontal ellipsis = three dot leader, U+2026 ISOpub" },
1568
1569 { 8240, "permil","per mille sign, U+2030 ISOtech" },
1570
1571 { 8242, "prime","prime = minutes = feet, U+2032 ISOtech" },
1572 { 8243, "Prime","double prime = seconds = inches, U+2033 ISOtech" },
1573
1574 { 8249, "lsaquo","single left-pointing angle quotation mark, U+2039 ISO proposed" },
1575 { 8250, "rsaquo","single right-pointing angle quotation mark, U+203A ISO proposed" },
1576
1577 { 8254, "oline","overline = spacing overscore, U+203E NEW" },
1578 { 8260, "frasl","fraction slash, U+2044 NEW" },
1579
1580 { 8364, "euro", "euro sign, U+20AC NEW" },
1581
1582 { 8465, "image","blackletter capital I = imaginary part, U+2111 ISOamso" },
1583 { 8472, "weierp","script capital P = power set = Weierstrass p, U+2118 ISOamso" },
1584 { 8476, "real", "blackletter capital R = real part symbol, U+211C ISOamso" },
1585 { 8482, "trade","trade mark sign, U+2122 ISOnum" },
1586 { 8501, "alefsym","alef symbol = first transfinite cardinal, U+2135 NEW" },
1587 { 8592, "larr", "leftwards arrow, U+2190 ISOnum" },
1588 { 8593, "uarr", "upwards arrow, U+2191 ISOnum" },
1589 { 8594, "rarr", "rightwards arrow, U+2192 ISOnum" },
1590 { 8595, "darr", "downwards arrow, U+2193 ISOnum" },
1591 { 8596, "harr", "left right arrow, U+2194 ISOamsa" },
1592 { 8629, "crarr","downwards arrow with corner leftwards = carriage return, U+21B5 NEW" },
1593 { 8656, "lArr", "leftwards double arrow, U+21D0 ISOtech" },
1594 { 8657, "uArr", "upwards double arrow, U+21D1 ISOamsa" },
1595 { 8658, "rArr", "rightwards double arrow, U+21D2 ISOtech" },
1596 { 8659, "dArr", "downwards double arrow, U+21D3 ISOamsa" },
1597 { 8660, "hArr", "left right double arrow, U+21D4 ISOamsa" },
1598
1599 { 8704, "forall","for all, U+2200 ISOtech" },
1600 { 8706, "part", "partial differential, U+2202 ISOtech" },
1601 { 8707, "exist","there exists, U+2203 ISOtech" },
1602 { 8709, "empty","empty set = null set = diameter, U+2205 ISOamso" },
1603 { 8711, "nabla","nabla = backward difference, U+2207 ISOtech" },
1604 { 8712, "isin", "element of, U+2208 ISOtech" },
1605 { 8713, "notin","not an element of, U+2209 ISOtech" },
1606 { 8715, "ni",   "contains as member, U+220B ISOtech" },
1607 { 8719, "prod", "n-ary product = product sign, U+220F ISOamsb" },
1608 { 8721, "sum",  "n-ary summation, U+2211 ISOamsb" },
1609 { 8722, "minus","minus sign, U+2212 ISOtech" },
1610 { 8727, "lowast","asterisk operator, U+2217 ISOtech" },
1611 { 8730, "radic","square root = radical sign, U+221A ISOtech" },
1612 { 8733, "prop", "proportional to, U+221D ISOtech" },
1613 { 8734, "infin","infinity, U+221E ISOtech" },
1614 { 8736, "ang",  "angle, U+2220 ISOamso" },
1615 { 8743, "and",  "logical and = wedge, U+2227 ISOtech" },
1616 { 8744, "or",   "logical or = vee, U+2228 ISOtech" },
1617 { 8745, "cap",  "intersection = cap, U+2229 ISOtech" },
1618 { 8746, "cup",  "union = cup, U+222A ISOtech" },
1619 { 8747, "int",  "integral, U+222B ISOtech" },
1620 { 8756, "there4","therefore, U+2234 ISOtech" },
1621 { 8764, "sim",  "tilde operator = varies with = similar to, U+223C ISOtech" },
1622 { 8773, "cong", "approximately equal to, U+2245 ISOtech" },
1623 { 8776, "asymp","almost equal to = asymptotic to, U+2248 ISOamsr" },
1624 { 8800, "ne",   "not equal to, U+2260 ISOtech" },
1625 { 8801, "equiv","identical to, U+2261 ISOtech" },
1626 { 8804, "le",   "less-than or equal to, U+2264 ISOtech" },
1627 { 8805, "ge",   "greater-than or equal to, U+2265 ISOtech" },
1628 { 8834, "sub",  "subset of, U+2282 ISOtech" },
1629 { 8835, "sup",  "superset of, U+2283 ISOtech" },
1630 { 8836, "nsub", "not a subset of, U+2284 ISOamsn" },
1631 { 8838, "sube", "subset of or equal to, U+2286 ISOtech" },
1632 { 8839, "supe", "superset of or equal to, U+2287 ISOtech" },
1633 { 8853, "oplus","circled plus = direct sum, U+2295 ISOamsb" },
1634 { 8855, "otimes","circled times = vector product, U+2297 ISOamsb" },
1635 { 8869, "perp", "up tack = orthogonal to = perpendicular, U+22A5 ISOtech" },
1636 { 8901, "sdot", "dot operator, U+22C5 ISOamsb" },
1637 { 8968, "lceil","left ceiling = apl upstile, U+2308 ISOamsc" },
1638 { 8969, "rceil","right ceiling, U+2309 ISOamsc" },
1639 { 8970, "lfloor","left floor = apl downstile, U+230A ISOamsc" },
1640 { 8971, "rfloor","right floor, U+230B ISOamsc" },
1641 { 9001, "lang", "left-pointing angle bracket = bra, U+2329 ISOtech" },
1642 { 9002, "rang", "right-pointing angle bracket = ket, U+232A ISOtech" },
1643 { 9674, "loz",  "lozenge, U+25CA ISOpub" },
1644
1645 { 9824, "spades","black spade suit, U+2660 ISOpub" },
1646 { 9827, "clubs","black club suit = shamrock, U+2663 ISOpub" },
1647 { 9829, "hearts","black heart suit = valentine, U+2665 ISOpub" },
1648 { 9830, "diams","black diamond suit, U+2666 ISOpub" },
1649
1650 };
1651
1652 /************************************************************************
1653  *                                                                      *
1654  *              Commodity functions to handle entities                  *
1655  *                                                                      *
1656  ************************************************************************/
1657
1658 /*
1659  * Macro used to grow the current buffer.
1660  */
1661 #define growBuffer(buffer) {                                            \
1662     buffer##_size *= 2;                                                 \
1663     buffer = (xmlChar *) xmlRealloc(buffer, buffer##_size * sizeof(xmlChar)); \
1664     if (buffer == NULL) {                                               \
1665         xmlGenericError(xmlGenericErrorContext, "realloc failed\n");    \
1666         return(NULL);                                                   \
1667     }                                                                   \
1668 }
1669
1670 /**
1671  * htmlEntityLookup:
1672  * @name: the entity name
1673  *
1674  * Lookup the given entity in EntitiesTable
1675  *
1676  * TODO: the linear scan is really ugly, an hash table is really needed.
1677  *
1678  * Returns the associated htmlEntityDescPtr if found, NULL otherwise.
1679  */
1680 const htmlEntityDesc *
1681 htmlEntityLookup(const xmlChar *name) {
1682     unsigned int i;
1683
1684     for (i = 0;i < (sizeof(html40EntitiesTable)/
1685                     sizeof(html40EntitiesTable[0]));i++) {
1686         if (xmlStrEqual(name, BAD_CAST html40EntitiesTable[i].name)) {
1687 #ifdef DEBUG
1688             xmlGenericError(xmlGenericErrorContext,"Found entity %s\n", name);
1689 #endif
1690             return((const htmlEntityDescPtr) &html40EntitiesTable[i]);
1691         }
1692     }
1693     return(NULL);
1694 }
1695
1696 /**
1697  * htmlEntityValueLookup:
1698  * @value: the entity's unicode value
1699  *
1700  * Lookup the given entity in EntitiesTable
1701  *
1702  * TODO: the linear scan is really ugly, an hash table is really needed.
1703  *
1704  * Returns the associated htmlEntityDescPtr if found, NULL otherwise.
1705  */
1706 const htmlEntityDesc *
1707 htmlEntityValueLookup(unsigned int value) {
1708     unsigned int i;
1709 #ifdef DEBUG
1710     unsigned int lv = 0;
1711 #endif
1712
1713     for (i = 0;i < (sizeof(html40EntitiesTable)/
1714                     sizeof(html40EntitiesTable[0]));i++) {
1715         if (html40EntitiesTable[i].value >= value) {
1716             if (html40EntitiesTable[i].value > value)
1717                 break;
1718 #ifdef DEBUG
1719             xmlGenericError(xmlGenericErrorContext,"Found entity %s\n", html40EntitiesTable[i].name);
1720 #endif
1721             return((const htmlEntityDescPtr) &html40EntitiesTable[i]);
1722         }
1723 #ifdef DEBUG
1724         if (lv > html40EntitiesTable[i].value) {
1725             xmlGenericError(xmlGenericErrorContext,
1726                     "html40EntitiesTable[] is not sorted (%d > %d)!\n",
1727                     lv, html40EntitiesTable[i].value);
1728         }
1729         lv = html40EntitiesTable[i].value;
1730 #endif
1731     }
1732     return(NULL);
1733 }
1734
1735 /**
1736  * UTF8ToHtml:
1737  * @out:  a pointer to an array of bytes to store the result
1738  * @outlen:  the length of @out
1739  * @in:  a pointer to an array of UTF-8 chars
1740  * @inlen:  the length of @in
1741  *
1742  * Take a block of UTF-8 chars in and try to convert it to an ASCII
1743  * plus HTML entities block of chars out.
1744  *
1745  * Returns 0 if success, -2 if the transcoding fails, or -1 otherwise
1746  * The value of @inlen after return is the number of octets consumed
1747  *     as the return value is positive, else unpredictable.
1748  * The value of @outlen after return is the number of octets consumed.
1749  */
1750 int
1751 UTF8ToHtml(unsigned char* out, int *outlen,
1752               const unsigned char* in, int *inlen) {
1753     const unsigned char* processed = in;
1754     const unsigned char* outend;
1755     const unsigned char* outstart = out;
1756     const unsigned char* instart = in;
1757     const unsigned char* inend;
1758     unsigned int c, d;
1759     int trailing;
1760
1761     if (in == NULL) {
1762         /*
1763          * initialization nothing to do
1764          */
1765         *outlen = 0;
1766         *inlen = 0;
1767         return(0);
1768     }
1769     inend = in + (*inlen);
1770     outend = out + (*outlen);
1771     while (in < inend) {
1772         d = *in++;
1773         if      (d < 0x80)  { c= d; trailing= 0; }
1774         else if (d < 0xC0) {
1775             /* trailing byte in leading position */
1776             *outlen = out - outstart;
1777             *inlen = processed - instart;
1778             return(-2);
1779         } else if (d < 0xE0)  { c= d & 0x1F; trailing= 1; }
1780         else if (d < 0xF0)  { c= d & 0x0F; trailing= 2; }
1781         else if (d < 0xF8)  { c= d & 0x07; trailing= 3; }
1782         else {
1783             /* no chance for this in Ascii */
1784             *outlen = out - outstart;
1785             *inlen = processed - instart;
1786             return(-2);
1787         }
1788
1789         if (inend - in < trailing) {
1790             break;
1791         } 
1792
1793         for ( ; trailing; trailing--) {
1794             if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80))
1795                 break;
1796             c <<= 6;
1797             c |= d & 0x3F;
1798         }
1799
1800         /* assertion: c is a single UTF-4 value */
1801         if (c < 0x80) {
1802             if (out + 1 >= outend)
1803                 break;
1804             *out++ = c;
1805         } else {
1806             int len;
1807             const htmlEntityDesc * ent;
1808
1809             /*
1810              * Try to lookup a predefined HTML entity for it
1811              */
1812
1813             ent = htmlEntityValueLookup(c);
1814             if (ent == NULL) {
1815                 /* no chance for this in Ascii */
1816                 *outlen = out - outstart;
1817                 *inlen = processed - instart;
1818                 return(-2);
1819             }
1820             len = strlen(ent->name);
1821             if (out + 2 + len >= outend)
1822                 break;
1823             *out++ = '&';
1824             memcpy(out, ent->name, len);
1825             out += len;
1826             *out++ = ';';
1827         }
1828         processed = in;
1829     }
1830     *outlen = out - outstart;
1831     *inlen = processed - instart;
1832     return(0);
1833 }
1834
1835 /**
1836  * htmlEncodeEntities:
1837  * @out:  a pointer to an array of bytes to store the result
1838  * @outlen:  the length of @out
1839  * @in:  a pointer to an array of UTF-8 chars
1840  * @inlen:  the length of @in
1841  * @quoteChar: the quote character to escape (' or ") or zero.
1842  *
1843  * Take a block of UTF-8 chars in and try to convert it to an ASCII
1844  * plus HTML entities block of chars out.
1845  *
1846  * Returns 0 if success, -2 if the transcoding fails, or -1 otherwise
1847  * The value of @inlen after return is the number of octets consumed
1848  *     as the return value is positive, else unpredictable.
1849  * The value of @outlen after return is the number of octets consumed.
1850  */
1851 int
1852 htmlEncodeEntities(unsigned char* out, int *outlen,
1853                    const unsigned char* in, int *inlen, int quoteChar) {
1854     const unsigned char* processed = in;
1855     const unsigned char* outend = out + (*outlen);
1856     const unsigned char* outstart = out;
1857     const unsigned char* instart = in;
1858     const unsigned char* inend = in + (*inlen);
1859     unsigned int c, d;
1860     int trailing;
1861
1862     while (in < inend) {
1863         d = *in++;
1864         if      (d < 0x80)  { c= d; trailing= 0; }
1865         else if (d < 0xC0) {
1866             /* trailing byte in leading position */
1867             *outlen = out - outstart;
1868             *inlen = processed - instart;
1869             return(-2);
1870         } else if (d < 0xE0)  { c= d & 0x1F; trailing= 1; }
1871         else if (d < 0xF0)  { c= d & 0x0F; trailing= 2; }
1872         else if (d < 0xF8)  { c= d & 0x07; trailing= 3; }
1873         else {
1874             /* no chance for this in Ascii */
1875             *outlen = out - outstart;
1876             *inlen = processed - instart;
1877             return(-2);
1878         }
1879
1880         if (inend - in < trailing)
1881             break;
1882
1883         while (trailing--) {
1884             if (((d= *in++) & 0xC0) != 0x80) {
1885                 *outlen = out - outstart;
1886                 *inlen = processed - instart;
1887                 return(-2);
1888             }
1889             c <<= 6;
1890             c |= d & 0x3F;
1891         }
1892
1893         /* assertion: c is a single UTF-4 value */
1894         if ((c < 0x80) && (c != (unsigned int) quoteChar) &&
1895             (c != '&') && (c != '<') && (c != '>')) {
1896             if (out >= outend)
1897                 break;
1898             *out++ = c;
1899         } else {
1900             const htmlEntityDesc * ent;
1901             const char *cp;
1902             char nbuf[16];
1903             int len;
1904
1905             /*
1906              * Try to lookup a predefined HTML entity for it
1907              */
1908             ent = htmlEntityValueLookup(c);
1909             if (ent == NULL) {
1910                 snprintf(nbuf, sizeof(nbuf), "#%u", c);
1911                 cp = nbuf;
1912             }
1913             else
1914                 cp = ent->name;
1915             len = strlen(cp);
1916             if (out + 2 + len > outend)
1917                 break;
1918             *out++ = '&';
1919             memcpy(out, cp, len);
1920             out += len;
1921             *out++ = ';';
1922         }
1923         processed = in;
1924     }
1925     *outlen = out - outstart;
1926     *inlen = processed - instart;
1927     return(0);
1928 }
1929
1930 /**
1931  * htmlDecodeEntities:
1932  * @ctxt:  the parser context
1933  * @len:  the len to decode (in bytes !), -1 for no size limit
1934  * @end:  an end marker xmlChar, 0 if none
1935  * @end2:  an end marker xmlChar, 0 if none
1936  * @end3:  an end marker xmlChar, 0 if none
1937  *
1938  * Substitute the HTML entities by their value
1939  *
1940  * DEPRECATED !!!!
1941  *
1942  * Returns A newly allocated string with the substitution done. The caller
1943  *      must deallocate it !
1944  */
1945 xmlChar *
1946 htmlDecodeEntities(htmlParserCtxtPtr ctxt ATTRIBUTE_UNUSED, int len ATTRIBUTE_UNUSED,
1947           xmlChar end ATTRIBUTE_UNUSED, xmlChar  end2 ATTRIBUTE_UNUSED, xmlChar end3 ATTRIBUTE_UNUSED) {
1948     static int deprecated = 0;
1949     if (!deprecated) {
1950         xmlGenericError(xmlGenericErrorContext,
1951                 "htmlDecodeEntities() deprecated function reached\n");
1952         deprecated = 1;
1953     }
1954     return(NULL);
1955 }
1956
1957 /************************************************************************
1958  *                                                                      *
1959  *              Commodity functions to handle streams                   *
1960  *                                                                      *
1961  ************************************************************************/
1962
1963 /**
1964  * htmlNewInputStream:
1965  * @ctxt:  an HTML parser context
1966  *
1967  * Create a new input stream structure
1968  * Returns the new input stream or NULL
1969  */
1970 static htmlParserInputPtr
1971 htmlNewInputStream(htmlParserCtxtPtr ctxt) {
1972     htmlParserInputPtr input;
1973
1974     input = (xmlParserInputPtr) xmlMalloc(sizeof(htmlParserInput));
1975     if (input == NULL) {
1976         ctxt->errNo = XML_ERR_NO_MEMORY;
1977         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1978             ctxt->sax->error(ctxt->userData, 
1979                              "malloc: couldn't allocate a new input stream\n");
1980         return(NULL);
1981     }
1982     memset(input, 0, sizeof(htmlParserInput));
1983     input->filename = NULL;
1984     input->directory = NULL;
1985     input->base = NULL;
1986     input->cur = NULL;
1987     input->buf = NULL;
1988     input->line = 1;
1989     input->col = 1;
1990     input->buf = NULL;
1991     input->free = NULL;
1992     input->version = NULL;
1993     input->consumed = 0;
1994     input->length = 0;
1995     return(input);
1996 }
1997
1998
1999 /************************************************************************
2000  *                                                                      *
2001  *              Commodity functions, cleanup needed ?                   *
2002  *                                                                      *
2003  ************************************************************************/
2004 /*
2005  * all tags allowing pc data from the html 4.01 loose dtd 
2006  * NOTE: it might be more apropriate to integrate this information
2007  * into the html40ElementTable array but I don't want to risk any
2008  * binary incomptibility
2009  */
2010 static const char *allowPCData[] = {
2011     "a", "abbr", "acronym", "address", "applet", "b", "bdo", "big",
2012     "blockquote", "body", "button", "caption", "center", "cite", "code",
2013     "dd", "del", "dfn", "div", "dt", "em", "font", "form", "h1", "h2",
2014     "h3", "h4", "h5", "h6", "i", "iframe", "ins", "kbd", "label", "legend",
2015     "li", "noframes", "noscript", "object", "p", "pre", "q", "s", "samp",
2016     "small", "span", "strike", "strong", "td", "th", "tt", "u", "var"
2017 };
2018
2019 /**
2020  * areBlanks:
2021  * @ctxt:  an HTML parser context
2022  * @str:  a xmlChar *
2023  * @len:  the size of @str
2024  *
2025  * Is this a sequence of blank chars that one can ignore ?
2026  *
2027  * Returns 1 if ignorable 0 otherwise.
2028  */
2029
2030 static int areBlanks(htmlParserCtxtPtr ctxt, const xmlChar *str, int len) {
2031     unsigned int i;
2032     int j;
2033     xmlNodePtr lastChild;
2034
2035     for (j = 0;j < len;j++)
2036         if (!(IS_BLANK(str[j]))) return(0);
2037
2038     if (CUR == 0) return(1);
2039     if (CUR != '<') return(0);
2040     if (ctxt->name == NULL)
2041         return(1);
2042     if (xmlStrEqual(ctxt->name, BAD_CAST"html"))
2043         return(1);
2044     if (xmlStrEqual(ctxt->name, BAD_CAST"head"))
2045         return(1);
2046     if (xmlStrEqual(ctxt->name, BAD_CAST"body"))
2047         return(1);
2048     if (ctxt->node == NULL) return(0);
2049     lastChild = xmlGetLastChild(ctxt->node);
2050     if (lastChild == NULL) {
2051         if ((ctxt->node->type != XML_ELEMENT_NODE) &&
2052             (ctxt->node->content != NULL)) return(0);
2053         /* keep ws in constructs like ...<b> </b>... 
2054            for all tags "b" allowing PCDATA */
2055         for ( i = 0; i < sizeof(allowPCData)/sizeof(allowPCData[0]); i++ ) {
2056             if ( xmlStrEqual(ctxt->name, BAD_CAST allowPCData[i]) ) {
2057                 return(0);
2058             }
2059         }
2060     } else if (xmlNodeIsText(lastChild)) {
2061         return(0);
2062     } else {
2063         /* keep ws in constructs like <p><b>xy</b> <i>z</i><p> 
2064            for all tags "p" allowing PCDATA */
2065         for ( i = 0; i < sizeof(allowPCData)/sizeof(allowPCData[0]); i++ ) {
2066             if ( xmlStrEqual(lastChild->name, BAD_CAST allowPCData[i]) ) {
2067                 return(0);
2068             }
2069         }
2070     }
2071     return(1);
2072 }
2073
2074 /**
2075  * htmlNewDocNoDtD:
2076  * @URI:  URI for the dtd, or NULL
2077  * @ExternalID:  the external ID of the DTD, or NULL
2078  *
2079  * Creates a new HTML document without a DTD node if @URI and @ExternalID
2080  * are NULL
2081  *
2082  * Returns a new document, do not initialize the DTD if not provided
2083  */
2084 htmlDocPtr
2085 htmlNewDocNoDtD(const xmlChar *URI, const xmlChar *ExternalID) {
2086     xmlDocPtr cur;
2087
2088     /*
2089      * Allocate a new document and fill the fields.
2090      */
2091     cur = (xmlDocPtr) xmlMalloc(sizeof(xmlDoc));
2092     if (cur == NULL) {
2093         xmlGenericError(xmlGenericErrorContext,
2094                 "htmlNewDocNoDtD : malloc failed\n");
2095         return(NULL);
2096     }
2097     memset(cur, 0, sizeof(xmlDoc));
2098
2099     cur->type = XML_HTML_DOCUMENT_NODE;
2100     cur->version = NULL;
2101     cur->intSubset = NULL;
2102     cur->doc = cur;
2103     cur->name = NULL;
2104     cur->children = NULL; 
2105     cur->extSubset = NULL;
2106     cur->oldNs = NULL;
2107     cur->encoding = NULL;
2108     cur->standalone = 1;
2109     cur->compression = 0;
2110     cur->ids = NULL;
2111     cur->refs = NULL;
2112     cur->_private = NULL;
2113     if ((ExternalID != NULL) ||
2114         (URI != NULL))
2115         xmlCreateIntSubset(cur, BAD_CAST "HTML", ExternalID, URI);
2116     return(cur);
2117 }
2118
2119 /**
2120  * htmlNewDoc:
2121  * @URI:  URI for the dtd, or NULL
2122  * @ExternalID:  the external ID of the DTD, or NULL
2123  *
2124  * Creates a new HTML document
2125  *
2126  * Returns a new document
2127  */
2128 htmlDocPtr
2129 htmlNewDoc(const xmlChar *URI, const xmlChar *ExternalID) {
2130     if ((URI == NULL) && (ExternalID == NULL))
2131         return(htmlNewDocNoDtD(
2132                     BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd",
2133                     BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN"));
2134
2135     return(htmlNewDocNoDtD(URI, ExternalID));
2136 }
2137
2138
2139 /************************************************************************
2140  *                                                                      *
2141  *                      The parser itself                               *
2142  *      Relates to http://www.w3.org/TR/html40                          *
2143  *                                                                      *
2144  ************************************************************************/
2145
2146 /************************************************************************
2147  *                                                                      *
2148  *                      The parser itself                               *
2149  *                                                                      *
2150  ************************************************************************/
2151
2152 static xmlChar * htmlParseNameComplex(xmlParserCtxtPtr ctxt);
2153
2154 /**
2155  * htmlParseHTMLName:
2156  * @ctxt:  an HTML parser context
2157  *
2158  * parse an HTML tag or attribute name, note that we convert it to lowercase
2159  * since HTML names are not case-sensitive.
2160  *
2161  * Returns the Tag Name parsed or NULL
2162  */
2163
2164 static xmlChar *
2165 htmlParseHTMLName(htmlParserCtxtPtr ctxt) {
2166     xmlChar *ret = NULL;
2167     int i = 0;
2168     xmlChar loc[HTML_PARSER_BUFFER_SIZE];
2169
2170     if (!IS_LETTER(CUR) && (CUR != '_') &&
2171         (CUR != ':')) return(NULL);
2172
2173     while ((i < HTML_PARSER_BUFFER_SIZE) &&
2174            ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
2175            (CUR == ':') || (CUR == '-') || (CUR == '_'))) {
2176         if ((CUR >= 'A') && (CUR <= 'Z')) loc[i] = CUR + 0x20;
2177         else loc[i] = CUR;
2178         i++;
2179         
2180         NEXT;
2181     }
2182     
2183     ret = xmlStrndup(loc, i);
2184
2185     return(ret);
2186 }
2187
2188 /**
2189  * htmlParseName:
2190  * @ctxt:  an HTML parser context
2191  *
2192  * parse an HTML name, this routine is case sensitive.
2193  *
2194  * Returns the Name parsed or NULL
2195  */
2196
2197 static xmlChar *
2198 htmlParseName(htmlParserCtxtPtr ctxt) {
2199     const xmlChar *in;
2200     xmlChar *ret;
2201     int count = 0;
2202
2203     GROW;
2204
2205     /*
2206      * Accelerator for simple ASCII names
2207      */
2208     in = ctxt->input->cur;
2209     if (((*in >= 0x61) && (*in <= 0x7A)) ||
2210         ((*in >= 0x41) && (*in <= 0x5A)) ||
2211         (*in == '_') || (*in == ':')) {
2212         in++;
2213         while (((*in >= 0x61) && (*in <= 0x7A)) ||
2214                ((*in >= 0x41) && (*in <= 0x5A)) ||
2215                ((*in >= 0x30) && (*in <= 0x39)) ||
2216                (*in == '_') || (*in == '-') ||
2217                (*in == ':') || (*in == '.'))
2218             in++;
2219         if ((*in > 0) && (*in < 0x80)) {
2220             count = in - ctxt->input->cur;
2221             ret = xmlStrndup(ctxt->input->cur, count);
2222             ctxt->input->cur = in;
2223             return(ret);
2224         }
2225     }
2226     return(htmlParseNameComplex(ctxt));
2227 }
2228
2229 static xmlChar *
2230 htmlParseNameComplex(xmlParserCtxtPtr ctxt) {
2231     xmlChar buf[XML_MAX_NAMELEN + 5];
2232     int len = 0, l;
2233     int c;
2234     int count = 0;
2235
2236     /*
2237      * Handler for more complex cases
2238      */
2239     GROW;
2240     c = CUR_CHAR(l);
2241     if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
2242         (!IS_LETTER(c) && (c != '_') &&
2243          (c != ':'))) {
2244         return(NULL);
2245     }
2246
2247     while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
2248            ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2249             (c == '.') || (c == '-') ||
2250             (c == '_') || (c == ':') || 
2251             (IS_COMBINING(c)) ||
2252             (IS_EXTENDER(c)))) {
2253         if (count++ > 100) {
2254             count = 0;
2255             GROW;
2256         }
2257         COPY_BUF(l,buf,len,c);
2258         NEXTL(l);
2259         c = CUR_CHAR(l);
2260         if (len >= XML_MAX_NAMELEN) {
2261             /*
2262              * Okay someone managed to make a huge name, so he's ready to pay
2263              * for the processing speed.
2264              */
2265             xmlChar *buffer;
2266             int max = len * 2;
2267             
2268             buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
2269             if (buffer == NULL) {
2270                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2271                     ctxt->sax->error(ctxt->userData,
2272                                      "htmlParseNameComplex: out of memory\n");
2273                 return(NULL);
2274             }
2275             memcpy(buffer, buf, len);
2276             while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
2277                    (c == '.') || (c == '-') ||
2278                    (c == '_') || (c == ':') || 
2279                    (IS_COMBINING(c)) ||
2280                    (IS_EXTENDER(c))) {
2281                 if (count++ > 100) {
2282                     count = 0;
2283                     GROW;
2284                 }
2285                 if (len + 10 > max) {
2286                     max *= 2;
2287                     buffer = (xmlChar *) xmlRealloc(buffer,
2288                                                     max * sizeof(xmlChar));
2289                     if (buffer == NULL) {
2290                         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2291                             ctxt->sax->error(ctxt->userData,
2292                                      "htmlParseNameComplex: out of memory\n");
2293                         return(NULL);
2294                     }
2295                 }
2296                 COPY_BUF(l,buffer,len,c);
2297                 NEXTL(l);
2298                 c = CUR_CHAR(l);
2299             }
2300             buffer[len] = 0;
2301             return(buffer);
2302         }
2303     }
2304     return(xmlStrndup(buf, len));
2305 }
2306
2307
2308 /**
2309  * htmlParseHTMLAttribute:
2310  * @ctxt:  an HTML parser context
2311  * @stop:  a char stop value
2312  * 
2313  * parse an HTML attribute value till the stop (quote), if
2314  * stop is 0 then it stops at the first space
2315  *
2316  * Returns the attribute parsed or NULL
2317  */
2318
2319 static xmlChar *
2320 htmlParseHTMLAttribute(htmlParserCtxtPtr ctxt, const xmlChar stop) {
2321     xmlChar *buffer = NULL;
2322     int buffer_size = 0;
2323     xmlChar *out = NULL;
2324     xmlChar *name = NULL;
2325
2326     xmlChar *cur = NULL;
2327     const htmlEntityDesc * ent;
2328
2329     /*
2330      * allocate a translation buffer.
2331      */
2332     buffer_size = HTML_PARSER_BUFFER_SIZE;
2333     buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
2334     if (buffer == NULL) {
2335         xmlGenericError(xmlGenericErrorContext,
2336                         "htmlParseHTMLAttribute: malloc failed\n");
2337         return(NULL);
2338     }
2339     out = buffer;
2340
2341     /*
2342      * Ok loop until we reach one of the ending chars
2343      */
2344     while ((CUR != 0) && (CUR != stop)) {
2345         if ((stop == 0) && (CUR == '>')) break;
2346         if ((stop == 0) && (IS_BLANK(CUR))) break;
2347         if (CUR == '&') {
2348             if (NXT(1) == '#') {
2349                 unsigned int c;
2350                 int bits;
2351
2352                 c = htmlParseCharRef(ctxt);
2353                 if      (c <    0x80)
2354                         { *out++  = c;                bits= -6; }
2355                 else if (c <   0x800)
2356                         { *out++  =((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
2357                 else if (c < 0x10000)
2358                         { *out++  =((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
2359                 else                 
2360                         { *out++  =((c >> 18) & 0x07) | 0xF0;  bits= 12; }
2361          
2362                 for ( ; bits >= 0; bits-= 6) {
2363                     *out++  = ((c >> bits) & 0x3F) | 0x80;
2364                 }
2365                 
2366                 if (out - buffer > buffer_size - 100) {
2367                         int indx = out - buffer;
2368
2369                         growBuffer(buffer);
2370                         out = &buffer[indx];
2371                 }
2372             } else {
2373                 ent = htmlParseEntityRef(ctxt, &name);
2374                 if (name == NULL) {
2375                     *out++ = '&';
2376                     if (out - buffer > buffer_size - 100) {
2377                         int indx = out - buffer;
2378
2379                         growBuffer(buffer);
2380                         out = &buffer[indx];
2381                     }
2382                 } else if (ent == NULL) {
2383                     *out++ = '&';
2384                     cur = name;
2385                     while (*cur != 0) {
2386                         if (out - buffer > buffer_size - 100) {
2387                             int indx = out - buffer;
2388
2389                             growBuffer(buffer);
2390                             out = &buffer[indx];
2391                         }
2392                         *out++ = *cur++;
2393                     }
2394                     xmlFree(name);
2395                 } else {
2396                     unsigned int c;
2397                     int bits;
2398
2399                     if (out - buffer > buffer_size - 100) {
2400                         int indx = out - buffer;
2401
2402                         growBuffer(buffer);
2403                         out = &buffer[indx];
2404                     }
2405                     c = (xmlChar)ent->value;
2406                     if      (c <    0x80)
2407                         { *out++  = c;                bits= -6; }
2408                     else if (c <   0x800)
2409                         { *out++  =((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
2410                     else if (c < 0x10000)
2411                         { *out++  =((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
2412                     else                 
2413                         { *out++  =((c >> 18) & 0x07) | 0xF0;  bits= 12; }
2414              
2415                     for ( ; bits >= 0; bits-= 6) {
2416                         *out++  = ((c >> bits) & 0x3F) | 0x80;
2417                     }
2418                     xmlFree(name);
2419                 }
2420             }
2421         } else {
2422             unsigned int c;
2423             int bits, l;
2424
2425             if (out - buffer > buffer_size - 100) {
2426                 int indx = out - buffer;
2427
2428                 growBuffer(buffer);
2429                 out = &buffer[indx];
2430             }
2431             c = CUR_CHAR(l);
2432             if      (c <    0x80)
2433                     { *out++  = c;                bits= -6; }
2434             else if (c <   0x800)
2435                     { *out++  =((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
2436             else if (c < 0x10000)
2437                     { *out++  =((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
2438             else                 
2439                     { *out++  =((c >> 18) & 0x07) | 0xF0;  bits= 12; }
2440      
2441             for ( ; bits >= 0; bits-= 6) {
2442                 *out++  = ((c >> bits) & 0x3F) | 0x80;
2443             }
2444             NEXT;
2445         }
2446     }
2447     *out++ = 0;
2448     return(buffer);
2449 }
2450
2451 /**
2452  * htmlParseEntityRef:
2453  * @ctxt:  an HTML parser context
2454  * @str:  location to store the entity name
2455  *
2456  * parse an HTML ENTITY references
2457  *
2458  * [68] EntityRef ::= '&' Name ';'
2459  *
2460  * Returns the associated htmlEntityDescPtr if found, or NULL otherwise,
2461  *         if non-NULL *str will have to be freed by the caller.
2462  */
2463 const htmlEntityDesc *
2464 htmlParseEntityRef(htmlParserCtxtPtr ctxt, xmlChar **str) {
2465     xmlChar *name;
2466     const htmlEntityDesc * ent = NULL;
2467     *str = NULL;
2468
2469     if (CUR == '&') {
2470         NEXT;
2471         name = htmlParseName(ctxt);
2472         if (name == NULL) {
2473             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2474                 ctxt->sax->error(ctxt->userData, "htmlParseEntityRef: no name\n");
2475             ctxt->wellFormed = 0;
2476         } else {
2477             GROW;
2478             if (CUR == ';') {
2479                 *str = name;
2480
2481                 /*
2482                  * Lookup the entity in the table.
2483                  */
2484                 ent = htmlEntityLookup(name);
2485                 if (ent != NULL) /* OK that's ugly !!! */
2486                     NEXT;
2487             } else {
2488                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2489                     ctxt->sax->error(ctxt->userData,
2490                                      "htmlParseEntityRef: expecting ';'\n");
2491                 *str = name;
2492             }
2493         }
2494     }
2495     return(ent);
2496 }
2497
2498 /**
2499  * htmlParseAttValue:
2500  * @ctxt:  an HTML parser context
2501  *
2502  * parse a value for an attribute
2503  * Note: the parser won't do substitution of entities here, this
2504  * will be handled later in xmlStringGetNodeList, unless it was
2505  * asked for ctxt->replaceEntities != 0 
2506  *
2507  * Returns the AttValue parsed or NULL.
2508  */
2509
2510 static xmlChar *
2511 htmlParseAttValue(htmlParserCtxtPtr ctxt) {
2512     xmlChar *ret = NULL;
2513
2514     if (CUR == '"') {
2515         NEXT;
2516         ret = htmlParseHTMLAttribute(ctxt, '"');
2517         if (CUR != '"') {
2518             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2519                 ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
2520             ctxt->wellFormed = 0;
2521         } else
2522             NEXT;
2523     } else if (CUR == '\'') {
2524         NEXT;
2525         ret = htmlParseHTMLAttribute(ctxt, '\'');
2526         if (CUR != '\'') {
2527             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2528                 ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
2529             ctxt->wellFormed = 0;
2530         } else
2531             NEXT;
2532     } else {
2533         /*
2534          * That's an HTMLism, the attribute value may not be quoted
2535          */
2536         ret = htmlParseHTMLAttribute(ctxt, 0);
2537         if (ret == NULL) {
2538             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2539                 ctxt->sax->error(ctxt->userData, "AttValue: no value found\n");
2540             ctxt->wellFormed = 0;
2541         }
2542     }
2543     return(ret);
2544 }
2545
2546 /**
2547  * htmlParseSystemLiteral:
2548  * @ctxt:  an HTML parser context
2549  * 
2550  * parse an HTML Literal
2551  *
2552  * [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
2553  *
2554  * Returns the SystemLiteral parsed or NULL
2555  */
2556
2557 static xmlChar *
2558 htmlParseSystemLiteral(htmlParserCtxtPtr ctxt) {
2559     const xmlChar *q;
2560     xmlChar *ret = NULL;
2561
2562     if (CUR == '"') {
2563         NEXT;
2564         q = CUR_PTR;
2565         while ((IS_CHAR(CUR)) && (CUR != '"'))
2566             NEXT;
2567         if (!IS_CHAR(CUR)) {
2568             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2569                 ctxt->sax->error(ctxt->userData, "Unfinished SystemLiteral\n");
2570             ctxt->wellFormed = 0;
2571         } else {
2572             ret = xmlStrndup(q, CUR_PTR - q);
2573             NEXT;
2574         }
2575     } else if (CUR == '\'') {
2576         NEXT;
2577         q = CUR_PTR;
2578         while ((IS_CHAR(CUR)) && (CUR != '\''))
2579             NEXT;
2580         if (!IS_CHAR(CUR)) {
2581             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2582                 ctxt->sax->error(ctxt->userData, "Unfinished SystemLiteral\n");
2583             ctxt->wellFormed = 0;
2584         } else {
2585             ret = xmlStrndup(q, CUR_PTR - q);
2586             NEXT;
2587         }
2588     } else {
2589         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2590             ctxt->sax->error(ctxt->userData,
2591                              "SystemLiteral \" or ' expected\n");
2592         ctxt->wellFormed = 0;
2593     }
2594     
2595     return(ret);
2596 }
2597
2598 /**
2599  * htmlParsePubidLiteral:
2600  * @ctxt:  an HTML parser context
2601  *
2602  * parse an HTML public literal
2603  *
2604  * [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
2605  *
2606  * Returns the PubidLiteral parsed or NULL.
2607  */
2608
2609 static xmlChar *
2610 htmlParsePubidLiteral(htmlParserCtxtPtr ctxt) {
2611     const xmlChar *q;
2612     xmlChar *ret = NULL;
2613     /*
2614      * Name ::= (Letter | '_') (NameChar)*
2615      */
2616     if (CUR == '"') {
2617         NEXT;
2618         q = CUR_PTR;
2619         while (IS_PUBIDCHAR(CUR)) NEXT;
2620         if (CUR != '"') {
2621             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2622                 ctxt->sax->error(ctxt->userData, "Unfinished PubidLiteral\n");
2623             ctxt->wellFormed = 0;
2624         } else {
2625             ret = xmlStrndup(q, CUR_PTR - q);
2626             NEXT;
2627         }
2628     } else if (CUR == '\'') {
2629         NEXT;
2630         q = CUR_PTR;
2631         while ((IS_LETTER(CUR)) && (CUR != '\''))
2632             NEXT;
2633         if (!IS_LETTER(CUR)) {
2634             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2635                 ctxt->sax->error(ctxt->userData, "Unfinished PubidLiteral\n");
2636             ctxt->wellFormed = 0;
2637         } else {
2638             ret = xmlStrndup(q, CUR_PTR - q);
2639             NEXT;
2640         }
2641     } else {
2642         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2643             ctxt->sax->error(ctxt->userData, "SystemLiteral \" or ' expected\n");
2644         ctxt->wellFormed = 0;
2645     }
2646     
2647     return(ret);
2648 }
2649
2650 /**
2651  * htmlParseScript:
2652  * @ctxt:  an HTML parser context
2653  *
2654  * parse the content of an HTML SCRIPT or STYLE element
2655  * http://www.w3.org/TR/html4/sgml/dtd.html#Script
2656  * http://www.w3.org/TR/html4/sgml/dtd.html#StyleSheet
2657  * http://www.w3.org/TR/html4/types.html#type-script
2658  * http://www.w3.org/TR/html4/types.html#h-6.15
2659  * http://www.w3.org/TR/html4/appendix/notes.html#h-B.3.2.1
2660  *
2661  * Script data ( %Script; in the DTD) can be the content of the SCRIPT
2662  * element and the value of intrinsic event attributes. User agents must
2663  * not evaluate script data as HTML markup but instead must pass it on as
2664  * data to a script engine.
2665  * NOTES:
2666  * - The content is passed like CDATA
2667  * - the attributes for style and scripting "onXXX" are also described
2668  *   as CDATA but SGML allows entities references in attributes so their
2669  *   processing is identical as other attributes
2670  */
2671 static void
2672 htmlParseScript(htmlParserCtxtPtr ctxt) {
2673     xmlChar buf[HTML_PARSER_BIG_BUFFER_SIZE + 1];
2674     int nbchar = 0;
2675     xmlChar cur;
2676
2677     SHRINK;
2678     cur = CUR;
2679     while (IS_CHAR(cur)) {
2680         if ((cur == '<') && (NXT(1) == '!') && (NXT(2) == '-') &&
2681             (NXT(3) == '-')) {
2682             if ((nbchar != 0) && (ctxt->sax != NULL) && (!ctxt->disableSAX)) {
2683                 if (ctxt->sax->cdataBlock!= NULL) {
2684                     /*
2685                      * Insert as CDATA, which is the same as HTML_PRESERVE_NODE
2686                      */
2687                     ctxt->sax->cdataBlock(ctxt->userData, buf, nbchar);
2688                 }
2689             }
2690             nbchar = 0;
2691             htmlParseComment(ctxt);
2692             cur = CUR;
2693             continue;
2694         } else if ((cur == '<') && (NXT(1) == '/')) {
2695             /*
2696              * One should break here, the specification is clear:
2697              * Authors should therefore escape "</" within the content.
2698              * Escape mechanisms are specific to each scripting or
2699              * style sheet language.
2700              */
2701             if (((NXT(2) >= 'A') && (NXT(2) <= 'Z')) ||
2702                 ((NXT(2) >= 'a') && (NXT(2) <= 'z')))
2703                 break; /* while */
2704         }
2705         buf[nbchar++] = cur;
2706         if (nbchar >= HTML_PARSER_BIG_BUFFER_SIZE) {
2707             if (ctxt->sax->cdataBlock!= NULL) {
2708                 /*
2709                  * Insert as CDATA, which is the same as HTML_PRESERVE_NODE
2710                  */
2711                 ctxt->sax->cdataBlock(ctxt->userData, buf, nbchar);
2712             }
2713             nbchar = 0;
2714         }
2715         NEXT;
2716         cur = CUR;
2717     }
2718     if (!(IS_CHAR(cur))) {
2719         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2720             ctxt->sax->error(ctxt->userData,
2721                 "Invalid char in CDATA 0x%X\n", cur);
2722         ctxt->wellFormed = 0;
2723         NEXT;
2724     }
2725
2726     if ((nbchar != 0) && (ctxt->sax != NULL) && (!ctxt->disableSAX)) {
2727         if (ctxt->sax->cdataBlock!= NULL) {
2728             /*
2729              * Insert as CDATA, which is the same as HTML_PRESERVE_NODE
2730              */
2731             ctxt->sax->cdataBlock(ctxt->userData, buf, nbchar);
2732         }
2733     }
2734 }
2735
2736
2737 /**
2738  * htmlParseCharData:
2739  * @ctxt:  an HTML parser context
2740  *
2741  * parse a CharData section.
2742  * if we are within a CDATA section ']]>' marks an end of section.
2743  *
2744  * [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
2745  */
2746
2747 static void
2748 htmlParseCharData(htmlParserCtxtPtr ctxt) {
2749     xmlChar buf[HTML_PARSER_BIG_BUFFER_SIZE + 5];
2750     int nbchar = 0;
2751     int cur, l;
2752
2753     SHRINK;
2754     cur = CUR_CHAR(l);
2755     while (((cur != '<') || (ctxt->token == '<')) &&
2756            ((cur != '&') || (ctxt->token == '&')) && 
2757            (IS_CHAR(cur))) {
2758         COPY_BUF(l,buf,nbchar,cur);
2759         if (nbchar >= HTML_PARSER_BIG_BUFFER_SIZE) {
2760             /*
2761              * Ok the segment is to be consumed as chars.
2762              */
2763             if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
2764                 if (areBlanks(ctxt, buf, nbchar)) {
2765                     if (ctxt->sax->ignorableWhitespace != NULL)
2766                         ctxt->sax->ignorableWhitespace(ctxt->userData,
2767                                                        buf, nbchar);
2768                 } else {
2769                     htmlCheckParagraph(ctxt);
2770                     if (ctxt->sax->characters != NULL)
2771                         ctxt->sax->characters(ctxt->userData, buf, nbchar);
2772                 }
2773             }
2774             nbchar = 0;
2775         }
2776         NEXTL(l);
2777         cur = CUR_CHAR(l);
2778         if (cur == 0) {
2779             SHRINK;
2780             GROW;
2781             cur = CUR_CHAR(l);
2782         }
2783     }
2784     if (nbchar != 0) {
2785         /*
2786          * Ok the segment is to be consumed as chars.
2787          */
2788         if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
2789             if (areBlanks(ctxt, buf, nbchar)) {
2790                 if (ctxt->sax->ignorableWhitespace != NULL)
2791                     ctxt->sax->ignorableWhitespace(ctxt->userData, buf, nbchar);
2792             } else {
2793                 htmlCheckParagraph(ctxt);
2794                 if (ctxt->sax->characters != NULL)
2795                     ctxt->sax->characters(ctxt->userData, buf, nbchar);
2796             }
2797         }
2798     } else {
2799         /*
2800          * Loop detection
2801          */
2802         if (cur == 0)
2803             ctxt->instate = XML_PARSER_EOF;
2804     }
2805 }
2806
2807 /**
2808  * htmlParseExternalID:
2809  * @ctxt:  an HTML parser context
2810  * @publicID:  a xmlChar** receiving PubidLiteral
2811  *
2812  * Parse an External ID or a Public ID
2813  *
2814  * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
2815  *                   | 'PUBLIC' S PubidLiteral S SystemLiteral
2816  *
2817  * [83] PublicID ::= 'PUBLIC' S PubidLiteral
2818  *
2819  * Returns the function returns SystemLiteral and in the second
2820  *                case publicID receives PubidLiteral, is strict is off
2821  *                it is possible to return NULL and have publicID set.
2822  */
2823
2824 static xmlChar *
2825 htmlParseExternalID(htmlParserCtxtPtr ctxt, xmlChar **publicID) {
2826     xmlChar *URI = NULL;
2827
2828     if ((UPPER == 'S') && (UPP(1) == 'Y') &&
2829          (UPP(2) == 'S') && (UPP(3) == 'T') &&
2830          (UPP(4) == 'E') && (UPP(5) == 'M')) {
2831         SKIP(6);
2832         if (!IS_BLANK(CUR)) {
2833             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2834                 ctxt->sax->error(ctxt->userData,
2835                     "Space required after 'SYSTEM'\n");
2836             ctxt->wellFormed = 0;
2837         }
2838         SKIP_BLANKS;
2839         URI = htmlParseSystemLiteral(ctxt);
2840         if (URI == NULL) {
2841             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2842                 ctxt->sax->error(ctxt->userData,
2843                   "htmlParseExternalID: SYSTEM, no URI\n");
2844             ctxt->wellFormed = 0;
2845         }
2846     } else if ((UPPER == 'P') && (UPP(1) == 'U') &&
2847                (UPP(2) == 'B') && (UPP(3) == 'L') &&
2848                (UPP(4) == 'I') && (UPP(5) == 'C')) {
2849         SKIP(6);
2850         if (!IS_BLANK(CUR)) {
2851             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2852                 ctxt->sax->error(ctxt->userData,
2853                     "Space required after 'PUBLIC'\n");
2854             ctxt->wellFormed = 0;
2855         }
2856         SKIP_BLANKS;
2857         *publicID = htmlParsePubidLiteral(ctxt);
2858         if (*publicID == NULL) {
2859             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2860                 ctxt->sax->error(ctxt->userData, 
2861                   "htmlParseExternalID: PUBLIC, no Public Identifier\n");
2862             ctxt->wellFormed = 0;
2863         }
2864         SKIP_BLANKS;
2865         if ((CUR == '"') || (CUR == '\'')) {
2866             URI = htmlParseSystemLiteral(ctxt);
2867         }
2868     }
2869     return(URI);
2870 }
2871
2872 /**
2873  * htmlParseComment:
2874  * @ctxt:  an HTML parser context
2875  *
2876  * Parse an XML (SGML) comment <!-- .... -->
2877  *
2878  * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
2879  */
2880 static void
2881 htmlParseComment(htmlParserCtxtPtr ctxt) {
2882     xmlChar *buf = NULL;
2883     int len;
2884     int size = HTML_PARSER_BUFFER_SIZE;
2885     int q, ql;
2886     int r, rl;
2887     int cur, l;
2888     xmlParserInputState state;
2889
2890     /*
2891      * Check that there is a comment right here.
2892      */
2893     if ((RAW != '<') || (NXT(1) != '!') ||
2894         (NXT(2) != '-') || (NXT(3) != '-')) return;
2895
2896     state = ctxt->instate;
2897     ctxt->instate = XML_PARSER_COMMENT;
2898     SHRINK;
2899     SKIP(4);
2900     buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
2901     if (buf == NULL) {
2902         xmlGenericError(xmlGenericErrorContext,
2903                 "malloc of %d byte failed\n", size);
2904         ctxt->instate = state;
2905         return;
2906     }
2907     q = CUR_CHAR(ql);
2908     NEXTL(ql);
2909     r = CUR_CHAR(rl);
2910     NEXTL(rl);
2911     cur = CUR_CHAR(l);
2912     len = 0;
2913     while (IS_CHAR(cur) &&
2914            ((cur != '>') ||
2915             (r != '-') || (q != '-'))) {
2916         if (len + 5 >= size) {
2917             size *= 2;
2918             buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
2919             if (buf == NULL) {
2920                 xmlGenericError(xmlGenericErrorContext,
2921                         "realloc of %d byte failed\n", size);
2922                 ctxt->instate = state;
2923                 return;
2924             }
2925         }
2926         COPY_BUF(ql,buf,len,q);
2927         q = r;
2928         ql = rl;
2929         r = cur;
2930         rl = l;
2931         NEXTL(l);
2932         cur = CUR_CHAR(l);
2933         if (cur == 0) {
2934             SHRINK;
2935             GROW;
2936             cur = CUR_CHAR(l);
2937         }
2938     }
2939     buf[len] = 0;
2940     if (!IS_CHAR(cur)) {
2941         ctxt->errNo = XML_ERR_COMMENT_NOT_FINISHED;
2942         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2943             ctxt->sax->error(ctxt->userData,
2944                              "Comment not terminated \n<!--%.50s\n", buf);
2945         ctxt->wellFormed = 0;
2946         xmlFree(buf);
2947     } else {
2948         NEXT;
2949         if ((ctxt->sax != NULL) && (ctxt->sax->comment != NULL) &&
2950             (!ctxt->disableSAX))
2951             ctxt->sax->comment(ctxt->userData, buf);
2952         xmlFree(buf);
2953     }
2954     ctxt->instate = state;
2955 }
2956
2957 /**
2958  * htmlParseCharRef:
2959  * @ctxt:  an HTML parser context
2960  *
2961  * parse Reference declarations
2962  *
2963  * [66] CharRef ::= '&#' [0-9]+ ';' |
2964  *                  '&#x' [0-9a-fA-F]+ ';'
2965  *
2966  * Returns the value parsed (as an int)
2967  */
2968 int
2969 htmlParseCharRef(htmlParserCtxtPtr ctxt) {
2970     int val = 0;
2971
2972     if ((CUR == '&') && (NXT(1) == '#') &&
2973         (NXT(2) == 'x')) {
2974         SKIP(3);
2975         while (CUR != ';') {
2976             if ((CUR >= '0') && (CUR <= '9')) 
2977                 val = val * 16 + (CUR - '0');
2978             else if ((CUR >= 'a') && (CUR <= 'f'))
2979                 val = val * 16 + (CUR - 'a') + 10;
2980             else if ((CUR >= 'A') && (CUR <= 'F'))
2981                 val = val * 16 + (CUR - 'A') + 10;
2982             else {
2983                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2984                     ctxt->sax->error(ctxt->userData, 
2985                          "htmlParseCharRef: invalid hexadecimal value\n");
2986                 ctxt->wellFormed = 0;
2987                 return(0);
2988             }
2989             NEXT;
2990         }
2991         if (CUR == ';')
2992             NEXT;
2993     } else if  ((CUR == '&') && (NXT(1) == '#')) {
2994         SKIP(2);
2995         while (CUR != ';') {
2996             if ((CUR >= '0') && (CUR <= '9')) 
2997                 val = val * 10 + (CUR - '0');
2998             else {
2999                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3000                     ctxt->sax->error(ctxt->userData, 
3001                          "htmlParseCharRef: invalid decimal value\n");
3002                 ctxt->wellFormed = 0;
3003                 return(0);
3004             }
3005             NEXT;
3006         }
3007         if (CUR == ';')
3008             NEXT;
3009     } else {
3010         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3011             ctxt->sax->error(ctxt->userData, "htmlParseCharRef: invalid value\n");
3012         ctxt->wellFormed = 0;
3013     }
3014     /*
3015      * Check the value IS_CHAR ...
3016      */
3017     if (IS_CHAR(val)) {
3018         return(val);
3019     } else {
3020         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3021             ctxt->sax->error(ctxt->userData, "htmlParseCharRef: invalid xmlChar value %d\n",
3022                              val);
3023         ctxt->wellFormed = 0;
3024     }
3025     return(0);
3026 }
3027
3028
3029 /**
3030  * htmlParseDocTypeDecl:
3031  * @ctxt:  an HTML parser context
3032  *
3033  * parse a DOCTYPE declaration
3034  *
3035  * [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? 
3036  *                      ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
3037  */
3038
3039 static void
3040 htmlParseDocTypeDecl(htmlParserCtxtPtr ctxt) {
3041     xmlChar *name;
3042     xmlChar *ExternalID = NULL;
3043     xmlChar *URI = NULL;
3044
3045     /*
3046      * We know that '<!DOCTYPE' has been detected.
3047      */
3048     SKIP(9);
3049
3050     SKIP_BLANKS;
3051
3052     /*
3053      * Parse the DOCTYPE name.
3054      */
3055     name = htmlParseName(ctxt);
3056     if (name == NULL) {
3057         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3058             ctxt->sax->error(ctxt->userData, "htmlParseDocTypeDecl : no DOCTYPE name !\n");
3059         ctxt->wellFormed = 0;
3060     }
3061     /*
3062      * Check that upper(name) == "HTML" !!!!!!!!!!!!!
3063      */
3064
3065     SKIP_BLANKS;
3066
3067     /*
3068      * Check for SystemID and ExternalID
3069      */
3070     URI = htmlParseExternalID(ctxt, &ExternalID);
3071     SKIP_BLANKS;
3072
3073     /*
3074      * We should be at the end of the DOCTYPE declaration.
3075      */
3076     if (CUR != '>') {
3077         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3078             ctxt->sax->error(ctxt->userData, "DOCTYPE improperly terminated\n");
3079         ctxt->wellFormed = 0;
3080         /* We shouldn't try to resynchronize ... */
3081     }
3082     NEXT;
3083
3084     /*
3085      * Create or update the document accordingly to the DOCTYPE
3086      */
3087     if ((ctxt->sax != NULL) && (ctxt->sax->internalSubset != NULL) &&
3088         (!ctxt->disableSAX))
3089         ctxt->sax->internalSubset(ctxt->userData, name, ExternalID, URI);
3090
3091     /*
3092      * Cleanup, since we don't use all those identifiers
3093      */
3094     if (URI != NULL) xmlFree(URI);
3095     if (ExternalID != NULL) xmlFree(ExternalID);
3096     if (name != NULL) xmlFree(name);
3097 }
3098
3099 /**
3100  * htmlParseAttribute:
3101  * @ctxt:  an HTML parser context
3102  * @value:  a xmlChar ** used to store the value of the attribute
3103  *
3104  * parse an attribute
3105  *
3106  * [41] Attribute ::= Name Eq AttValue
3107  *
3108  * [25] Eq ::= S? '=' S?
3109  *
3110  * With namespace:
3111  *
3112  * [NS 11] Attribute ::= QName Eq AttValue
3113  *
3114  * Also the case QName == xmlns:??? is handled independently as a namespace
3115  * definition.
3116  *
3117  * Returns the attribute name, and the value in *value.
3118  */
3119
3120 static xmlChar *
3121 htmlParseAttribute(htmlParserCtxtPtr ctxt, xmlChar **value) {
3122     xmlChar *name, *val = NULL;
3123
3124     *value = NULL;
3125     name = htmlParseHTMLName(ctxt);
3126     if (name == NULL) {
3127         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3128             ctxt->sax->error(ctxt->userData, "error parsing attribute name\n");
3129         ctxt->wellFormed = 0;
3130         return(NULL);
3131     }
3132
3133     /*
3134      * read the value
3135      */
3136     SKIP_BLANKS;
3137     if (CUR == '=') {
3138         NEXT;
3139         SKIP_BLANKS;
3140         val = htmlParseAttValue(ctxt);
3141         /******
3142     } else {
3143         * TODO : some attribute must have values, some may not
3144         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3145             ctxt->sax->warning(ctxt->userData,
3146                "No value for attribute %s\n", name); */
3147     }
3148
3149     *value = val;
3150     return(name);
3151 }
3152
3153 /**
3154  * htmlCheckEncoding:
3155  * @ctxt:  an HTML parser context
3156  * @attvalue: the attribute value
3157  *
3158  * Checks an http-equiv attribute from a Meta tag to detect
3159  * the encoding
3160  * If a new encoding is detected the parser is switched to decode
3161  * it and pass UTF8
3162  */
3163 static void
3164 htmlCheckEncoding(htmlParserCtxtPtr ctxt, const xmlChar *attvalue) {
3165     const xmlChar *encoding;
3166
3167     if ((ctxt == NULL) || (attvalue == NULL))
3168         return;
3169
3170     /* do not change encoding */        
3171     if (ctxt->input->encoding != NULL)
3172         return;
3173
3174     encoding = xmlStrcasestr(attvalue, BAD_CAST"charset=");
3175     if (encoding != NULL) {
3176         encoding += 8;
3177     } else {
3178         encoding = xmlStrcasestr(attvalue, BAD_CAST"charset =");
3179         if (encoding != NULL)
3180             encoding += 9;
3181     }
3182     if (encoding != NULL) {
3183         xmlCharEncoding enc;
3184         xmlCharEncodingHandlerPtr handler;
3185
3186         while ((*encoding == ' ') || (*encoding == '\t')) encoding++;
3187
3188         if (ctxt->input->encoding != NULL)
3189             xmlFree((xmlChar *) ctxt->input->encoding);
3190         ctxt->input->encoding = xmlStrdup(encoding);
3191
3192         enc = xmlParseCharEncoding((const char *) encoding);
3193         /*
3194          * registered set of known encodings
3195          */
3196         if (enc != XML_CHAR_ENCODING_ERROR) {
3197             xmlSwitchEncoding(ctxt, enc);
3198             ctxt->charset = XML_CHAR_ENCODING_UTF8;
3199         } else {
3200             /*
3201              * fallback for unknown encodings
3202              */
3203             handler = xmlFindCharEncodingHandler((const char *) encoding);
3204             if (handler != NULL) {
3205                 xmlSwitchToEncoding(ctxt, handler);
3206                 ctxt->charset = XML_CHAR_ENCODING_UTF8;
3207             } else {
3208                 ctxt->errNo = XML_ERR_UNSUPPORTED_ENCODING;
3209             }
3210         }
3211
3212         if ((ctxt->input->buf != NULL) &&
3213             (ctxt->input->buf->encoder != NULL) &&
3214             (ctxt->input->buf->raw != NULL) &&
3215             (ctxt->input->buf->buffer != NULL)) {
3216             int nbchars;
3217             int processed;
3218
3219             /*
3220              * convert as much as possible to the parser reading buffer.
3221              */
3222             processed = ctxt->input->cur - ctxt->input->base;
3223             xmlBufferShrink(ctxt->input->buf->buffer, processed);
3224             nbchars = xmlCharEncInFunc(ctxt->input->buf->encoder,
3225                                        ctxt->input->buf->buffer,
3226                                        ctxt->input->buf->raw);
3227             if (nbchars < 0) {
3228                 ctxt->errNo = XML_ERR_INVALID_ENCODING;
3229                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3230                     ctxt->sax->error(ctxt->userData, 
3231                      "htmlCheckEncoding: encoder error\n");
3232             }
3233             ctxt->input->base =
3234             ctxt->input->cur = ctxt->input->buf->buffer->content;
3235         }
3236     }
3237 }
3238
3239 /**
3240  * htmlCheckMeta:
3241  * @ctxt:  an HTML parser context
3242  * @atts:  the attributes values
3243  *
3244  * Checks an attributes from a Meta tag
3245  */
3246 static void
3247 htmlCheckMeta(htmlParserCtxtPtr ctxt, const xmlChar **atts) {
3248     int i;
3249     const xmlChar *att, *value;
3250     int http = 0;
3251     const xmlChar *content = NULL;
3252
3253     if ((ctxt == NULL) || (atts == NULL))
3254         return;
3255
3256     i = 0;
3257     att = atts[i++];
3258     while (att != NULL) {
3259         value = atts[i++];
3260         if ((value != NULL) && (!xmlStrcasecmp(att, BAD_CAST"http-equiv"))
3261          && (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
3262             http = 1;
3263         else if ((value != NULL) && (!xmlStrcasecmp(att, BAD_CAST"content")))
3264             content = value;
3265         att = atts[i++];
3266     }
3267     if ((http) && (content != NULL))
3268         htmlCheckEncoding(ctxt, content);
3269
3270 }
3271
3272 /**
3273  * htmlParseStartTag:
3274  * @ctxt:  an HTML parser context
3275  * 
3276  * parse a start of tag either for rule element or
3277  * EmptyElement. In both case we don't parse the tag closing chars.
3278  *
3279  * [40] STag ::= '<' Name (S Attribute)* S? '>'
3280  *
3281  * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
3282  *
3283  * With namespace:
3284  *
3285  * [NS 8] STag ::= '<' QName (S Attribute)* S? '>'
3286  *
3287  * [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>'
3288  *
3289  */
3290
3291 static void
3292 htmlParseStartTag(htmlParserCtxtPtr ctxt) {
3293     xmlChar *name;
3294     xmlChar *attname;
3295     xmlChar *attvalue;
3296     const xmlChar **atts = NULL;
3297     int nbatts = 0;
3298     int maxatts = 0;
3299     int meta = 0;
3300     int i;
3301
3302     if (CUR != '<') return;
3303     NEXT;
3304
3305     GROW;
3306     name = htmlParseHTMLName(ctxt);
3307     if (name == NULL) {
3308         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3309             ctxt->sax->error(ctxt->userData, 
3310              "htmlParseStartTag: invalid element name\n");
3311         ctxt->wellFormed = 0;
3312         /* Dump the bogus tag like browsers do */
3313         while ((IS_CHAR(CUR)) && (CUR != '>'))
3314             NEXT;
3315         return;
3316     }
3317     if (xmlStrEqual(name, BAD_CAST"meta"))
3318         meta = 1;
3319
3320     /*
3321      * Check for auto-closure of HTML elements.
3322      */
3323     htmlAutoClose(ctxt, name);
3324
3325     /*
3326      * Check for implied HTML elements.
3327      */
3328     htmlCheckImplied(ctxt, name);
3329
3330     /*
3331      * Avoid html at any level > 0, head at any level != 1
3332      * or any attempt to recurse body
3333      */
3334     if ((ctxt->nameNr > 0) && (xmlStrEqual(name, BAD_CAST"html"))) {
3335         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3336             ctxt->sax->error(ctxt->userData, 
3337              "htmlParseStartTag: misplaced <html> tag\n");
3338         ctxt->wellFormed = 0;
3339         xmlFree(name);
3340         return;
3341     }
3342     if ((ctxt->nameNr != 1) && 
3343         (xmlStrEqual(name, BAD_CAST"head"))) {
3344         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3345             ctxt->sax->error(ctxt->userData, 
3346              "htmlParseStartTag: misplaced <head> tag\n");
3347         ctxt->wellFormed = 0;
3348         xmlFree(name);
3349         return;
3350     }
3351     if (xmlStrEqual(name, BAD_CAST"body")) {
3352         int indx;
3353         for (indx = 0;indx < ctxt->nameNr;indx++) {
3354             if (xmlStrEqual(ctxt->nameTab[indx], BAD_CAST"body")) {
3355                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3356                     ctxt->sax->error(ctxt->userData, 
3357                      "htmlParseStartTag: misplaced <body> tag\n");
3358                 ctxt->wellFormed = 0;
3359                 xmlFree(name);
3360                 return;
3361             }
3362         }
3363     }
3364
3365     /*
3366      * Now parse the attributes, it ends up with the ending
3367      *
3368      * (S Attribute)* S?
3369      */
3370     SKIP_BLANKS;
3371     while ((IS_CHAR(CUR)) &&
3372            (CUR != '>') && 
3373            ((CUR != '/') || (NXT(1) != '>'))) {
3374         long cons = ctxt->nbChars;
3375
3376         GROW;
3377         attname = htmlParseAttribute(ctxt, &attvalue);
3378         if (attname != NULL) {
3379
3380             /*
3381              * Well formedness requires at most one declaration of an attribute
3382              */
3383             for (i = 0; i < nbatts;i += 2) {
3384                 if (xmlStrEqual(atts[i], attname)) {
3385                     if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3386                         ctxt->sax->error(ctxt->userData,
3387                                          "Attribute %s redefined\n",
3388                                          attname);
3389                     ctxt->wellFormed = 0;
3390                     xmlFree(attname);
3391                     if (attvalue != NULL)
3392                         xmlFree(attvalue);
3393                     goto failed;
3394                 }
3395             }
3396
3397             /*
3398              * Add the pair to atts
3399              */
3400             if (atts == NULL) {
3401                 maxatts = 10;
3402                 atts = (const xmlChar **) xmlMalloc(maxatts * sizeof(xmlChar *));
3403                 if (atts == NULL) {
3404                     xmlGenericError(xmlGenericErrorContext,
3405                             "malloc of %ld byte failed\n",
3406                             maxatts * (long)sizeof(xmlChar *));
3407                     if (name != NULL) xmlFree(name);
3408                     return;
3409                 }
3410             } else if (nbatts + 4 > maxatts) {
3411                 maxatts *= 2;
3412                 atts = (const xmlChar **) xmlRealloc((void *) atts,
3413                                                      maxatts * sizeof(xmlChar *));
3414                 if (atts == NULL) {
3415                     xmlGenericError(xmlGenericErrorContext,
3416                             "realloc of %ld byte failed\n",
3417                             maxatts * (long)sizeof(xmlChar *));
3418                     if (name != NULL) xmlFree(name);
3419                     return;
3420                 }
3421             }
3422             atts[nbatts++] = attname;
3423             atts[nbatts++] = attvalue;
3424             atts[nbatts] = NULL;
3425             atts[nbatts + 1] = NULL;
3426         }
3427         else {
3428             /* Dump the bogus attribute string up to the next blank or
3429              * the end of the tag. */
3430             while ((IS_CHAR(CUR)) && !(IS_BLANK(CUR)) && (CUR != '>')
3431              && ((CUR != '/') || (NXT(1) != '>')))
3432                 NEXT;
3433         }
3434
3435 failed:
3436         SKIP_BLANKS;
3437         if (cons == ctxt->nbChars) {
3438             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3439                 ctxt->sax->error(ctxt->userData, 
3440                  "htmlParseStartTag: problem parsing attributes\n");
3441             ctxt->wellFormed = 0;
3442             break;
3443         }
3444     }
3445
3446     /*
3447      * Handle specific association to the META tag
3448      */
3449     if (meta)
3450         htmlCheckMeta(ctxt, atts);
3451
3452     /*
3453      * SAX: Start of Element !
3454      */
3455     htmlnamePush(ctxt, xmlStrdup(name));
3456 #ifdef DEBUG
3457     xmlGenericError(xmlGenericErrorContext,"Start of element %s: pushed %s\n", name, ctxt->name);
3458 #endif    
3459     if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
3460         ctxt->sax->startElement(ctxt->userData, name, atts);
3461
3462     if (atts != NULL) {
3463         for (i = 0;i < nbatts;i++) {
3464             if (atts[i] != NULL)
3465                 xmlFree((xmlChar *) atts[i]);
3466         }
3467         xmlFree((void *) atts);
3468     }
3469     if (name != NULL) xmlFree(name);
3470 }
3471
3472 /**
3473  * htmlParseEndTag:
3474  * @ctxt:  an HTML parser context
3475  *
3476  * parse an end of tag
3477  *
3478  * [42] ETag ::= '</' Name S? '>'
3479  *
3480  * With namespace
3481  *
3482  * [NS 9] ETag ::= '</' QName S? '>'
3483  *
3484  * Returns 1 if the current level should be closed.
3485  */
3486
3487 static int
3488 htmlParseEndTag(htmlParserCtxtPtr ctxt) {
3489     xmlChar *name;
3490     xmlChar *oldname;
3491     int i, ret;
3492
3493     if ((CUR != '<') || (NXT(1) != '/')) {
3494         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3495             ctxt->sax->error(ctxt->userData, "htmlParseEndTag: '</' not found\n");
3496         ctxt->wellFormed = 0;
3497         return(0);
3498     }
3499     SKIP(2);
3500
3501     name = htmlParseHTMLName(ctxt);
3502     if (name == NULL) return(0);
3503
3504     /*
3505      * We should definitely be at the ending "S? '>'" part
3506      */
3507     SKIP_BLANKS;
3508     if ((!IS_CHAR(CUR)) || (CUR != '>')) {
3509         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3510             ctxt->sax->error(ctxt->userData, "End tag : expected '>'\n");
3511         ctxt->wellFormed = 0;
3512     } else
3513         NEXT;
3514
3515     /*
3516      * If the name read is not one of the element in the parsing stack
3517      * then return, it's just an error.
3518      */
3519     for (i = (ctxt->nameNr - 1);i >= 0;i--) {
3520         if (xmlStrEqual(name, ctxt->nameTab[i])) break;
3521     }
3522     if (i < 0) {
3523         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3524             ctxt->sax->error(ctxt->userData,
3525              "Unexpected end tag : %s\n", name);
3526         xmlFree(name);
3527         ctxt->wellFormed = 0;
3528         return(0);
3529     }
3530
3531
3532     /*
3533      * Check for auto-closure of HTML elements.
3534      */
3535
3536     htmlAutoCloseOnClose(ctxt, name);
3537
3538     /*
3539      * Well formedness constraints, opening and closing must match.
3540      * With the exception that the autoclose may have popped stuff out
3541      * of the stack.
3542      */
3543     if (!xmlStrEqual(name, ctxt->name)) {
3544 #ifdef DEBUG
3545         xmlGenericError(xmlGenericErrorContext,"End of tag %s: expecting %s\n", name, ctxt->name);
3546 #endif
3547         if ((ctxt->name != NULL) && 
3548             (!xmlStrEqual(ctxt->name, name))) {
3549             if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3550                 ctxt->sax->error(ctxt->userData,
3551                  "Opening and ending tag mismatch: %s and %s\n",
3552                                  name, ctxt->name);
3553             ctxt->wellFormed = 0;
3554         }
3555     }
3556
3557     /*
3558      * SAX: End of Tag
3559      */
3560     oldname = ctxt->name;
3561     if ((oldname != NULL) && (xmlStrEqual(oldname, name))) {
3562         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
3563             ctxt->sax->endElement(ctxt->userData, name);
3564         oldname = htmlnamePop(ctxt);
3565         if (oldname != NULL) {
3566 #ifdef DEBUG
3567             xmlGenericError(xmlGenericErrorContext,"End of tag %s: popping out %s\n", name, oldname);
3568 #endif
3569             xmlFree(oldname);
3570 #ifdef DEBUG
3571         } else {
3572             xmlGenericError(xmlGenericErrorContext,"End of tag %s: stack empty !!!\n", name);
3573 #endif
3574         }
3575         ret = 1;
3576     } else {
3577         ret = 0;
3578     }
3579
3580     if (name != NULL)
3581         xmlFree(name);
3582
3583     return(ret);
3584 }
3585
3586
3587 /**
3588  * htmlParseReference:
3589  * @ctxt:  an HTML parser context
3590  * 
3591  * parse and handle entity references in content,
3592  * this will end-up in a call to character() since this is either a
3593  * CharRef, or a predefined entity.
3594  */
3595 static void
3596 htmlParseReference(htmlParserCtxtPtr ctxt) {
3597     const htmlEntityDesc * ent;
3598     xmlChar out[6];
3599     xmlChar *name;
3600     if (CUR != '&') return;
3601
3602     if (NXT(1) == '#') {
3603         unsigned int c;
3604         int bits, i = 0;
3605
3606         c = htmlParseCharRef(ctxt);
3607         if (c == 0)
3608             return;
3609
3610         if      (c <    0x80) { out[i++]= c;                bits= -6; }
3611         else if (c <   0x800) { out[i++]=((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
3612         else if (c < 0x10000) { out[i++]=((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
3613         else                  { out[i++]=((c >> 18) & 0x07) | 0xF0;  bits= 12; }
3614  
3615         for ( ; bits >= 0; bits-= 6) {
3616             out[i++]= ((c >> bits) & 0x3F) | 0x80;
3617         }
3618         out[i] = 0;
3619
3620         htmlCheckParagraph(ctxt);
3621         if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
3622             ctxt->sax->characters(ctxt->userData, out, i);
3623     } else {
3624         ent = htmlParseEntityRef(ctxt, &name);
3625         if (name == NULL) {
3626             htmlCheckParagraph(ctxt);
3627             if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
3628                 ctxt->sax->characters(ctxt->userData, BAD_CAST "&", 1);
3629             return;
3630         }
3631         if ((ent == NULL) || !(ent->value > 0)) {
3632             htmlCheckParagraph(ctxt);
3633             if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL)) {
3634                 ctxt->sax->characters(ctxt->userData, BAD_CAST "&", 1);
3635                 ctxt->sax->characters(ctxt->userData, name, xmlStrlen(name));
3636                 /* ctxt->sax->characters(ctxt->userData, BAD_CAST ";", 1); */
3637             }
3638         } else {
3639             unsigned int c;
3640             int bits, i = 0;
3641
3642             c = ent->value;
3643             if      (c <    0x80)
3644                     { out[i++]= c;                bits= -6; }
3645             else if (c <   0x800)
3646                     { out[i++]=((c >>  6) & 0x1F) | 0xC0;  bits=  0; }
3647             else if (c < 0x10000)
3648                     { out[i++]=((c >> 12) & 0x0F) | 0xE0;  bits=  6; }
3649             else                 
3650                     { out[i++]=((c >> 18) & 0x07) | 0xF0;  bits= 12; }
3651      
3652             for ( ; bits >= 0; bits-= 6) {
3653                 out[i++]= ((c >> bits) & 0x3F) | 0x80;
3654             }
3655             out[i] = 0;
3656
3657             htmlCheckParagraph(ctxt);
3658             if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
3659                 ctxt->sax->characters(ctxt->userData, out, i);
3660         }
3661         xmlFree(name);
3662     }
3663 }
3664
3665 /**
3666  * htmlParseContent:
3667  * @ctxt:  an HTML parser context
3668  * @name:  the node name
3669  *
3670  * Parse a content: comment, sub-element, reference or text.
3671  *
3672  */
3673
3674 static void
3675 htmlParseContent(htmlParserCtxtPtr ctxt) {
3676     xmlChar *currentNode;
3677     int depth;
3678
3679     currentNode = xmlStrdup(ctxt->name);
3680     depth = ctxt->nameNr;
3681     while (1) {
3682         long cons = ctxt->nbChars;
3683
3684         GROW;
3685         /*
3686          * Our tag or one of it's parent or children is ending.
3687          */
3688         if ((CUR == '<') && (NXT(1) == '/')) {
3689             if (htmlParseEndTag(ctxt) &&
3690                 ((currentNode != NULL) || (ctxt->nameNr == 0))) {
3691                 if (currentNode != NULL)
3692                     xmlFree(currentNode);
3693                 return;
3694             }
3695             continue; /* while */
3696         }
3697
3698         /*
3699          * Has this node been popped out during parsing of
3700          * the next element
3701          */
3702         if ((ctxt->nameNr > 0) && (depth >= ctxt->nameNr) &&
3703             (!xmlStrEqual(currentNode, ctxt->name)))
3704              {
3705             if (currentNode != NULL) xmlFree(currentNode);
3706             return;
3707         }
3708
3709         if ((CUR != 0) && ((xmlStrEqual(currentNode, BAD_CAST"script")) ||
3710             (xmlStrEqual(currentNode, BAD_CAST"style")))) {
3711             /*
3712              * Handle SCRIPT/STYLE separately
3713              */
3714             htmlParseScript(ctxt);
3715         } else {
3716             /*
3717              * Sometimes DOCTYPE arrives in the middle of the document
3718              */
3719             if ((CUR == '<') && (NXT(1) == '!') &&
3720                 (UPP(2) == 'D') && (UPP(3) == 'O') &&
3721                 (UPP(4) == 'C') && (UPP(5) == 'T') &&
3722                 (UPP(6) == 'Y') && (UPP(7) == 'P') &&
3723                 (UPP(8) == 'E')) {
3724                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3725                     ctxt->sax->error(ctxt->userData,
3726                                      "Misplaced DOCTYPE declaration\n");
3727                 ctxt->wellFormed = 0;
3728                 htmlParseDocTypeDecl(ctxt);
3729             }
3730
3731             /*
3732              * First case :  a comment
3733              */
3734             if ((CUR == '<') && (NXT(1) == '!') &&
3735                 (NXT(2) == '-') && (NXT(3) == '-')) {
3736                 htmlParseComment(ctxt);
3737             }
3738
3739             /*
3740              * Second case :  a sub-element.
3741              */
3742             else if (CUR == '<') {
3743                 htmlParseElement(ctxt);
3744             }
3745
3746             /*
3747              * Third case : a reference. If if has not been resolved,
3748              *    parsing returns it's Name, create the node 
3749              */
3750             else if (CUR == '&') {
3751                 htmlParseReference(ctxt);
3752             }
3753
3754             /*
3755              * Fourth : end of the resource
3756              */
3757             else if (CUR == 0) {
3758                 htmlAutoCloseOnEnd(ctxt);
3759                 break;
3760             }
3761
3762             /*
3763              * Last case, text. Note that References are handled directly.
3764              */
3765             else {
3766                 htmlParseCharData(ctxt);
3767             }
3768
3769             if (cons == ctxt->nbChars) {
3770                 if (ctxt->node != NULL) {
3771                     if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3772                         ctxt->sax->error(ctxt->userData,
3773                                          "detected an error in element content\n");
3774                     ctxt->wellFormed = 0;
3775                 }
3776                 break;
3777             }
3778         }
3779         GROW;
3780     }
3781     if (currentNode != NULL) xmlFree(currentNode);
3782 }
3783
3784 /**
3785  * htmlParseElement:
3786  * @ctxt:  an HTML parser context
3787  *
3788  * parse an HTML element, this is highly recursive
3789  *
3790  * [39] element ::= EmptyElemTag | STag content ETag
3791  *
3792  * [41] Attribute ::= Name Eq AttValue
3793  */
3794
3795 void
3796 htmlParseElement(htmlParserCtxtPtr ctxt) {
3797     xmlChar *name;
3798     xmlChar *currentNode = NULL;
3799     const htmlElemDesc * info;
3800     htmlParserNodeInfo node_info;
3801     xmlChar *oldname;
3802     int depth = ctxt->nameNr;
3803     const xmlChar *oldptr;
3804
3805     /* Capture start position */
3806     if (ctxt->record_info) {
3807         node_info.begin_pos = ctxt->input->consumed +
3808                           (CUR_PTR - ctxt->input->base);
3809         node_info.begin_line = ctxt->input->line;
3810     }
3811
3812     oldname = xmlStrdup(ctxt->name);
3813     htmlParseStartTag(ctxt);
3814     name = ctxt->name;
3815 #ifdef DEBUG
3816     if (oldname == NULL)
3817         xmlGenericError(xmlGenericErrorContext,
3818                 "Start of element %s\n", name);
3819     else if (name == NULL)      
3820         xmlGenericError(xmlGenericErrorContext,
3821                 "Start of element failed, was %s\n", oldname);
3822     else        
3823         xmlGenericError(xmlGenericErrorContext,
3824                 "Start of element %s, was %s\n", name, oldname);
3825 #endif
3826     if (((depth == ctxt->nameNr) && (xmlStrEqual(oldname, ctxt->name))) ||
3827         (name == NULL)) {
3828         if (CUR == '>')
3829             NEXT;
3830         if (oldname != NULL)
3831             xmlFree(oldname);
3832         return;
3833     }
3834     if (oldname != NULL)
3835         xmlFree(oldname);
3836
3837     /*
3838      * Lookup the info for that element.
3839      */
3840     info = htmlTagLookup(name);
3841     if (info == NULL) {
3842         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3843             ctxt->sax->error(ctxt->userData, "Tag %s invalid\n",
3844                              name);
3845         ctxt->wellFormed = 0;
3846     } else if (info->depr) {
3847 /***************************
3848         if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
3849             ctxt->sax->warning(ctxt->userData, "Tag %s is deprecated\n",
3850                                name);
3851  ***************************/
3852     }
3853
3854     /*
3855      * Check for an Empty Element labeled the XML/SGML way
3856      */
3857     if ((CUR == '/') && (NXT(1) == '>')) {
3858         SKIP(2);
3859         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
3860             ctxt->sax->endElement(ctxt->userData, name);
3861         oldname = htmlnamePop(ctxt);
3862 #ifdef DEBUG
3863         xmlGenericError(xmlGenericErrorContext,"End of tag the XML way: popping out %s\n", oldname);
3864 #endif
3865         if (oldname != NULL)
3866             xmlFree(oldname);
3867         return;
3868     }
3869
3870     if (CUR == '>') {
3871         NEXT;
3872     } else {
3873         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3874             ctxt->sax->error(ctxt->userData,
3875                              "Couldn't find end of Start Tag %s\n",
3876                              name);
3877         ctxt->wellFormed = 0;
3878
3879         /*
3880          * end of parsing of this node.
3881          */
3882         if (xmlStrEqual(name, ctxt->name)) { 
3883             nodePop(ctxt);
3884             oldname = htmlnamePop(ctxt);
3885 #ifdef DEBUG
3886             xmlGenericError(xmlGenericErrorContext,"End of start tag problem: popping out %s\n", oldname);
3887 #endif
3888             if (oldname != NULL)
3889                 xmlFree(oldname);
3890         }    
3891
3892         /*
3893          * Capture end position and add node
3894          */
3895         if ( currentNode != NULL && ctxt->record_info ) {
3896            node_info.end_pos = ctxt->input->consumed +
3897                               (CUR_PTR - ctxt->input->base);
3898            node_info.end_line = ctxt->input->line;
3899            node_info.node = ctxt->node;
3900            xmlParserAddNodeInfo(ctxt, &node_info);
3901         }
3902         return;
3903     }
3904
3905     /*
3906      * Check for an Empty Element from DTD definition
3907      */
3908     if ((info != NULL) && (info->empty)) {
3909         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
3910             ctxt->sax->endElement(ctxt->userData, name);
3911         oldname = htmlnamePop(ctxt);
3912 #ifdef DEBUG
3913         xmlGenericError(xmlGenericErrorContext,"End of empty tag %s : popping out %s\n", name, oldname);
3914 #endif
3915         if (oldname != NULL)
3916             xmlFree(oldname);
3917         return;
3918     }
3919
3920     /*
3921      * Parse the content of the element:
3922      */
3923     currentNode = xmlStrdup(ctxt->name);
3924     depth = ctxt->nameNr;
3925     while (IS_CHAR(CUR)) {
3926         oldptr = ctxt->input->cur;
3927         htmlParseContent(ctxt);
3928         if (oldptr==ctxt->input->cur) break;
3929         if (ctxt->nameNr < depth) break; 
3930     }   
3931
3932     /*
3933      * Capture end position and add node
3934      */
3935     if ( currentNode != NULL && ctxt->record_info ) {
3936        node_info.end_pos = ctxt->input->consumed +
3937                           (CUR_PTR - ctxt->input->base);
3938        node_info.end_line = ctxt->input->line;
3939        node_info.node = ctxt->node;
3940        xmlParserAddNodeInfo(ctxt, &node_info);
3941     }
3942     if (!IS_CHAR(CUR)) {
3943         htmlAutoCloseOnEnd(ctxt);
3944     }
3945
3946     if (currentNode != NULL)
3947         xmlFree(currentNode);
3948 }
3949
3950 /**
3951  * htmlParseDocument:
3952  * @ctxt:  an HTML parser context
3953  * 
3954  * parse an HTML document (and build a tree if using the standard SAX
3955  * interface).
3956  *
3957  * Returns 0, -1 in case of error. the parser context is augmented
3958  *                as a result of the parsing.
3959  */
3960
3961 int
3962 htmlParseDocument(htmlParserCtxtPtr ctxt) {
3963     xmlDtdPtr dtd;
3964
3965     xmlInitParser();
3966
3967     htmlDefaultSAXHandlerInit();
3968     ctxt->html = 1;
3969
3970     GROW;
3971     /*
3972      * SAX: beginning of the document processing.
3973      */
3974     if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
3975         ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
3976
3977     /*
3978      * Wipe out everything which is before the first '<'
3979      */
3980     SKIP_BLANKS;
3981     if (CUR == 0) {
3982         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
3983             ctxt->sax->error(ctxt->userData, "Document is empty\n");
3984         ctxt->wellFormed = 0;
3985     }
3986
3987     if ((ctxt->sax) && (ctxt->sax->startDocument) && (!ctxt->disableSAX))
3988         ctxt->sax->startDocument(ctxt->userData);
3989
3990
3991     /*
3992      * Parse possible comments before any content
3993      */
3994     while ((CUR == '<') && (NXT(1) == '!') &&
3995            (NXT(2) == '-') && (NXT(3) == '-')) {
3996         htmlParseComment(ctxt);    
3997         SKIP_BLANKS;
3998     }      
3999
4000
4001     /*
4002      * Then possibly doc type declaration(s) and more Misc
4003      * (doctypedecl Misc*)?
4004      */
4005     if ((CUR == '<') && (NXT(1) == '!') &&
4006         (UPP(2) == 'D') && (UPP(3) == 'O') &&
4007         (UPP(4) == 'C') && (UPP(5) == 'T') &&
4008         (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4009         (UPP(8) == 'E')) {
4010         htmlParseDocTypeDecl(ctxt);
4011     }
4012     SKIP_BLANKS;
4013
4014     /*
4015      * Parse possible comments before any content
4016      */
4017     while ((CUR == '<') && (NXT(1) == '!') &&
4018            (NXT(2) == '-') && (NXT(3) == '-')) {
4019         htmlParseComment(ctxt);    
4020         SKIP_BLANKS;
4021     }      
4022
4023     /*
4024      * Time to start parsing the tree itself
4025      */
4026     htmlParseContent(ctxt);
4027
4028     /*
4029      * autoclose
4030      */
4031     if (CUR == 0)
4032         htmlAutoCloseOnEnd(ctxt);
4033
4034
4035     /*
4036      * SAX: end of the document processing.
4037      */
4038     if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
4039         ctxt->sax->endDocument(ctxt->userData);
4040
4041     if (ctxt->myDoc != NULL) {
4042         dtd = xmlGetIntSubset(ctxt->myDoc);
4043         if (dtd == NULL)
4044             ctxt->myDoc->intSubset = 
4045                 xmlCreateIntSubset(ctxt->myDoc, BAD_CAST "HTML", 
4046                     BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN",
4047                     BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd");
4048     }
4049     if (! ctxt->wellFormed) return(-1);
4050     return(0);
4051 }
4052
4053
4054 /************************************************************************
4055  *                                                                      *
4056  *                      Parser contexts handling                        *
4057  *                                                                      *
4058  ************************************************************************/
4059
4060 /**
4061  * xmlInitParserCtxt:
4062  * @ctxt:  an HTML parser context
4063  *
4064  * Initialize a parser context
4065  */
4066
4067 static void
4068 htmlInitParserCtxt(htmlParserCtxtPtr ctxt)
4069 {
4070     htmlSAXHandler *sax;
4071
4072     if (ctxt == NULL) return;
4073     memset(ctxt, 0, sizeof(htmlParserCtxt));
4074
4075     sax = (htmlSAXHandler *) xmlMalloc(sizeof(htmlSAXHandler));
4076     if (sax == NULL) {
4077         xmlGenericError(xmlGenericErrorContext,
4078                 "htmlInitParserCtxt: out of memory\n");
4079     }
4080     else
4081         memset(sax, 0, sizeof(htmlSAXHandler));
4082
4083     /* Allocate the Input stack */
4084     ctxt->inputTab = (htmlParserInputPtr *) 
4085                       xmlMalloc(5 * sizeof(htmlParserInputPtr));
4086     if (ctxt->inputTab == NULL) {
4087         xmlGenericError(xmlGenericErrorContext,
4088                 "htmlInitParserCtxt: out of memory\n");
4089         ctxt->inputNr = 0;
4090         ctxt->inputMax = 0;
4091         ctxt->input = NULL;
4092         return;
4093     }
4094     ctxt->inputNr = 0;
4095     ctxt->inputMax = 5;
4096     ctxt->input = NULL;
4097     ctxt->version = NULL;
4098     ctxt->encoding = NULL;
4099     ctxt->standalone = -1;
4100     ctxt->instate = XML_PARSER_START;
4101
4102     /* Allocate the Node stack */
4103     ctxt->nodeTab = (htmlNodePtr *) xmlMalloc(10 * sizeof(htmlNodePtr));
4104     if (ctxt->nodeTab == NULL) {
4105         xmlGenericError(xmlGenericErrorContext,
4106                 "htmlInitParserCtxt: out of memory\n");
4107         ctxt->nodeNr = 0;
4108         ctxt->nodeMax = 0;
4109         ctxt->node = NULL;
4110         ctxt->inputNr = 0;
4111         ctxt->inputMax = 0;
4112         ctxt->input = NULL;
4113         return;
4114     }
4115     ctxt->nodeNr = 0;
4116     ctxt->nodeMax = 10;
4117     ctxt->node = NULL;
4118
4119     /* Allocate the Name stack */
4120     ctxt->nameTab = (xmlChar **) xmlMalloc(10 * sizeof(xmlChar *));
4121     if (ctxt->nameTab == NULL) {
4122         xmlGenericError(xmlGenericErrorContext,
4123                 "htmlInitParserCtxt: out of memory\n");
4124         ctxt->nameNr = 0;
4125         ctxt->nameMax = 10;
4126         ctxt->name = NULL;
4127         ctxt->nodeNr = 0;
4128         ctxt->nodeMax = 0;
4129         ctxt->node = NULL;
4130         ctxt->inputNr = 0;
4131         ctxt->inputMax = 0;
4132         ctxt->input = NULL;
4133         return;
4134     }
4135     ctxt->nameNr = 0;
4136     ctxt->nameMax = 10;
4137     ctxt->name = NULL;
4138
4139     if (sax == NULL) ctxt->sax = &htmlDefaultSAXHandler;
4140     else {
4141         ctxt->sax = sax;
4142         memcpy(sax, &htmlDefaultSAXHandler, sizeof(htmlSAXHandler));
4143     }
4144     ctxt->userData = ctxt;
4145     ctxt->myDoc = NULL;
4146     ctxt->wellFormed = 1;
4147     ctxt->replaceEntities = 0;
4148     ctxt->linenumbers = xmlLineNumbersDefaultValue;
4149     ctxt->html = 1;
4150     ctxt->record_info = 0;
4151     ctxt->validate = 0;
4152     ctxt->nbChars = 0;
4153     ctxt->checkIndex = 0;
4154     ctxt->catalogs = NULL;
4155     xmlInitNodeInfoSeq(&ctxt->node_seq);
4156 }
4157
4158 /**
4159  * htmlFreeParserCtxt:
4160  * @ctxt:  an HTML parser context
4161  *
4162  * Free all the memory used by a parser context. However the parsed
4163  * document in ctxt->myDoc is not freed.
4164  */
4165
4166 void
4167 htmlFreeParserCtxt(htmlParserCtxtPtr ctxt)
4168 {
4169     xmlFreeParserCtxt(ctxt);
4170 }
4171
4172 /**
4173  * htmlNewParserCtxt:
4174  *
4175  * Allocate and initialize a new parser context.
4176  *
4177  * Returns the xmlParserCtxtPtr or NULL
4178  */
4179
4180 static htmlParserCtxtPtr
4181 htmlNewParserCtxt(void)
4182 {
4183     xmlParserCtxtPtr ctxt;
4184
4185     ctxt = (xmlParserCtxtPtr) xmlMalloc(sizeof(xmlParserCtxt));
4186     if (ctxt == NULL) {
4187         xmlGenericError(xmlGenericErrorContext,
4188                 "xmlNewParserCtxt : cannot allocate context\n");
4189         return(NULL);
4190     }
4191     memset(ctxt, 0, sizeof(xmlParserCtxt));
4192     htmlInitParserCtxt(ctxt);
4193     return(ctxt);
4194 }
4195
4196 /**
4197  * htmlCreateMemoryParserCtxt:
4198  * @buffer:  a pointer to a char array
4199  * @size:  the size of the array
4200  *
4201  * Create a parser context for an HTML in-memory document.
4202  *
4203  * Returns the new parser context or NULL
4204  */
4205 static htmlParserCtxtPtr
4206 htmlCreateMemoryParserCtxt(const char *buffer, int size) {
4207     xmlParserCtxtPtr ctxt;
4208     xmlParserInputPtr input;
4209     xmlParserInputBufferPtr buf;
4210
4211     if (buffer == NULL)
4212         return(NULL);
4213     if (size <= 0)
4214         return(NULL);
4215
4216     ctxt = htmlNewParserCtxt();
4217     if (ctxt == NULL)
4218         return(NULL);
4219
4220     buf = xmlParserInputBufferCreateMem(buffer, size, XML_CHAR_ENCODING_NONE);
4221     if (buf == NULL) return(NULL);
4222
4223     input = xmlNewInputStream(ctxt);
4224     if (input == NULL) {
4225         xmlFreeParserCtxt(ctxt);
4226         return(NULL);
4227     }
4228
4229     input->filename = NULL;
4230     input->buf = buf;
4231     input->base = input->buf->buffer->content;
4232     input->cur = input->buf->buffer->content;
4233     input->end = &input->buf->buffer->content[input->buf->buffer->use];
4234
4235     inputPush(ctxt, input);
4236     return(ctxt);
4237 }
4238
4239 /**
4240  * htmlCreateDocParserCtxt:
4241  * @cur:  a pointer to an array of xmlChar
4242  * @encoding:  a free form C string describing the HTML document encoding, or NULL
4243  *
4244  * Create a parser context for an HTML document.
4245  *
4246  * TODO: check the need to add encoding handling there
4247  *
4248  * Returns the new parser context or NULL
4249  */
4250 static htmlParserCtxtPtr
4251 htmlCreateDocParserCtxt(xmlChar *cur, const char *encoding ATTRIBUTE_UNUSED) {
4252     int len;
4253     htmlParserCtxtPtr ctxt;
4254
4255     if (cur == NULL)
4256         return(NULL);
4257     len = xmlStrlen(cur);
4258     ctxt = htmlCreateMemoryParserCtxt((char *)cur, len);
4259
4260     if (encoding != NULL) {
4261         xmlCharEncoding enc;
4262         xmlCharEncodingHandlerPtr handler;
4263
4264         if (ctxt->input->encoding != NULL)
4265             xmlFree((xmlChar *) ctxt->input->encoding);
4266         ctxt->input->encoding = (const xmlChar *) encoding;
4267
4268         enc = xmlParseCharEncoding(encoding);
4269         /*
4270          * registered set of known encodings
4271          */
4272         if (enc != XML_CHAR_ENCODING_ERROR) {
4273             xmlSwitchEncoding(ctxt, enc);
4274             if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
4275                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4276                     ctxt->sax->error(ctxt->userData,
4277                          "Unsupported encoding %s\n", encoding);
4278                 ctxt->input->encoding = NULL;
4279             }
4280         } else {
4281             /*
4282              * fallback for unknown encodings
4283              */
4284             handler = xmlFindCharEncodingHandler((const char *) encoding);
4285             if (handler != NULL) {
4286                 xmlSwitchToEncoding(ctxt, handler);
4287             } else {
4288                 ctxt->errNo = XML_ERR_UNSUPPORTED_ENCODING;
4289                 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4290                     ctxt->sax->error(ctxt->userData,
4291                          "Unsupported encoding %s\n", encoding);
4292             }
4293         }
4294     }
4295     return(ctxt);
4296 }
4297
4298 /************************************************************************
4299  *                                                                      *
4300  *              Progressive parsing interfaces                          *
4301  *                                                                      *
4302  ************************************************************************/
4303
4304 /**
4305  * htmlParseLookupSequence:
4306  * @ctxt:  an HTML parser context
4307  * @first:  the first char to lookup
4308  * @next:  the next char to lookup or zero
4309  * @third:  the next char to lookup or zero
4310  *
4311  * Try to find if a sequence (first, next, third) or  just (first next) or
4312  * (first) is available in the input stream.
4313  * This function has a side effect of (possibly) incrementing ctxt->checkIndex
4314  * to avoid rescanning sequences of bytes, it DOES change the state of the
4315  * parser, do not use liberally.
4316  * This is basically similar to xmlParseLookupSequence()
4317  *
4318  * Returns the index to the current parsing point if the full sequence
4319  *      is available, -1 otherwise.
4320  */
4321 static int
4322 htmlParseLookupSequence(htmlParserCtxtPtr ctxt, xmlChar first,
4323                        xmlChar next, xmlChar third) {
4324     int base, len;
4325     htmlParserInputPtr in;
4326     const xmlChar *buf;
4327     int incomment = 0;
4328
4329     in = ctxt->input;
4330     if (in == NULL) return(-1);
4331     base = in->cur - in->base;
4332     if (base < 0) return(-1);
4333     if (ctxt->checkIndex > base)
4334         base = ctxt->checkIndex;
4335     if (in->buf == NULL) {
4336         buf = in->base;
4337         len = in->length;
4338     } else {
4339         buf = in->buf->buffer->content;
4340         len = in->buf->buffer->use;
4341     }
4342     /* take into account the sequence length */
4343     if (third) len -= 2;
4344     else if (next) len --;
4345     for (;base < len;base++) {
4346         if (!incomment && (base + 4 < len)) {
4347             if ((buf[base] == '<') && (buf[base + 1] == '!') &&
4348                 (buf[base + 2] == '-') && (buf[base + 3] == '-')) {
4349                 incomment = 1;
4350             }
4351             /* do not increment base, some people use <!--> */
4352         }
4353         if (incomment) {
4354             if (base + 3 < len)
4355                 return(-1);
4356             if ((buf[base] == '-') && (buf[base + 1] == '-') &&
4357                 (buf[base + 2] == '>')) {
4358                 incomment = 0;
4359                 base += 2;
4360             }
4361             continue;
4362         }
4363         if (buf[base] == first) {
4364             if (third != 0) {
4365                 if ((buf[base + 1] != next) ||
4366                     (buf[base + 2] != third)) continue;
4367             } else if (next != 0) {
4368                 if (buf[base + 1] != next) continue;
4369             }
4370             ctxt->checkIndex = 0;
4371 #ifdef DEBUG_PUSH
4372             if (next == 0)
4373                 xmlGenericError(xmlGenericErrorContext,
4374                         "HPP: lookup '%c' found at %d\n",
4375                         first, base);
4376             else if (third == 0)
4377                 xmlGenericError(xmlGenericErrorContext,
4378                         "HPP: lookup '%c%c' found at %d\n",
4379                         first, next, base);
4380             else 
4381                 xmlGenericError(xmlGenericErrorContext,
4382                         "HPP: lookup '%c%c%c' found at %d\n",
4383                         first, next, third, base);
4384 #endif
4385             return(base - (in->cur - in->base));
4386         }
4387     }
4388     ctxt->checkIndex = base;
4389 #ifdef DEBUG_PUSH
4390     if (next == 0)
4391         xmlGenericError(xmlGenericErrorContext,
4392                 "HPP: lookup '%c' failed\n", first);
4393     else if (third == 0)
4394         xmlGenericError(xmlGenericErrorContext,
4395                 "HPP: lookup '%c%c' failed\n", first, next);
4396     else        
4397         xmlGenericError(xmlGenericErrorContext,
4398                 "HPP: lookup '%c%c%c' failed\n", first, next, third);
4399 #endif
4400     return(-1);
4401 }
4402
4403 /**
4404  * htmlParseTryOrFinish:
4405  * @ctxt:  an HTML parser context
4406  * @terminate:  last chunk indicator
4407  *
4408  * Try to progress on parsing
4409  *
4410  * Returns zero if no parsing was possible
4411  */
4412 static int
4413 htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate) {
4414     int ret = 0;
4415     htmlParserInputPtr in;
4416     int avail = 0;
4417     xmlChar cur, next;
4418
4419 #ifdef DEBUG_PUSH
4420     switch (ctxt->instate) {
4421         case XML_PARSER_EOF:
4422             xmlGenericError(xmlGenericErrorContext,
4423                     "HPP: try EOF\n"); break;
4424         case XML_PARSER_START:
4425             xmlGenericError(xmlGenericErrorContext,
4426                     "HPP: try START\n"); break;
4427         case XML_PARSER_MISC:
4428             xmlGenericError(xmlGenericErrorContext,
4429                     "HPP: try MISC\n");break;
4430         case XML_PARSER_COMMENT:
4431             xmlGenericError(xmlGenericErrorContext,
4432                     "HPP: try COMMENT\n");break;
4433         case XML_PARSER_PROLOG:
4434             xmlGenericError(xmlGenericErrorContext,
4435                     "HPP: try PROLOG\n");break;
4436         case XML_PARSER_START_TAG:
4437             xmlGenericError(xmlGenericErrorContext,
4438                     "HPP: try START_TAG\n");break;
4439         case XML_PARSER_CONTENT:
4440             xmlGenericError(xmlGenericErrorContext,
4441                     "HPP: try CONTENT\n");break;
4442         case XML_PARSER_CDATA_SECTION:
4443             xmlGenericError(xmlGenericErrorContext,
4444                     "HPP: try CDATA_SECTION\n");break;
4445         case XML_PARSER_END_TAG:
4446             xmlGenericError(xmlGenericErrorContext,
4447                     "HPP: try END_TAG\n");break;
4448         case XML_PARSER_ENTITY_DECL:
4449             xmlGenericError(xmlGenericErrorContext,
4450                     "HPP: try ENTITY_DECL\n");break;
4451         case XML_PARSER_ENTITY_VALUE:
4452             xmlGenericError(xmlGenericErrorContext,
4453                     "HPP: try ENTITY_VALUE\n");break;
4454         case XML_PARSER_ATTRIBUTE_VALUE:
4455             xmlGenericError(xmlGenericErrorContext,
4456                     "HPP: try ATTRIBUTE_VALUE\n");break;
4457         case XML_PARSER_DTD:
4458             xmlGenericError(xmlGenericErrorContext,
4459                     "HPP: try DTD\n");break;
4460         case XML_PARSER_EPILOG:
4461             xmlGenericError(xmlGenericErrorContext,
4462                     "HPP: try EPILOG\n");break;
4463         case XML_PARSER_PI:
4464             xmlGenericError(xmlGenericErrorContext,
4465                     "HPP: try PI\n");break;
4466         case XML_PARSER_SYSTEM_LITERAL:
4467             xmlGenericError(xmlGenericErrorContext,
4468                     "HPP: try SYSTEM_LITERAL\n");break;
4469     }
4470 #endif
4471
4472     while (1) {
4473
4474         in = ctxt->input;
4475         if (in == NULL) break;
4476         if (in->buf == NULL)
4477             avail = in->length - (in->cur - in->base);
4478         else
4479             avail = in->buf->buffer->use - (in->cur - in->base);
4480         if ((avail == 0) && (terminate)) {
4481             htmlAutoCloseOnEnd(ctxt);
4482             if ((ctxt->nameNr == 0) && (ctxt->instate != XML_PARSER_EOF)) { 
4483                 /*
4484                  * SAX: end of the document processing.
4485                  */
4486                 ctxt->instate = XML_PARSER_EOF;
4487                 if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
4488                     ctxt->sax->endDocument(ctxt->userData);
4489             }
4490         }
4491         if (avail < 1)
4492             goto done;
4493         switch (ctxt->instate) {
4494             case XML_PARSER_EOF:
4495                 /*
4496                  * Document parsing is done !
4497                  */
4498                 goto done;
4499             case XML_PARSER_START:
4500                 /*
4501                  * Very first chars read from the document flow.
4502                  */
4503                 cur = in->cur[0];
4504                 if (IS_BLANK(cur)) {
4505                     SKIP_BLANKS;
4506                     if (in->buf == NULL)
4507                         avail = in->length - (in->cur - in->base);
4508                     else
4509                         avail = in->buf->buffer->use - (in->cur - in->base);
4510                 }
4511                 if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
4512                     ctxt->sax->setDocumentLocator(ctxt->userData,
4513                                                   &xmlDefaultSAXLocator);
4514                 if ((ctxt->sax) && (ctxt->sax->startDocument) &&
4515                     (!ctxt->disableSAX))
4516                     ctxt->sax->startDocument(ctxt->userData);
4517
4518                 cur = in->cur[0];
4519                 next = in->cur[1];
4520                 if ((cur == '<') && (next == '!') &&
4521                     (UPP(2) == 'D') && (UPP(3) == 'O') &&
4522                     (UPP(4) == 'C') && (UPP(5) == 'T') &&
4523                     (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4524                     (UPP(8) == 'E')) {
4525                     if ((!terminate) &&
4526                         (htmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
4527                         goto done;
4528 #ifdef DEBUG_PUSH
4529                     xmlGenericError(xmlGenericErrorContext,
4530                             "HPP: Parsing internal subset\n");
4531 #endif
4532                     htmlParseDocTypeDecl(ctxt);
4533                     ctxt->instate = XML_PARSER_PROLOG;
4534 #ifdef DEBUG_PUSH
4535                     xmlGenericError(xmlGenericErrorContext,
4536                             "HPP: entering PROLOG\n");
4537 #endif
4538                 } else {
4539                     ctxt->instate = XML_PARSER_MISC;
4540                 }
4541 #ifdef DEBUG_PUSH
4542                 xmlGenericError(xmlGenericErrorContext,
4543                         "HPP: entering MISC\n");
4544 #endif
4545                 break;
4546             case XML_PARSER_MISC:
4547                 SKIP_BLANKS;
4548                 if (in->buf == NULL)
4549                     avail = in->length - (in->cur - in->base);
4550                 else
4551                     avail = in->buf->buffer->use - (in->cur - in->base);
4552                 if (avail < 2)
4553                     goto done;
4554                 cur = in->cur[0];
4555                 next = in->cur[1];
4556                 if ((cur == '<') && (next == '!') &&
4557                     (in->cur[2] == '-') && (in->cur[3] == '-')) {
4558                     if ((!terminate) &&
4559                         (htmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
4560                         goto done;
4561 #ifdef DEBUG_PUSH
4562                     xmlGenericError(xmlGenericErrorContext,
4563                             "HPP: Parsing Comment\n");
4564 #endif
4565                     htmlParseComment(ctxt);
4566                     ctxt->instate = XML_PARSER_MISC;
4567                 } else if ((cur == '<') && (next == '!') &&
4568                     (UPP(2) == 'D') && (UPP(3) == 'O') &&
4569                     (UPP(4) == 'C') && (UPP(5) == 'T') &&
4570                     (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4571                     (UPP(8) == 'E')) {
4572                     if ((!terminate) &&
4573                         (htmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
4574                         goto done;
4575 #ifdef DEBUG_PUSH
4576                     xmlGenericError(xmlGenericErrorContext,
4577                             "HPP: Parsing internal subset\n");
4578 #endif
4579                     htmlParseDocTypeDecl(ctxt);
4580                     ctxt->instate = XML_PARSER_PROLOG;
4581 #ifdef DEBUG_PUSH
4582                     xmlGenericError(xmlGenericErrorContext,
4583                             "HPP: entering PROLOG\n");
4584 #endif
4585                 } else if ((cur == '<') && (next == '!') &&
4586                            (avail < 9)) {
4587                     goto done;
4588                 } else {
4589                     ctxt->instate = XML_PARSER_START_TAG;
4590 #ifdef DEBUG_PUSH
4591                     xmlGenericError(xmlGenericErrorContext,
4592                             "HPP: entering START_TAG\n");
4593 #endif
4594                 }
4595                 break;
4596             case XML_PARSER_PROLOG:
4597                 SKIP_BLANKS;
4598                 if (in->buf == NULL)
4599                     avail = in->length - (in->cur - in->base);
4600                 else
4601                     avail = in->buf->buffer->use - (in->cur - in->base);
4602                 if (avail < 2) 
4603                     goto done;
4604                 cur = in->cur[0];
4605                 next = in->cur[1];
4606                 if ((cur == '<') && (next == '!') &&
4607                     (in->cur[2] == '-') && (in->cur[3] == '-')) {
4608                     if ((!terminate) &&
4609                         (htmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
4610                         goto done;
4611 #ifdef DEBUG_PUSH
4612                     xmlGenericError(xmlGenericErrorContext,
4613                             "HPP: Parsing Comment\n");
4614 #endif
4615                     htmlParseComment(ctxt);
4616                     ctxt->instate = XML_PARSER_PROLOG;
4617                 } else if ((cur == '<') && (next == '!') &&
4618                            (avail < 4)) {
4619                     goto done;
4620                 } else {
4621                     ctxt->instate = XML_PARSER_START_TAG;
4622 #ifdef DEBUG_PUSH
4623                     xmlGenericError(xmlGenericErrorContext,
4624                             "HPP: entering START_TAG\n");
4625 #endif
4626                 }
4627                 break;
4628             case XML_PARSER_EPILOG:
4629                 if (in->buf == NULL)
4630                     avail = in->length - (in->cur - in->base);
4631                 else
4632                     avail = in->buf->buffer->use - (in->cur - in->base);
4633                 if (avail < 1)
4634                     goto done;
4635                 cur = in->cur[0];
4636                 if (IS_BLANK(cur)) {
4637                     htmlParseCharData(ctxt);
4638                     goto done;
4639                 }
4640                 if (avail < 2)
4641                     goto done;
4642                 next = in->cur[1];
4643                 if ((cur == '<') && (next == '!') &&
4644                     (in->cur[2] == '-') && (in->cur[3] == '-')) {
4645                     if ((!terminate) &&
4646                         (htmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
4647                         goto done;
4648 #ifdef DEBUG_PUSH
4649                     xmlGenericError(xmlGenericErrorContext,
4650                             "HPP: Parsing Comment\n");
4651 #endif
4652                     htmlParseComment(ctxt);
4653                     ctxt->instate = XML_PARSER_EPILOG;
4654                 } else if ((cur == '<') && (next == '!') &&
4655                            (avail < 4)) {
4656                     goto done;
4657                 } else {
4658                     ctxt->errNo = XML_ERR_DOCUMENT_END;
4659                     ctxt->wellFormed = 0;
4660                     ctxt->instate = XML_PARSER_EOF;
4661 #ifdef DEBUG_PUSH
4662                     xmlGenericError(xmlGenericErrorContext,
4663                             "HPP: entering EOF\n");
4664 #endif
4665                     if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
4666                         ctxt->sax->endDocument(ctxt->userData);
4667                     goto done;
4668                 }
4669                 break;
4670             case XML_PARSER_START_TAG: {
4671                 xmlChar *name, *oldname;
4672                 int depth = ctxt->nameNr;
4673                 const htmlElemDesc * info;
4674
4675                 if (avail < 2)
4676                     goto done;
4677                 cur = in->cur[0];
4678                 if (cur != '<') {
4679                     ctxt->instate = XML_PARSER_CONTENT;
4680 #ifdef DEBUG_PUSH
4681                     xmlGenericError(xmlGenericErrorContext,
4682                             "HPP: entering CONTENT\n");
4683 #endif
4684                     break;
4685                 }
4686                 if (in->cur[1] == '/') {
4687                     ctxt->instate = XML_PARSER_END_TAG;
4688                     ctxt->checkIndex = 0;
4689 #ifdef DEBUG_PUSH
4690                     xmlGenericError(xmlGenericErrorContext,
4691                             "HPP: entering END_TAG\n");
4692 #endif
4693                     break;
4694                 }
4695                 if ((!terminate) &&
4696                     (htmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
4697                     goto done;
4698
4699                 oldname = xmlStrdup(ctxt->name);
4700                 htmlParseStartTag(ctxt);
4701                 name = ctxt->name;
4702 #ifdef DEBUG
4703                 if (oldname == NULL)
4704                     xmlGenericError(xmlGenericErrorContext,
4705                             "Start of element %s\n", name);
4706                 else if (name == NULL)  
4707                     xmlGenericError(xmlGenericErrorContext,
4708                             "Start of element failed, was %s\n",
4709                             oldname);
4710                 else    
4711                     xmlGenericError(xmlGenericErrorContext,
4712                             "Start of element %s, was %s\n",
4713                             name, oldname);
4714 #endif
4715                 if (((depth == ctxt->nameNr) &&
4716                      (xmlStrEqual(oldname, ctxt->name))) ||
4717                     (name == NULL)) {
4718                     if (CUR == '>')
4719                         NEXT;
4720                     if (oldname != NULL)
4721                         xmlFree(oldname);
4722                     break;
4723                 }
4724                 if (oldname != NULL)
4725                     xmlFree(oldname);
4726
4727                 /*
4728                  * Lookup the info for that element.
4729                  */
4730                 info = htmlTagLookup(name);
4731                 if (info == NULL) {
4732                     if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4733                         ctxt->sax->error(ctxt->userData, "Tag %s invalid\n",
4734                                          name);
4735                     ctxt->wellFormed = 0;
4736                 } else if (info->depr) {
4737                     /***************************
4738                     if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
4739                         ctxt->sax->warning(ctxt->userData,
4740                                            "Tag %s is deprecated\n",
4741                                            name);
4742                      ***************************/
4743                 }
4744
4745                 /*
4746                  * Check for an Empty Element labeled the XML/SGML way
4747                  */
4748                 if ((CUR == '/') && (NXT(1) == '>')) {
4749                     SKIP(2);
4750                     if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4751                         ctxt->sax->endElement(ctxt->userData, name);
4752                     oldname = htmlnamePop(ctxt);
4753 #ifdef DEBUG
4754                     xmlGenericError(xmlGenericErrorContext,"End of tag the XML way: popping out %s\n",
4755                             oldname);
4756 #endif
4757                     if (oldname != NULL)
4758                         xmlFree(oldname);
4759                     ctxt->instate = XML_PARSER_CONTENT;
4760 #ifdef DEBUG_PUSH
4761                     xmlGenericError(xmlGenericErrorContext,
4762                             "HPP: entering CONTENT\n");
4763 #endif
4764                     break;
4765                 }
4766
4767                 if (CUR == '>') {
4768                     NEXT;
4769                 } else {
4770                     if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4771                         ctxt->sax->error(ctxt->userData, 
4772                                          "Couldn't find end of Start Tag %s\n",
4773                                          name);
4774                     ctxt->wellFormed = 0;
4775
4776                     /*
4777                      * end of parsing of this node.
4778                      */
4779                     if (xmlStrEqual(name, ctxt->name)) { 
4780                         nodePop(ctxt);
4781                         oldname = htmlnamePop(ctxt);
4782 #ifdef DEBUG
4783                         xmlGenericError(xmlGenericErrorContext,
4784                          "End of start tag problem: popping out %s\n", oldname);
4785 #endif
4786                         if (oldname != NULL)
4787                             xmlFree(oldname);
4788                     }    
4789
4790                     ctxt->instate = XML_PARSER_CONTENT;
4791 #ifdef DEBUG_PUSH
4792                     xmlGenericError(xmlGenericErrorContext,
4793                             "HPP: entering CONTENT\n");
4794 #endif
4795                     break;
4796                 }
4797
4798                 /*
4799                  * Check for an Empty Element from DTD definition
4800                  */
4801                 if ((info != NULL) && (info->empty)) {
4802                     if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4803                         ctxt->sax->endElement(ctxt->userData, name);
4804                     oldname = htmlnamePop(ctxt);
4805 #ifdef DEBUG
4806                     xmlGenericError(xmlGenericErrorContext,"End of empty tag %s : popping out %s\n", name, oldname);
4807 #endif
4808                     if (oldname != NULL)
4809                         xmlFree(oldname);
4810                 }
4811                 ctxt->instate = XML_PARSER_CONTENT;
4812 #ifdef DEBUG_PUSH
4813                 xmlGenericError(xmlGenericErrorContext,
4814                         "HPP: entering CONTENT\n");
4815 #endif
4816                 break;
4817             }
4818             case XML_PARSER_CONTENT: {
4819                 long cons;
4820                 /*
4821                  * Handle preparsed entities and charRef
4822                  */
4823                 if (ctxt->token != 0) {
4824                     xmlChar chr[2] = { 0 , 0 } ;
4825
4826                     chr[0] = (xmlChar) ctxt->token;
4827                     htmlCheckParagraph(ctxt);
4828                     if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
4829                         ctxt->sax->characters(ctxt->userData, chr, 1);
4830                     ctxt->token = 0;
4831                     ctxt->checkIndex = 0;
4832                 }
4833                 if ((avail == 1) && (terminate)) {
4834                     cur = in->cur[0];
4835                     if ((cur != '<') && (cur != '&')) {
4836                         if (ctxt->sax != NULL) {
4837                             if (IS_BLANK(cur)) {
4838                                 if (ctxt->sax->ignorableWhitespace != NULL)
4839                                     ctxt->sax->ignorableWhitespace(
4840                                             ctxt->userData, &cur, 1);
4841                             } else {
4842                                 htmlCheckParagraph(ctxt);
4843                                 if (ctxt->sax->characters != NULL)
4844                                     ctxt->sax->characters(
4845                                             ctxt->userData, &cur, 1);
4846                             }
4847                         }
4848                         ctxt->token = 0;
4849                         ctxt->checkIndex = 0;
4850                         in->cur++;
4851                         break;
4852                     }
4853                 }
4854                 if (avail < 2)
4855                     goto done;
4856                 cur = in->cur[0];
4857                 next = in->cur[1];
4858                 cons = ctxt->nbChars;
4859                 if ((xmlStrEqual(ctxt->name, BAD_CAST"script")) ||
4860                     (xmlStrEqual(ctxt->name, BAD_CAST"style"))) {
4861                     /*
4862                      * Handle SCRIPT/STYLE separately
4863                      */
4864                     if ((!terminate) &&
4865                         (htmlParseLookupSequence(ctxt, '<', '/', 0) < 0))
4866                         goto done;
4867                     htmlParseScript(ctxt);
4868                     if ((cur == '<') && (next == '/')) {
4869                         ctxt->instate = XML_PARSER_END_TAG;
4870                         ctxt->checkIndex = 0;
4871 #ifdef DEBUG_PUSH
4872                         xmlGenericError(xmlGenericErrorContext,
4873                                 "HPP: entering END_TAG\n");
4874 #endif
4875                         break;
4876                     }
4877                 } else {
4878                     /*
4879                      * Sometimes DOCTYPE arrives in the middle of the document
4880                      */
4881                     if ((cur == '<') && (next == '!') &&
4882                         (UPP(2) == 'D') && (UPP(3) == 'O') &&
4883                         (UPP(4) == 'C') && (UPP(5) == 'T') &&
4884                         (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4885                         (UPP(8) == 'E')) {
4886                         if ((!terminate) &&
4887                             (htmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
4888                             goto done;
4889                         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4890                             ctxt->sax->error(ctxt->userData,
4891                                  "Misplaced DOCTYPE declaration\n");
4892                         ctxt->wellFormed = 0;
4893                         htmlParseDocTypeDecl(ctxt);
4894                     } else if ((cur == '<') && (next == '!') &&
4895                         (in->cur[2] == '-') && (in->cur[3] == '-')) {
4896                         if ((!terminate) &&
4897                             (htmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
4898                             goto done;
4899 #ifdef DEBUG_PUSH
4900                         xmlGenericError(xmlGenericErrorContext,
4901                                 "HPP: Parsing Comment\n");
4902 #endif
4903                         htmlParseComment(ctxt);
4904                         ctxt->instate = XML_PARSER_CONTENT;
4905                     } else if ((cur == '<') && (next == '!') && (avail < 4)) {
4906                         goto done;
4907                     } else if ((cur == '<') && (next == '/')) {
4908                         ctxt->instate = XML_PARSER_END_TAG;
4909                         ctxt->checkIndex = 0;
4910 #ifdef DEBUG_PUSH
4911                         xmlGenericError(xmlGenericErrorContext,
4912                                 "HPP: entering END_TAG\n");
4913 #endif
4914                         break;
4915                     } else if (cur == '<') {
4916                         ctxt->instate = XML_PARSER_START_TAG;
4917                         ctxt->checkIndex = 0;
4918 #ifdef DEBUG_PUSH
4919                         xmlGenericError(xmlGenericErrorContext,
4920                                 "HPP: entering START_TAG\n");
4921 #endif
4922                         break;
4923                     } else if (cur == '&') {
4924                         if ((!terminate) &&
4925                             (htmlParseLookupSequence(ctxt, ';', 0, 0) < 0))
4926                             goto done;
4927 #ifdef DEBUG_PUSH
4928                         xmlGenericError(xmlGenericErrorContext,
4929                                 "HPP: Parsing Reference\n");
4930 #endif
4931                         /* TODO: check generation of subtrees if noent !!! */
4932                         htmlParseReference(ctxt);
4933                     } else {
4934                         /* TODO Avoid the extra copy, handle directly !!!!!! */
4935                         /*
4936                          * Goal of the following test is:
4937                          *  - minimize calls to the SAX 'character' callback
4938                          *    when they are mergeable
4939                          */
4940                         if ((ctxt->inputNr == 1) &&
4941                             (avail < HTML_PARSER_BIG_BUFFER_SIZE)) {
4942                             if ((!terminate) &&
4943                                 (htmlParseLookupSequence(ctxt, '<', 0, 0) < 0))
4944                                 goto done;
4945                         }
4946                         ctxt->checkIndex = 0;
4947 #ifdef DEBUG_PUSH
4948                         xmlGenericError(xmlGenericErrorContext,
4949                                 "HPP: Parsing char data\n");
4950 #endif
4951                         htmlParseCharData(ctxt);
4952                     }
4953                 }
4954                 if (cons == ctxt->nbChars) {
4955                     if (ctxt->node != NULL) {
4956                         if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
4957                             ctxt->sax->error(ctxt->userData,
4958                                  "detected an error in element content\n");
4959                         ctxt->wellFormed = 0;
4960                     }
4961                     NEXT;
4962                     break;
4963                 }
4964
4965                 break;
4966             }
4967             case XML_PARSER_END_TAG:
4968                 if (avail < 2)
4969                     goto done;
4970                 if ((!terminate) &&
4971                     (htmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
4972                     goto done;
4973                 htmlParseEndTag(ctxt);
4974                 if (ctxt->nameNr == 0) {
4975                     ctxt->instate = XML_PARSER_EPILOG;
4976                 } else {
4977                     ctxt->instate = XML_PARSER_CONTENT;
4978                 }
4979                 ctxt->checkIndex = 0;
4980 #ifdef DEBUG_PUSH
4981                 xmlGenericError(xmlGenericErrorContext,
4982                         "HPP: entering CONTENT\n");
4983 #endif
4984                 break;
4985             case XML_PARSER_CDATA_SECTION:
4986                 xmlGenericError(xmlGenericErrorContext,
4987                         "HPP: internal error, state == CDATA\n");
4988                 ctxt->instate = XML_PARSER_CONTENT;
4989                 ctxt->checkIndex = 0;
4990 #ifdef DEBUG_PUSH
4991                 xmlGenericError(xmlGenericErrorContext,
4992                         "HPP: entering CONTENT\n");
4993 #endif
4994                 break;
4995             case XML_PARSER_DTD:
4996                 xmlGenericError(xmlGenericErrorContext,
4997                         "HPP: internal error, state == DTD\n");
4998                 ctxt->instate = XML_PARSER_CONTENT;
4999                 ctxt->checkIndex = 0;
5000 #ifdef DEBUG_PUSH
5001                 xmlGenericError(xmlGenericErrorContext,
5002                         "HPP: entering CONTENT\n");
5003 #endif
5004                 break;
5005             case XML_PARSER_COMMENT:
5006                 xmlGenericError(xmlGenericErrorContext,
5007                         "HPP: internal error, state == COMMENT\n");
5008                 ctxt->instate = XML_PARSER_CONTENT;
5009                 ctxt->checkIndex = 0;
5010 #ifdef DEBUG_PUSH
5011                 xmlGenericError(xmlGenericErrorContext,
5012                         "HPP: entering CONTENT\n");
5013 #endif
5014                 break;
5015             case XML_PARSER_PI:
5016                 xmlGenericError(xmlGenericErrorContext,
5017                         "HPP: internal error, state == PI\n");
5018                 ctxt->instate = XML_PARSER_CONTENT;
5019                 ctxt->checkIndex = 0;
5020 #ifdef DEBUG_PUSH
5021                 xmlGenericError(xmlGenericErrorContext,
5022                         "HPP: entering CONTENT\n");
5023 #endif
5024                 break;
5025             case XML_PARSER_ENTITY_DECL:
5026                 xmlGenericError(xmlGenericErrorContext,
5027                         "HPP: internal error, state == ENTITY_DECL\n");
5028                 ctxt->instate = XML_PARSER_CONTENT;
5029                 ctxt->checkIndex = 0;
5030 #ifdef DEBUG_PUSH
5031                 xmlGenericError(xmlGenericErrorContext,
5032                         "HPP: entering CONTENT\n");
5033 #endif
5034                 break;
5035             case XML_PARSER_ENTITY_VALUE:
5036                 xmlGenericError(xmlGenericErrorContext,
5037                         "HPP: internal error, state == ENTITY_VALUE\n");
5038                 ctxt->instate = XML_PARSER_CONTENT;
5039                 ctxt->checkIndex = 0;
5040 #ifdef DEBUG_PUSH
5041                 xmlGenericError(xmlGenericErrorContext,
5042                         "HPP: entering DTD\n");
5043 #endif
5044                 break;
5045             case XML_PARSER_ATTRIBUTE_VALUE:
5046                 xmlGenericError(xmlGenericErrorContext,
5047                         "HPP: internal error, state == ATTRIBUTE_VALUE\n");
5048                 ctxt->instate = XML_PARSER_START_TAG;
5049                 ctxt->checkIndex = 0;
5050 #ifdef DEBUG_PUSH
5051                 xmlGenericError(xmlGenericErrorContext,
5052                         "HPP: entering START_TAG\n");
5053 #endif
5054                 break;
5055             case XML_PARSER_SYSTEM_LITERAL:
5056                 xmlGenericError(xmlGenericErrorContext,
5057                         "HPP: internal error, state == XML_PARSER_SYSTEM_LITERAL\n");
5058                 ctxt->instate = XML_PARSER_CONTENT;
5059                 ctxt->checkIndex = 0;
5060 #ifdef DEBUG_PUSH
5061                 xmlGenericError(xmlGenericErrorContext,
5062                         "HPP: entering CONTENT\n");
5063 #endif
5064                 break;
5065             case XML_PARSER_IGNORE:
5066                 xmlGenericError(xmlGenericErrorContext,
5067                         "HPP: internal error, state == XML_PARSER_IGNORE\n");
5068                 ctxt->instate = XML_PARSER_CONTENT;
5069                 ctxt->checkIndex = 0;
5070 #ifdef DEBUG_PUSH
5071                 xmlGenericError(xmlGenericErrorContext,
5072                         "HPP: entering CONTENT\n");
5073 #endif
5074                 break;
5075             case XML_PARSER_PUBLIC_LITERAL:
5076                 xmlGenericError(xmlGenericErrorContext,
5077                         "HPP: internal error, state == XML_PARSER_LITERAL\n");
5078                 ctxt->instate = XML_PARSER_CONTENT;
5079                 ctxt->checkIndex = 0;
5080 #ifdef DEBUG_PUSH
5081                 xmlGenericError(xmlGenericErrorContext,
5082                         "HPP: entering CONTENT\n");
5083 #endif
5084                 break;
5085
5086         }
5087     }
5088 done:    
5089     if ((avail == 0) && (terminate)) {
5090         htmlAutoCloseOnEnd(ctxt);
5091         if ((ctxt->nameNr == 0) && (ctxt->instate != XML_PARSER_EOF)) { 
5092             /*
5093              * SAX: end of the document processing.
5094              */
5095             ctxt->instate = XML_PARSER_EOF;
5096             if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
5097                 ctxt->sax->endDocument(ctxt->userData);
5098         }
5099     }
5100     if ((ctxt->myDoc != NULL) &&
5101         ((terminate) || (ctxt->instate == XML_PARSER_EOF) ||
5102          (ctxt->instate == XML_PARSER_EPILOG))) {
5103         xmlDtdPtr dtd;
5104         dtd = xmlGetIntSubset(ctxt->myDoc);
5105         if (dtd == NULL)
5106             ctxt->myDoc->intSubset = 
5107                 xmlCreateIntSubset(ctxt->myDoc, BAD_CAST "HTML", 
5108                     BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN",
5109                     BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd");
5110     }
5111 #ifdef DEBUG_PUSH
5112     xmlGenericError(xmlGenericErrorContext, "HPP: done %d\n", ret);
5113 #endif
5114     return(ret);
5115 }
5116
5117 /**
5118  * htmlParseChunk:
5119  * @ctxt:  an XML parser context
5120  * @chunk:  an char array
5121  * @size:  the size in byte of the chunk
5122  * @terminate:  last chunk indicator
5123  *
5124  * Parse a Chunk of memory
5125  *
5126  * Returns zero if no error, the xmlParserErrors otherwise.
5127  */
5128 int
5129 htmlParseChunk(htmlParserCtxtPtr ctxt, const char *chunk, int size,
5130               int terminate) {
5131     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
5132         (ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF))  {
5133         int base = ctxt->input->base - ctxt->input->buf->buffer->content;
5134         int cur = ctxt->input->cur - ctxt->input->base;
5135         
5136         xmlParserInputBufferPush(ctxt->input->buf, size, chunk);              
5137         ctxt->input->base = ctxt->input->buf->buffer->content + base;
5138         ctxt->input->cur = ctxt->input->base + cur;
5139 #ifdef DEBUG_PUSH
5140         xmlGenericError(xmlGenericErrorContext, "HPP: pushed %d\n", size);
5141 #endif
5142
5143         if ((terminate) || (ctxt->input->buf->buffer->use > 80))
5144             htmlParseTryOrFinish(ctxt, terminate);
5145     } else if (ctxt->instate != XML_PARSER_EOF) {
5146         xmlParserInputBufferPush(ctxt->input->buf, 0, "");
5147         htmlParseTryOrFinish(ctxt, terminate);
5148     }
5149     if (terminate) {
5150         if ((ctxt->instate != XML_PARSER_EOF) &&
5151             (ctxt->instate != XML_PARSER_EPILOG) &&
5152             (ctxt->instate != XML_PARSER_MISC)) {
5153             ctxt->errNo = XML_ERR_DOCUMENT_END;
5154             ctxt->wellFormed = 0;
5155         } 
5156         if (ctxt->instate != XML_PARSER_EOF) {
5157             if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
5158                 ctxt->sax->endDocument(ctxt->userData);
5159         }
5160         ctxt->instate = XML_PARSER_EOF;
5161     }
5162     return((xmlParserErrors) ctxt->errNo);            
5163 }
5164
5165 /************************************************************************
5166  *                                                                      *
5167  *                      User entry points                               *
5168  *                                                                      *
5169  ************************************************************************/
5170
5171 /**
5172  * htmlCreatePushParserCtxt:
5173  * @sax:  a SAX handler
5174  * @user_data:  The user data returned on SAX callbacks
5175  * @chunk:  a pointer to an array of chars
5176  * @size:  number of chars in the array
5177  * @filename:  an optional file name or URI
5178  * @enc:  an optional encoding
5179  *
5180  * Create a parser context for using the HTML parser in push mode
5181  * The value of @filename is used for fetching external entities
5182  * and error/warning reports.
5183  *
5184  * Returns the new parser context or NULL
5185  */
5186 htmlParserCtxtPtr
5187 htmlCreatePushParserCtxt(htmlSAXHandlerPtr sax, void *user_data, 
5188                          const char *chunk, int size, const char *filename,
5189                          xmlCharEncoding enc) {
5190     htmlParserCtxtPtr ctxt;
5191     htmlParserInputPtr inputStream;
5192     xmlParserInputBufferPtr buf;
5193
5194     xmlInitParser();
5195
5196     buf = xmlAllocParserInputBuffer(enc);
5197     if (buf == NULL) return(NULL);
5198
5199     ctxt = (htmlParserCtxtPtr) xmlMalloc(sizeof(htmlParserCtxt));
5200     if (ctxt == NULL) {
5201         xmlFree(buf);
5202         return(NULL);
5203     }
5204     memset(ctxt, 0, sizeof(htmlParserCtxt));
5205     htmlInitParserCtxt(ctxt);
5206     if (sax != NULL) {
5207         if (ctxt->sax != &htmlDefaultSAXHandler)
5208             xmlFree(ctxt->sax);
5209         ctxt->sax = (htmlSAXHandlerPtr) xmlMalloc(sizeof(htmlSAXHandler));
5210         if (ctxt->sax == NULL) {
5211             xmlFree(buf);
5212             xmlFree(ctxt);
5213             return(NULL);
5214         }
5215         memcpy(ctxt->sax, sax, sizeof(htmlSAXHandler));
5216         if (user_data != NULL)
5217             ctxt->userData = user_data;
5218     }   
5219     if (filename == NULL) {
5220         ctxt->directory = NULL;
5221     } else {
5222         ctxt->directory = xmlParserGetDirectory(filename);
5223     }
5224
5225     inputStream = htmlNewInputStream(ctxt);
5226     if (inputStream == NULL) {
5227         xmlFreeParserCtxt(ctxt);
5228         return(NULL);
5229     }
5230
5231     if (filename == NULL)
5232         inputStream->filename = NULL;
5233     else
5234         inputStream->filename = xmlMemStrdup(filename);
5235     inputStream->buf = buf;
5236     inputStream->base = inputStream->buf->buffer->content;
5237     inputStream->cur = inputStream->buf->buffer->content;
5238
5239     inputPush(ctxt, inputStream);
5240
5241     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
5242         (ctxt->input->buf != NULL))  {        
5243         xmlParserInputBufferPush(ctxt->input->buf, size, chunk);              
5244 #ifdef DEBUG_PUSH
5245         xmlGenericError(xmlGenericErrorContext, "HPP: pushed %d\n", size);
5246 #endif
5247     }
5248
5249     return(ctxt);
5250 }
5251
5252 /**
5253  * htmlSAXParseDoc:
5254  * @cur:  a pointer to an array of xmlChar
5255  * @encoding:  a free form C string describing the HTML document encoding, or NULL
5256  * @sax:  the SAX handler block
5257  * @userData: if using SAX, this pointer will be provided on callbacks. 
5258  *
5259  * Parse an HTML in-memory document. If sax is not NULL, use the SAX callbacks
5260  * to handle parse events. If sax is NULL, fallback to the default DOM
5261  * behavior and return a tree.
5262  * 
5263  * Returns the resulting document tree unless SAX is NULL or the document is
5264  *     not well formed.
5265  */
5266
5267 htmlDocPtr
5268 htmlSAXParseDoc(xmlChar *cur, const char *encoding, htmlSAXHandlerPtr sax, void *userData) {
5269     htmlDocPtr ret;
5270     htmlParserCtxtPtr ctxt;
5271
5272     xmlInitParser();
5273
5274     if (cur == NULL) return(NULL);
5275
5276
5277     ctxt = htmlCreateDocParserCtxt(cur, encoding);
5278     if (ctxt == NULL) return(NULL);
5279     if (sax != NULL) { 
5280         ctxt->sax = sax;
5281         ctxt->userData = userData;
5282     }
5283
5284     htmlParseDocument(ctxt);
5285     ret = ctxt->myDoc;
5286     if (sax != NULL) {
5287         ctxt->sax = NULL;
5288         ctxt->userData = NULL;
5289     }
5290     htmlFreeParserCtxt(ctxt);
5291     
5292     return(ret);
5293 }
5294
5295 /**
5296  * htmlParseDoc:
5297  * @cur:  a pointer to an array of xmlChar
5298  * @encoding:  a free form C string describing the HTML document encoding, or NULL
5299  *
5300  * parse an HTML in-memory document and build a tree.
5301  * 
5302  * Returns the resulting document tree
5303  */
5304
5305 htmlDocPtr
5306 htmlParseDoc(xmlChar *cur, const char *encoding) {
5307     return(htmlSAXParseDoc(cur, encoding, NULL, NULL));
5308 }
5309
5310
5311 /**
5312  * htmlCreateFileParserCtxt:
5313  * @filename:  the filename
5314  * @encoding:  a free form C string describing the HTML document encoding, or NULL
5315  *
5316  * Create a parser context for a file content. 
5317  * Automatic support for ZLIB/Compress compressed document is provided
5318  * by default if found at compile-time.
5319  *
5320  * Returns the new parser context or NULL
5321  */
5322 htmlParserCtxtPtr
5323 htmlCreateFileParserCtxt(const char *filename, const char *encoding)
5324 {
5325     htmlParserCtxtPtr ctxt;
5326     htmlParserInputPtr inputStream;
5327     xmlParserInputBufferPtr buf;
5328     /* htmlCharEncoding enc; */
5329     xmlChar *content, *content_line = (xmlChar *) "charset=";
5330
5331     buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
5332     if (buf == NULL) return(NULL);
5333
5334     ctxt = (htmlParserCtxtPtr) xmlMalloc(sizeof(htmlParserCtxt));
5335     if (ctxt == NULL) {
5336         xmlGenericError(xmlGenericErrorContext, "malloc failed\n");
5337         return(NULL);
5338     }
5339     memset(ctxt, 0, sizeof(htmlParserCtxt));
5340     htmlInitParserCtxt(ctxt);
5341     inputStream = (htmlParserInputPtr) xmlMalloc(sizeof(htmlParserInput));
5342     if (inputStream == NULL) {
5343         xmlGenericError(xmlGenericErrorContext, "malloc failed\n");
5344         xmlFree(ctxt);
5345         return(NULL);
5346     }
5347     memset(inputStream, 0, sizeof(htmlParserInput));
5348
5349     inputStream->filename = (char *)
5350         xmlCanonicPath((xmlChar *)filename);
5351     inputStream->line = 1;
5352     inputStream->col = 1;
5353     inputStream->buf = buf;
5354     inputStream->directory = NULL;
5355
5356     inputStream->base = inputStream->buf->buffer->content;
5357     inputStream->cur = inputStream->buf->buffer->content;
5358     inputStream->free = NULL;
5359
5360     inputPush(ctxt, inputStream);
5361     
5362     /* set encoding */
5363     if (encoding) {
5364         content = xmlMalloc (xmlStrlen(content_line) + strlen(encoding) + 1);
5365         if (content) {  
5366             strcpy ((char *)content, (char *)content_line);
5367             strcat ((char *)content, (char *)encoding);
5368             htmlCheckEncoding (ctxt, content);
5369             xmlFree (content);
5370         }
5371     }
5372     
5373     return(ctxt);
5374 }
5375
5376 /**
5377  * htmlSAXParseFile:
5378  * @filename:  the filename
5379  * @encoding:  a free form C string describing the HTML document encoding, or NULL
5380  * @sax:  the SAX handler block
5381  * @userData: if using SAX, this pointer will be provided on callbacks. 
5382  *
5383  * parse an HTML file and build a tree. Automatic support for ZLIB/Compress
5384  * compressed document is provided by default if found at compile-time.
5385  * It use the given SAX function block to handle the parsing callback.
5386  * If sax is NULL, fallback to the default DOM tree building routines.
5387  *
5388  * Returns the resulting document tree unless SAX is NULL or the document is
5389  *     not well formed.
5390  */
5391
5392 htmlDocPtr
5393 htmlSAXParseFile(const char *filename, const char *encoding, htmlSAXHandlerPtr sax, 
5394                  void *userData) {
5395     htmlDocPtr ret;
5396     htmlParserCtxtPtr ctxt;
5397     htmlSAXHandlerPtr oldsax = NULL;
5398
5399     xmlInitParser();
5400
5401     ctxt = htmlCreateFileParserCtxt(filename, encoding);
5402     if (ctxt == NULL) return(NULL);
5403     if (sax != NULL) {
5404         oldsax = ctxt->sax;
5405         ctxt->sax = sax;
5406         ctxt->userData = userData;
5407     }
5408
5409     htmlParseDocument(ctxt);
5410
5411     ret = ctxt->myDoc;
5412     if (sax != NULL) {
5413         ctxt->sax = oldsax;
5414         ctxt->userData = NULL;
5415     }
5416     htmlFreeParserCtxt(ctxt);
5417     
5418     return(ret);
5419 }
5420
5421 /**
5422  * htmlParseFile:
5423  * @filename:  the filename
5424  * @encoding:  a free form C string describing the HTML document encoding, or NULL
5425  *
5426  * parse an HTML file and build a tree. Automatic support for ZLIB/Compress
5427  * compressed document is provided by default if found at compile-time.
5428  *
5429  * Returns the resulting document tree
5430  */
5431
5432 htmlDocPtr
5433 htmlParseFile(const char *filename, const char *encoding) {
5434     return(htmlSAXParseFile(filename, encoding, NULL, NULL));
5435 }
5436
5437 /**
5438  * htmlHandleOmittedElem:
5439  * @val:  int 0 or 1 
5440  *
5441  * Set and return the previous value for handling HTML omitted tags.
5442  *
5443  * Returns the last value for 0 for no handling, 1 for auto insertion.
5444  */
5445
5446 int
5447 htmlHandleOmittedElem(int val) {
5448     int old = htmlOmittedDefaultValue;
5449
5450     htmlOmittedDefaultValue = val;
5451     return(old);
5452 }
5453
5454 /**
5455  * htmlElementAllowedHere:
5456  * @parent: HTML parent element
5457  * @elt: HTML element
5458  *
5459  * Checks whether an HTML element may be a direct child of a parent element.
5460  * Note - doesn't check for deprecated elements
5461  *
5462  * Returns 1 if allowed; 0 otherwise.
5463  */
5464 int
5465 htmlElementAllowedHere(const htmlElemDesc* parent, const xmlChar* elt) {
5466   const char** p ;
5467
5468   if ( ! elt || ! parent || ! parent->subelts )
5469         return 0 ;
5470
5471   for ( p = parent->subelts; *p; ++p )
5472     if ( !xmlStrcmp((const xmlChar *)*p, elt) )
5473       return 1 ;
5474
5475   return 0 ;
5476 }
5477 /**
5478  * htmlElementStatusHere:
5479  * @parent: HTML parent element
5480  * @elt: HTML element
5481  *
5482  * Checks whether an HTML element may be a direct child of a parent element.
5483  * and if so whether it is valid or deprecated.
5484  *
5485  * Returns one of HTML_VALID, HTML_DEPRECATED, HTML_INVALID
5486  */
5487 htmlStatus
5488 htmlElementStatusHere(const htmlElemDesc* parent, const htmlElemDesc* elt) {
5489   if ( ! parent || ! elt )
5490     return HTML_INVALID ;
5491   if ( ! htmlElementAllowedHere(parent, (const xmlChar*) elt->name ) )
5492     return HTML_INVALID ;
5493
5494   return ( elt->dtd == 0 ) ? HTML_VALID : HTML_DEPRECATED ;
5495 }
5496 /**
5497  * htmlAttrAllowed:
5498  * @elt: HTML element
5499  * @attr: HTML attribute
5500  * @legacy: whether to allow deprecated attributes
5501  *
5502  * Checks whether an attribute is valid for an element
5503  * Has full knowledge of Required and Deprecated attributes
5504  *
5505  * Returns one of HTML_REQUIRED, HTML_VALID, HTML_DEPRECATED, HTML_INVALID
5506  */
5507 htmlStatus
5508 htmlAttrAllowed(const htmlElemDesc* elt, const xmlChar* attr, int legacy) {
5509   const char** p ;
5510
5511   if ( !elt || ! attr )
5512         return HTML_INVALID ;
5513
5514   if ( elt->attrs_req )
5515     for ( p = elt->attrs_req; *p; ++p)
5516       if ( !xmlStrcmp((const xmlChar*)*p, attr) )
5517         return HTML_REQUIRED ;
5518
5519   if ( elt->attrs_opt )
5520     for ( p = elt->attrs_opt; *p; ++p)
5521       if ( !xmlStrcmp((const xmlChar*)*p, attr) )
5522         return HTML_VALID ;
5523
5524   if ( legacy && elt->attrs_depr )
5525     for ( p = elt->attrs_depr; *p; ++p)
5526       if ( !xmlStrcmp((const xmlChar*)*p, attr) )
5527         return HTML_DEPRECATED ;
5528
5529   return HTML_INVALID ;
5530 }
5531 /**
5532  * htmlNodeStatus:
5533  * @node: an htmlNodePtr in a tree
5534  * @legacy: whether to allow deprecated elements (YES is faster here
5535  *      for Element nodes)
5536  *
5537  * Checks whether the tree node is valid.  Experimental (the author
5538  *     only uses the HTML enhancements in a SAX parser)
5539  *
5540  * Return: for Element nodes, a return from htmlElementAllowedHere (if
5541  *      legacy allowed) or htmlElementStatusHere (otherwise).
5542  *      for Attribute nodes, a return from htmlAttrAllowed
5543  *      for other nodes, HTML_NA (no checks performed)
5544  */
5545 htmlStatus
5546 htmlNodeStatus(const htmlNodePtr node, int legacy) {
5547   if ( ! node )
5548     return HTML_INVALID ;
5549
5550   switch ( node->type ) {
5551     case XML_ELEMENT_NODE:
5552       return legacy
5553         ? ( htmlElementAllowedHere (
5554                 htmlTagLookup(node->parent->name) , node->name
5555                 ) ? HTML_VALID : HTML_INVALID )
5556         : htmlElementStatusHere(
5557                 htmlTagLookup(node->parent->name) ,
5558                 htmlTagLookup(node->name) )
5559         ;
5560     case XML_ATTRIBUTE_NODE:
5561       return htmlAttrAllowed(
5562         htmlTagLookup(node->parent->name) , node->name, legacy) ;
5563     default: return HTML_NA ;
5564   }
5565 }
5566 #endif /* LIBXML_HTML_ENABLED */