blob: 6df2172289fd178d688ffb72ff8ef56bbdf2af42 [file] [log] [blame]
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001/*
2 * HTMLparser.c : an HTML 4.0 non-verifying parser
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel.Veillard@w3.org
7 */
8
9#ifdef WIN32
10#define HAVE_FCNTL_H
11#include <io.h>
12#else
Daniel Veillard7f7d1111999-09-22 09:46:25 +000013#include "config.h"
Daniel Veillardbe70ff71999-07-05 16:50:46 +000014#endif
Daniel Veillard7f7d1111999-09-22 09:46:25 +000015
Daniel Veillardbe70ff71999-07-05 16:50:46 +000016#include <stdio.h>
Daniel Veillardbe70ff71999-07-05 16:50:46 +000017#include <string.h> /* for memset() only */
Daniel Veillard7f7d1111999-09-22 09:46:25 +000018#ifdef HAVE_CTYPE_H
19#include <ctype.h>
20#endif
21#ifdef HAVE_STDLIB_H
Daniel Veillardbe70ff71999-07-05 16:50:46 +000022#include <stdlib.h>
Daniel Veillard7f7d1111999-09-22 09:46:25 +000023#endif
24#ifdef HAVE_SYS_STAT_H
Daniel Veillardbe70ff71999-07-05 16:50:46 +000025#include <sys/stat.h>
Daniel Veillard7f7d1111999-09-22 09:46:25 +000026#endif
Daniel Veillardbe70ff71999-07-05 16:50:46 +000027#ifdef HAVE_FCNTL_H
28#include <fcntl.h>
29#endif
30#ifdef HAVE_UNISTD_H
31#include <unistd.h>
32#endif
33#ifdef HAVE_ZLIB_H
34#include <zlib.h>
35#endif
36
Daniel Veillard6454aec1999-09-02 22:04:43 +000037#include "xmlmemory.h"
Daniel Veillardbe70ff71999-07-05 16:50:46 +000038#include "tree.h"
39#include "HTMLparser.h"
40#include "entities.h"
41#include "encoding.h"
42#include "valid.h"
43#include "parserInternals.h"
Daniel Veillarde2d034d1999-07-27 19:52:06 +000044#include "xmlIO.h"
45
46#define HTML_MAX_NAMELEN 1000
47#define INPUT_CHUNK 50
Daniel Veillardbe70ff71999-07-05 16:50:46 +000048
Daniel Veillard82150d81999-07-07 07:32:15 +000049/* #define DEBUG */
Daniel Veillard5233ffc1999-07-06 22:25:25 +000050
51/************************************************************************
52 * *
53 * Parser stacks related functions and macros *
54 * *
55 ************************************************************************/
56
57/*
58 * Generic function for accessing stacks in the Parser Context
59 */
60
61#define PUSH_AND_POP(type, name) \
62int html##name##Push(htmlParserCtxtPtr ctxt, type value) { \
63 if (ctxt->name##Nr >= ctxt->name##Max) { \
64 ctxt->name##Max *= 2; \
Daniel Veillard6454aec1999-09-02 22:04:43 +000065 ctxt->name##Tab = (void *) xmlRealloc(ctxt->name##Tab, \
Daniel Veillard5233ffc1999-07-06 22:25:25 +000066 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
67 if (ctxt->name##Tab == NULL) { \
68 fprintf(stderr, "realloc failed !\n"); \
69 exit(1); \
70 } \
71 } \
72 ctxt->name##Tab[ctxt->name##Nr] = value; \
73 ctxt->name = value; \
74 return(ctxt->name##Nr++); \
75} \
76type html##name##Pop(htmlParserCtxtPtr ctxt) { \
77 type ret; \
78 if (ctxt->name##Nr <= 0) return(0); \
79 ctxt->name##Nr--; \
80 if (ctxt->name##Nr > 0) \
81 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
82 else \
83 ctxt->name = NULL; \
84 ret = ctxt->name##Tab[ctxt->name##Nr]; \
85 ctxt->name##Tab[ctxt->name##Nr] = 0; \
86 return(ret); \
87} \
88
89PUSH_AND_POP(xmlNodePtr, node)
Daniel Veillard2673d3c1999-10-08 14:37:09 +000090PUSH_AND_POP(xmlChar*, name)
Daniel Veillard5233ffc1999-07-06 22:25:25 +000091
92/*
93 * Macros for accessing the content. Those should be used only by the parser,
94 * and not exported.
95 *
96 * Dirty macros, i.e. one need to make assumption on the context to use them
97 *
Daniel Veillarddd6b3671999-09-23 22:19:22 +000098 * CUR_PTR return the current pointer to the xmlChar to be parsed.
99 * CUR returns the current xmlChar value, i.e. a 8 bit value if compiled
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000100 * in ISO-Latin or UTF-8, and the current 16 bit value if compiled
101 * in UNICODE mode. This should be used internally by the parser
102 * only to compare to ASCII values otherwise it would break when
103 * running with UTF-8 encoding.
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000104 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000105 * to compare on ASCII based substring.
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000106 * UPP(n) returns the n'th next xmlChar converted to uppercase. Same as CUR
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000107 * it should be used only to compare on ASCII based substring.
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000108 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000109 * strings within the parser.
110 *
111 * Clean macros, not dependent of an ASCII context, expect UTF-8 encoding
112 *
113 * CURRENT Returns the current char value, with the full decoding of
114 * UTF-8 if we are using this mode. It returns an int.
115 * NEXT Skip to the next character, this does the proper decoding
116 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000117 * COPY(to) copy one char to *to, increment CUR_PTR and to accordingly
118 */
119
120#define CUR (*ctxt->input->cur)
121#define UPPER (toupper(*ctxt->input->cur))
122#define SKIP(val) ctxt->input->cur += (val)
123#define NXT(val) ctxt->input->cur[(val)]
124#define UPP(val) (toupper(ctxt->input->cur[(val)]))
125#define CUR_PTR ctxt->input->cur
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000126#define SHRINK xmlParserInputShrink(ctxt->input)
127#define GROW xmlParserInputGrow(ctxt->input, INPUT_CHUNK)
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000128
129#define SKIP_BLANKS \
130 while (IS_BLANK(*(ctxt->input->cur))) NEXT
131
132#ifndef USE_UTF_8
133#define CURRENT (*ctxt->input->cur)
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000134#define NEXT { \
135 if ((*ctxt->input->cur == 0) && \
136 (xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0)) { \
137 xmlPopInput(ctxt); \
138 } else { \
139 if (*(ctxt->input->cur) == '\n') { \
140 ctxt->input->line++; ctxt->input->col = 1; \
141 } else ctxt->input->col++; \
142 ctxt->input->cur++; \
143 if (*ctxt->input->cur == 0) \
144 xmlParserInputGrow(ctxt->input, INPUT_CHUNK); \
145 }}
146
147/****************************************
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000148#define NEXT ((*ctxt->input->cur) ? \
149 (((*(ctxt->input->cur) == '\n') ? \
150 (ctxt->input->line++, ctxt->input->col = 1) : \
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000151 (ctxt->input->col++)), \
152 (ctxt->input->cur++), \
153 ((*ctxt->input->cur) ? \
154 (xmlParserInputGrow(ctxt->input, 100), \
155 ctxt->input->cur): \
156 (ctxt->input->cur))) : \
157 ((xmlParserInputGrow(ctxt->input, 100) > 0) ? \
158 ctxt->input->cur: \
159 (xmlPopInput(ctxt), ctxt->input->cur)))
160 ****************************************/
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000161#else
162#endif
163
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000164
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000165
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000166/************************************************************************
167 * *
168 * The list of HTML elements and their properties *
169 * *
170 ************************************************************************/
171
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000172/*
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000173 * Start Tag: 1 means the start tag can be ommited
174 * End Tag: 1 means the end tag can be ommited
175 * 2 means it's forbidden (empty elements)
176 * Depr: this element is deprecated
177 * DTD: 1 means that this element is valid only in the Loose DTD
178 * 2 means that this element is valid only in the Frameset DTD
179 *
180 * Name,Start Tag,End Tag, Empty, Depr., DTD, Description
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000181 */
182htmlElemDesc html40ElementTable[] = {
183{ "A", 0, 0, 0, 0, 0, "anchor " },
184{ "ABBR", 0, 0, 0, 0, 0, "abbreviated form" },
185{ "ACRONYM", 0, 0, 0, 0, 0, "" },
186{ "ADDRESS", 0, 0, 0, 0, 0, "information on author " },
187{ "APPLET", 0, 0, 0, 1, 1, "Java applet " },
188{ "AREA", 0, 2, 1, 0, 0, "client-side image map area " },
189{ "B", 0, 0, 0, 0, 0, "bold text style" },
190{ "BASE", 0, 2, 1, 0, 0, "document base URI " },
191{ "BASEFONT", 0, 2, 1, 1, 1, "base font size " },
192{ "BDO", 0, 0, 0, 0, 0, "I18N BiDi over-ride " },
193{ "BIG", 0, 0, 0, 0, 0, "large text style" },
194{ "BLOCKQUOTE", 0, 0, 0, 0, 0, "long quotation " },
195{ "BODY", 1, 1, 0, 0, 0, "document body " },
196{ "BR", 0, 2, 1, 0, 0, "forced line break " },
197{ "BUTTON", 0, 0, 0, 0, 0, "push button " },
198{ "CAPTION", 0, 0, 0, 0, 0, "table caption " },
199{ "CENTER", 0, 0, 0, 1, 1, "shorthand for DIV align=center " },
200{ "CITE", 0, 0, 0, 0, 0, "citation" },
201{ "CODE", 0, 0, 0, 0, 0, "computer code fragment" },
202{ "COL", 0, 2, 1, 0, 0, "table column " },
203{ "COLGROUP", 0, 1, 0, 0, 0, "table column group " },
204{ "DD", 0, 1, 0, 0, 0, "definition description " },
205{ "DEL", 0, 0, 0, 0, 0, "deleted text " },
206{ "DFN", 0, 0, 0, 0, 0, "instance definition" },
207{ "DIR", 0, 0, 0, 1, 1, "directory list" },
208{ "DIV", 0, 0, 0, 0, 0, "generic language/style container"},
209{ "DL", 0, 0, 0, 0, 0, "definition list " },
210{ "DT", 0, 1, 0, 0, 0, "definition term " },
211{ "EM", 0, 0, 0, 0, 0, "emphasis" },
212{ "FIELDSET", 0, 0, 0, 0, 0, "form control group " },
213{ "FONT", 0, 0, 0, 1, 1, "local change to font " },
214{ "FORM", 0, 0, 0, 0, 0, "interactive form " },
215{ "FRAME", 0, 2, 1, 0, 2, "subwindow " },
216{ "FRAMESET", 0, 0, 0, 0, 2, "window subdivision" },
217{ "H1", 0, 0, 0, 0, 0, "heading " },
218{ "H2", 0, 0, 0, 0, 0, "heading " },
219{ "H3", 0, 0, 0, 0, 0, "heading " },
220{ "H4", 0, 0, 0, 0, 0, "heading " },
221{ "H5", 0, 0, 0, 0, 0, "heading " },
222{ "H6", 0, 0, 0, 0, 0, "heading " },
223{ "HEAD", 1, 1, 0, 0, 0, "document head " },
224{ "HR", 0, 2, 1, 0, 0, "horizontal rule " },
225{ "HTML", 1, 1, 0, 0, 0, "document root element " },
226{ "I", 0, 0, 0, 0, 0, "italic text style" },
227{ "IFRAME", 0, 0, 0, 0, 1, "inline subwindow " },
228{ "IMG", 0, 2, 1, 0, 0, "Embedded image " },
229{ "INPUT", 0, 2, 1, 0, 0, "form control " },
230{ "INS", 0, 0, 0, 0, 0, "inserted text" },
231{ "ISINDEX", 0, 2, 1, 1, 1, "single line prompt " },
232{ "KBD", 0, 0, 0, 0, 0, "text to be entered by the user" },
233{ "LABEL", 0, 0, 0, 0, 0, "form field label text " },
234{ "LEGEND", 0, 0, 0, 0, 0, "fieldset legend " },
235{ "LI", 0, 1, 0, 0, 0, "list item " },
236{ "LINK", 0, 2, 1, 0, 0, "a media-independent link " },
237{ "MAP", 0, 0, 0, 0, 0, "client-side image map " },
238{ "MENU", 0, 0, 0, 1, 1, "menu list " },
239{ "META", 0, 2, 1, 0, 0, "generic metainformation " },
240{ "NOFRAMES", 0, 0, 0, 0, 2, "alternate content container for non frame-based rendering " },
241{ "NOSCRIPT", 0, 0, 0, 0, 0, "alternate content container for non script-based rendering " },
242{ "OBJECT", 0, 0, 0, 0, 0, "generic embedded object " },
243{ "OL", 0, 0, 0, 0, 0, "ordered list " },
244{ "OPTGROUP", 0, 0, 0, 0, 0, "option group " },
245{ "OPTION", 0, 1, 0, 0, 0, "selectable choice " },
246{ "P", 0, 1, 0, 0, 0, "paragraph " },
247{ "PARAM", 0, 2, 1, 0, 0, "named property value " },
248{ "PRE", 0, 0, 0, 0, 0, "preformatted text " },
249{ "Q", 0, 0, 0, 0, 0, "short inline quotation " },
250{ "S", 0, 0, 0, 1, 1, "strike-through text style" },
251{ "SAMP", 0, 0, 0, 0, 0, "sample program output, scripts, etc." },
252{ "SCRIPT", 0, 0, 0, 0, 0, "script statements " },
253{ "SELECT", 0, 0, 0, 0, 0, "option selector " },
254{ "SMALL", 0, 0, 0, 0, 0, "small text style" },
255{ "SPAN", 0, 0, 0, 0, 0, "generic language/style container " },
256{ "STRIKE", 0, 0, 0, 1, 1, "strike-through text" },
257{ "STRONG", 0, 0, 0, 0, 0, "strong emphasis" },
258{ "STYLE", 0, 0, 0, 0, 0, "style info " },
259{ "SUB", 0, 0, 0, 0, 0, "subscript" },
260{ "SUP", 0, 0, 0, 0, 0, "superscript " },
261{ "TABLE", 0, 0, 0, 0, 0, "&#160;" },
262{ "TBODY", 1, 1, 0, 0, 0, "table body " },
263{ "TD", 0, 1, 0, 0, 0, "table data cell" },
264{ "TEXTAREA", 0, 0, 0, 0, 0, "multi-line text field " },
265{ "TFOOT", 0, 1, 0, 0, 0, "table footer " },
266{ "TH", 0, 1, 0, 0, 0, "table header cell" },
267{ "THEAD", 0, 1, 0, 0, 0, "table header " },
268{ "TITLE", 0, 0, 0, 0, 0, "document title " },
269{ "TR", 0, 1, 0, 0, 0, "table row " },
270{ "TT", 0, 0, 0, 0, 0, "teletype or monospaced text style" },
271{ "U", 0, 0, 0, 1, 1, "underlined text style" },
272{ "UL", 0, 0, 0, 0, 0, "unordered list " },
273{ "VAR", 0, 0, 0, 0, 0, "instance of a variable or program argument" },
274};
275
276/*
277 * start tags that imply the end of a current element
278 * any tag of each line implies the end of the current element if the type of
279 * that element is in the same line
280 */
Daniel Veillardb96e6431999-08-29 21:02:19 +0000281char *htmlEquEnd[] = {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000282"DT", "DD", "LI", "OPTION", NULL,
283"H1", "H2", "H3", "H4", "H5", "H6", NULL,
284"OL", "MENU", "DIR", "ADDRESS", "PRE", "LISTING", "XMP", NULL,
285NULL
286};
287/*
288 * acording the HTML DTD, HR should be added to the 2nd line above, as it
289 * is not allowed within a H1, H2, H3, etc. But we should tolerate that case
290 * because many documents contain rules in headings...
291 */
292
293/*
294 * start tags that imply the end of current element
295 */
Daniel Veillardb96e6431999-08-29 21:02:19 +0000296char *htmlStartClose[] = {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000297"FORM", "FORM", "P", "HR", "H1", "H2", "H3", "H4", "H5", "H6",
298 "DL", "UL", "OL", "MENU", "DIR", "ADDRESS", "PRE",
299 "LISTING", "XMP", "HEAD", NULL,
300"HEAD", "P", NULL,
301"TITLE", "P", NULL,
302"BODY", "HEAD", "STYLE", "LINK", "TITLE", "P", NULL,
303"LI", "P", "H1", "H2", "H3", "H4", "H5", "H6", "DL", "ADDRESS",
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000304 "PRE", "LISTING", "XMP", "HEAD", "LI", NULL,
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000305"HR", "P", "HEAD", NULL,
306"H1", "P", "HEAD", NULL,
307"H2", "P", "HEAD", NULL,
308"H3", "P", "HEAD", NULL,
309"H4", "P", "HEAD", NULL,
310"H5", "P", "HEAD", NULL,
311"H6", "P", "HEAD", NULL,
312"DIR", "P", "HEAD", NULL,
313"ADDRESS", "P", "HEAD", "UL", NULL,
314"PRE", "P", "HEAD", "UL", NULL,
315"LISTING", "P", "HEAD", NULL,
316"XMP", "P", "HEAD", NULL,
317"BLOCKQUOTE", "P", "HEAD", NULL,
318"DL", "P", "DT", "MENU", "DIR", "ADDRESS", "PRE", "LISTING",
319 "XMP", "HEAD", NULL,
320"DT", "P", "MENU", "DIR", "ADDRESS", "PRE", "LISTING", "XMP", "HEAD", NULL,
321"DD", "P", "MENU", "DIR", "ADDRESS", "PRE", "LISTING", "XMP", "HEAD", NULL,
322"UL", "P", "HEAD", "OL", "MENU", "DIR", "ADDRESS", "PRE",
323 "LISTING", "XMP", NULL,
324"OL", "P", "HEAD", "UL", NULL,
325"MENU", "P", "HEAD", "UL", NULL,
326"P", "P", "HEAD", "H1", "H2", "H3", "H4", "H5", "H6", NULL,
327"DIV", "P", "HEAD", NULL,
328"NOSCRIPT", "P", "HEAD", NULL,
329"CENTER", "FONT", "B", "I", "P", "HEAD", NULL,
330"A", "A", NULL,
331"CAPTION", "P", NULL,
332"COLGROUP", "CAPTION", "COLGROUP", "COL", "P", NULL,
333"COL", "CAPTION", "COL", "P", NULL,
334"TABLE", "P", "HEAD", "H1", "H2", "H3", "H4", "H5", "H6", "PRE",
335 "LISTING", "XMP", "A", NULL,
336"TH", "TH", "TD", NULL,
337"TD", "TH", "TD", NULL,
338"TR", "TH", "TD", "TR", "CAPTION", "COL", "COLGROUP", NULL,
339"THEAD", "CAPTION", "COL", "COLGROUP", NULL,
340"TFOOT", "TH", "TD", "TR", "CAPTION", "COL", "COLGROUP", "THEAD",
341 "TBODY", NULL,
342"TBODY", "TH", "TD", "TR", "CAPTION", "COL", "COLGROUP", "THEAD",
343 "TFOOT", "TBODY", NULL,
344"OPTGROUP", "OPTION", NULL,
345"FIELDSET", "LEGEND", "P", "HEAD", "H1", "H2", "H3", "H4", "H5", "H6",
346 "PRE", "LISTING", "XMP", "A", NULL,
347NULL
348};
349
Daniel Veillardb96e6431999-08-29 21:02:19 +0000350static char** htmlStartCloseIndex[100];
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000351static int htmlStartCloseIndexinitialized = 0;
352
353/************************************************************************
354 * *
355 * functions to handle HTML specific data *
356 * *
357 ************************************************************************/
358
359/**
360 * htmlInitAutoClose:
361 *
362 * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
363 *
364 */
365void
366htmlInitAutoClose(void) {
367 int index, i = 0;
368
369 if (htmlStartCloseIndexinitialized) return;
370
371 for (index = 0;index < 100;index ++) htmlStartCloseIndex[index] = NULL;
372 index = 0;
373 while ((htmlStartClose[i] != NULL) && (index < 100 - 1)) {
374 htmlStartCloseIndex[index++] = &htmlStartClose[i];
375 while (htmlStartClose[i] != NULL) i++;
376 i++;
377 }
378}
379
380/**
381 * htmlTagLookup:
382 * @tag: The tag name
383 *
384 * Lookup the HTML tag in the ElementTable
385 *
386 * Returns the related htmlElemDescPtr or NULL if not found.
387 */
388htmlElemDescPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000389htmlTagLookup(const xmlChar *tag) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000390 int i = 0;
391
392 for (i = 0; i < (sizeof(html40ElementTable) /
393 sizeof(html40ElementTable[0]));i++) {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000394 if (!xmlStrcmp(tag, BAD_CAST html40ElementTable[i].name))
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000395 return(&html40ElementTable[i]);
396 }
397 return(NULL);
398}
399
400/**
401 * htmlCheckAutoClose:
402 * @new: The new tag name
403 * @old: The old tag name
404 *
405 * Checks wether the new tag is one of the registered valid tags for closing old.
406 * Initialize the htmlStartCloseIndex for fast lookup of closing tags names.
407 *
408 * Returns 0 if no, 1 if yes.
409 */
410int
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000411htmlCheckAutoClose(const xmlChar *new, const xmlChar *old) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000412 int i, index;
Daniel Veillardb96e6431999-08-29 21:02:19 +0000413 char **close;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000414
415 if (htmlStartCloseIndexinitialized == 0) htmlInitAutoClose();
416
417 /* inefficient, but not a big deal */
418 for (index = 0; index < 100;index++) {
419 close = htmlStartCloseIndex[index];
420 if (close == NULL) return(0);
Daniel Veillardb96e6431999-08-29 21:02:19 +0000421 if (!xmlStrcmp(BAD_CAST *close, new)) break;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000422 }
423
424 i = close - htmlStartClose;
425 i++;
426 while (htmlStartClose[i] != NULL) {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000427 if (!xmlStrcmp(BAD_CAST htmlStartClose[i], old)) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000428 return(1);
429 }
430 i++;
431 }
432 return(0);
433}
434
435/**
436 * htmlAutoClose:
437 * @ctxt: an HTML parser context
438 * @new: The new tag name
439 *
440 * The HTmL DtD allows a tag to implicitely close other tags.
441 * The list is kept in htmlStartClose array. This function is
442 * called when a new tag has been detected and generates the
443 * appropriates closes if possible/needed.
444 */
445void
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000446htmlAutoClose(htmlParserCtxtPtr ctxt, const xmlChar *new) {
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000447 xmlChar *oldname;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000448
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000449 while ((ctxt->name != NULL) &&
450 (htmlCheckAutoClose(new, ctxt->name))) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000451#ifdef DEBUG
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000452 printf("htmlAutoClose: %s closes %s\n", new, ctxt->name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000453#endif
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000454 if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000455 ctxt->sax->endElement(ctxt->userData, ctxt->name);
456 oldname = ctxt->name;
457 htmlnamePop(ctxt);
458 xmlFree(oldname);
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000459 }
460}
461
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000462/**
463 * htmlAutoCloseOnClose:
464 * @ctxt: an HTML parser context
465 * @new: The new tag name
466 *
467 * The HTmL DtD allows an ending tag to implicitely close other tags.
468 */
469void
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000470htmlAutoCloseOnClose(htmlParserCtxtPtr ctxt, const xmlChar *new) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000471 htmlElemDescPtr info;
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000472 xmlChar *oldname;
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000473
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000474 while ((ctxt->name != NULL) &&
475 (xmlStrcmp(new, ctxt->name))) {
476 info = htmlTagLookup(ctxt->name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000477 if ((info == NULL) || (info->endTag == 1)) {
478#ifdef DEBUG
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000479 printf("htmlAutoCloseOnClose: %s closes %s\n", new, ctxt->name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000480#endif
481 if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
Daniel Veillard2673d3c1999-10-08 14:37:09 +0000482 ctxt->sax->endElement(ctxt->userData, ctxt->name);
483 oldname = ctxt->name;
484 htmlnamePop(ctxt);
485 xmlFree(oldname);
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000486 } else
487 break;
488 }
489}
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000490
491/************************************************************************
492 * *
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000493 * The list of HTML predefined entities *
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000494 * *
495 ************************************************************************/
496
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000497
498htmlEntityDesc html40EntitiesTable[] = {
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000499/*
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000500 * the 4 absolute ones,
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000501 */
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000502{ 34, "quot", "quotation mark = APL quote, U+0022 ISOnum" },
503{ 38, "amp", "ampersand, U+0026 ISOnum" },
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000504{ 39, "apos", "single quote" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000505{ 60, "lt", "less-than sign, U+003C ISOnum" },
506{ 62, "gt", "greater-than sign, U+003E ISOnum" },
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000507
508/*
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000509 * A bunch still in the 128-255 range
510 * Replacing them depend really on the charset used.
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000511 */
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000512{ 160, "nbsp", "no-break space = non-breaking space, U+00A0 ISOnum" },
513{ 161, "iexcl","inverted exclamation mark, U+00A1 ISOnum" },
514{ 162, "cent", "cent sign, U+00A2 ISOnum" },
515{ 163, "pound","pound sign, U+00A3 ISOnum" },
516{ 164, "curren","currency sign, U+00A4 ISOnum" },
517{ 165, "yen", "yen sign = yuan sign, U+00A5 ISOnum" },
518{ 166, "brvbar","broken bar = broken vertical bar, U+00A6 ISOnum" },
519{ 167, "sect", "section sign, U+00A7 ISOnum" },
520{ 168, "uml", "diaeresis = spacing diaeresis, U+00A8 ISOdia" },
521{ 169, "copy", "copyright sign, U+00A9 ISOnum" },
522{ 170, "ordf", "feminine ordinal indicator, U+00AA ISOnum" },
523{ 171, "laquo","left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum" },
524{ 172, "not", "not sign, U+00AC ISOnum" },
525{ 173, "shy", "soft hyphen = discretionary hyphen, U+00AD ISOnum" },
526{ 174, "reg", "registered sign = registered trade mark sign, U+00AE ISOnum" },
527{ 175, "macr", "macron = spacing macron = overline = APL overbar, U+00AF ISOdia" },
528{ 176, "deg", "degree sign, U+00B0 ISOnum" },
529{ 177, "plusmn","plus-minus sign = plus-or-minus sign, U+00B1 ISOnum" },
530{ 178, "sup2", "superscript two = superscript digit two = squared, U+00B2 ISOnum" },
531{ 179, "sup3", "superscript three = superscript digit three = cubed, U+00B3 ISOnum" },
532{ 180, "acute","acute accent = spacing acute, U+00B4 ISOdia" },
533{ 181, "micro","micro sign, U+00B5 ISOnum" },
534{ 182, "para", "pilcrow sign = paragraph sign, U+00B6 ISOnum" },
Daniel Veillardb05deb71999-08-10 19:04:08 +0000535{ 183, "middot","middle dot = Georgian comma Greek middle dot, U+00B7 ISOnum" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000536{ 184, "cedil","cedilla = spacing cedilla, U+00B8 ISOdia" },
537{ 185, "sup1", "superscript one = superscript digit one, U+00B9 ISOnum" },
538{ 186, "ordm", "masculine ordinal indicator, U+00BA ISOnum" },
Daniel Veillardb05deb71999-08-10 19:04:08 +0000539{ 187, "raquo","right-pointing double angle quotation mark right pointing guillemet, U+00BB ISOnum" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000540{ 188, "frac14","vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum" },
541{ 189, "frac12","vulgar fraction one half = fraction one half, U+00BD ISOnum" },
542{ 190, "frac34","vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum" },
543{ 191, "iquest","inverted question mark = turned question mark, U+00BF ISOnum" },
544{ 192, "Agrave","latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1" },
545{ 193, "Aacute","latin capital letter A with acute, U+00C1 ISOlat1" },
546{ 194, "Acirc","latin capital letter A with circumflex, U+00C2 ISOlat1" },
547{ 195, "Atilde","latin capital letter A with tilde, U+00C3 ISOlat1" },
548{ 196, "Auml", "latin capital letter A with diaeresis, U+00C4 ISOlat1" },
549{ 197, "Aring","latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1" },
550{ 198, "AElig","latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1" },
551{ 199, "Ccedil","latin capital letter C with cedilla, U+00C7 ISOlat1" },
552{ 200, "Egrave","latin capital letter E with grave, U+00C8 ISOlat1" },
553{ 201, "Eacute","latin capital letter E with acute, U+00C9 ISOlat1" },
554{ 202, "Ecirc","latin capital letter E with circumflex, U+00CA ISOlat1" },
555{ 203, "Euml", "latin capital letter E with diaeresis, U+00CB ISOlat1" },
556{ 204, "Igrave","latin capital letter I with grave, U+00CC ISOlat1" },
557{ 205, "Iacute","latin capital letter I with acute, U+00CD ISOlat1" },
558{ 206, "Icirc","latin capital letter I with circumflex, U+00CE ISOlat1" },
559{ 207, "Iuml", "latin capital letter I with diaeresis, U+00CF ISOlat1" },
560{ 208, "ETH", "latin capital letter ETH, U+00D0 ISOlat1" },
561{ 209, "Ntilde","latin capital letter N with tilde, U+00D1 ISOlat1" },
562{ 210, "Ograve","latin capital letter O with grave, U+00D2 ISOlat1" },
563{ 211, "Oacute","latin capital letter O with acute, U+00D3 ISOlat1" },
564{ 212, "Ocirc","latin capital letter O with circumflex, U+00D4 ISOlat1" },
565{ 213, "Otilde","latin capital letter O with tilde, U+00D5 ISOlat1" },
566{ 214, "Ouml", "latin capital letter O with diaeresis, U+00D6 ISOlat1" },
567{ 215, "times","multiplication sign, U+00D7 ISOnum" },
Daniel Veillardb05deb71999-08-10 19:04:08 +0000568{ 216, "Oslash","latin capital letter O with stroke latin capital letter O slash, U+00D8 ISOlat1" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000569{ 217, "Ugrave","latin capital letter U with grave, U+00D9 ISOlat1" },
570{ 218, "Uacute","latin capital letter U with acute, U+00DA ISOlat1" },
571{ 219, "Ucirc","latin capital letter U with circumflex, U+00DB ISOlat1" },
572{ 220, "Uuml", "latin capital letter U with diaeresis, U+00DC ISOlat1" },
573{ 221, "Yacute","latin capital letter Y with acute, U+00DD ISOlat1" },
574{ 222, "THORN","latin capital letter THORN, U+00DE ISOlat1" },
575{ 223, "szlig","latin small letter sharp s = ess-zed, U+00DF ISOlat1" },
576{ 224, "agrave","latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1" },
577{ 225, "aacute","latin small letter a with acute, U+00E1 ISOlat1" },
578{ 226, "acirc","latin small letter a with circumflex, U+00E2 ISOlat1" },
579{ 227, "atilde","latin small letter a with tilde, U+00E3 ISOlat1" },
580{ 228, "auml", "latin small letter a with diaeresis, U+00E4 ISOlat1" },
581{ 229, "aring","latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1" },
582{ 230, "aelig","latin small letter ae = latin small ligature ae, U+00E6 ISOlat1" },
583{ 231, "ccedil","latin small letter c with cedilla, U+00E7 ISOlat1" },
584{ 232, "egrave","latin small letter e with grave, U+00E8 ISOlat1" },
585{ 233, "eacute","latin small letter e with acute, U+00E9 ISOlat1" },
586{ 234, "ecirc","latin small letter e with circumflex, U+00EA ISOlat1" },
587{ 235, "euml", "latin small letter e with diaeresis, U+00EB ISOlat1" },
588{ 236, "igrave","latin small letter i with grave, U+00EC ISOlat1" },
589{ 237, "iacute","latin small letter i with acute, U+00ED ISOlat1" },
590{ 238, "icirc","latin small letter i with circumflex, U+00EE ISOlat1" },
591{ 239, "iuml", "latin small letter i with diaeresis, U+00EF ISOlat1" },
592{ 240, "eth", "latin small letter eth, U+00F0 ISOlat1" },
593{ 241, "ntilde","latin small letter n with tilde, U+00F1 ISOlat1" },
594{ 242, "ograve","latin small letter o with grave, U+00F2 ISOlat1" },
595{ 243, "oacute","latin small letter o with acute, U+00F3 ISOlat1" },
596{ 244, "ocirc","latin small letter o with circumflex, U+00F4 ISOlat1" },
597{ 245, "otilde","latin small letter o with tilde, U+00F5 ISOlat1" },
598{ 246, "ouml", "latin small letter o with diaeresis, U+00F6 ISOlat1" },
599{ 247, "divide","division sign, U+00F7 ISOnum" },
600{ 248, "oslash","latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1" },
601{ 249, "ugrave","latin small letter u with grave, U+00F9 ISOlat1" },
602{ 250, "uacute","latin small letter u with acute, U+00FA ISOlat1" },
603{ 251, "ucirc","latin small letter u with circumflex, U+00FB ISOlat1" },
604{ 252, "uuml", "latin small letter u with diaeresis, U+00FC ISOlat1" },
605{ 253, "yacute","latin small letter y with acute, U+00FD ISOlat1" },
606{ 254, "thorn","latin small letter thorn with, U+00FE ISOlat1" },
607{ 255, "yuml", "latin small letter y with diaeresis, U+00FF ISOlat1" },
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000608
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000609/*
610 * Anything below should really be kept as entities references
611 */
612{ 402, "fnof", "latin small f with hook = function = florin, U+0192 ISOtech" },
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000613
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000614{ 913, "Alpha","greek capital letter alpha, U+0391" },
615{ 914, "Beta", "greek capital letter beta, U+0392" },
616{ 915, "Gamma","greek capital letter gamma, U+0393 ISOgrk3" },
617{ 916, "Delta","greek capital letter delta, U+0394 ISOgrk3" },
618{ 917, "Epsilon","greek capital letter epsilon, U+0395" },
619{ 918, "Zeta", "greek capital letter zeta, U+0396" },
620{ 919, "Eta", "greek capital letter eta, U+0397" },
621{ 920, "Theta","greek capital letter theta, U+0398 ISOgrk3" },
622{ 921, "Iota", "greek capital letter iota, U+0399" },
623{ 922, "Kappa","greek capital letter kappa, U+039A" },
624{ 923, "Lambda""greek capital letter lambda, U+039B ISOgrk3" },
625{ 924, "Mu", "greek capital letter mu, U+039C" },
626{ 925, "Nu", "greek capital letter nu, U+039D" },
627{ 926, "Xi", "greek capital letter xi, U+039E ISOgrk3" },
628{ 927, "Omicron","greek capital letter omicron, U+039F" },
629{ 928, "Pi", "greek capital letter pi, U+03A0 ISOgrk3" },
630{ 929, "Rho", "greek capital letter rho, U+03A1" },
631{ 931, "Sigma","greek capital letter sigma, U+03A3 ISOgrk3" },
632{ 932, "Tau", "greek capital letter tau, U+03A4" },
633{ 933, "Upsilon","greek capital letter upsilon, U+03A5 ISOgrk3" },
634{ 934, "Phi", "greek capital letter phi, U+03A6 ISOgrk3" },
635{ 935, "Chi", "greek capital letter chi, U+03A7" },
636{ 936, "Psi", "greek capital letter psi, U+03A8 ISOgrk3" },
637{ 937, "Omega","greek capital letter omega, U+03A9 ISOgrk3" },
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000638
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000639{ 945, "alpha","greek small letter alpha, U+03B1 ISOgrk3" },
640{ 946, "beta", "greek small letter beta, U+03B2 ISOgrk3" },
641{ 947, "gamma","greek small letter gamma, U+03B3 ISOgrk3" },
642{ 948, "delta","greek small letter delta, U+03B4 ISOgrk3" },
643{ 949, "epsilon","greek small letter epsilon, U+03B5 ISOgrk3" },
644{ 950, "zeta", "greek small letter zeta, U+03B6 ISOgrk3" },
645{ 951, "eta", "greek small letter eta, U+03B7 ISOgrk3" },
646{ 952, "theta","greek small letter theta, U+03B8 ISOgrk3" },
647{ 953, "iota", "greek small letter iota, U+03B9 ISOgrk3" },
648{ 954, "kappa","greek small letter kappa, U+03BA ISOgrk3" },
649{ 955, "lambda","greek small letter lambda, U+03BB ISOgrk3" },
650{ 956, "mu", "greek small letter mu, U+03BC ISOgrk3" },
651{ 957, "nu", "greek small letter nu, U+03BD ISOgrk3" },
652{ 958, "xi", "greek small letter xi, U+03BE ISOgrk3" },
653{ 959, "omicron","greek small letter omicron, U+03BF NEW" },
654{ 960, "pi", "greek small letter pi, U+03C0 ISOgrk3" },
655{ 961, "rho", "greek small letter rho, U+03C1 ISOgrk3" },
656{ 962, "sigmaf","greek small letter final sigma, U+03C2 ISOgrk3" },
657{ 963, "sigma","greek small letter sigma, U+03C3 ISOgrk3" },
658{ 964, "tau", "greek small letter tau, U+03C4 ISOgrk3" },
659{ 965, "upsilon","greek small letter upsilon, U+03C5 ISOgrk3" },
660{ 966, "phi", "greek small letter phi, U+03C6 ISOgrk3" },
661{ 967, "chi", "greek small letter chi, U+03C7 ISOgrk3" },
662{ 968, "psi", "greek small letter psi, U+03C8 ISOgrk3" },
663{ 969, "omega","greek small letter omega, U+03C9 ISOgrk3" },
664{ 977, "thetasym","greek small letter theta symbol, U+03D1 NEW" },
665{ 978, "upsih","greek upsilon with hook symbol, U+03D2 NEW" },
666{ 982, "piv", "greek pi symbol, U+03D6 ISOgrk3" },
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000667
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000668{ 8226, "bull", "bullet = black small circle, U+2022 ISOpub" },
669{ 8230, "hellip","horizontal ellipsis = three dot leader, U+2026 ISOpub" },
670{ 8242, "prime","prime = minutes = feet, U+2032 ISOtech" },
671{ 8243, "Prime","double prime = seconds = inches, U+2033 ISOtech" },
672{ 8254, "oline","overline = spacing overscore, U+203E NEW" },
673{ 8260, "frasl","fraction slash, U+2044 NEW" },
674
Daniel Veillardb05deb71999-08-10 19:04:08 +0000675{ 8472, "weierp","script capital P = power set = Weierstrass p, U+2118 ISOamso" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000676{ 8465, "image","blackletter capital I = imaginary part, U+2111 ISOamso" },
677{ 8476, "real", "blackletter capital R = real part symbol, U+211C ISOamso" },
678{ 8482, "trade","trade mark sign, U+2122 ISOnum" },
679{ 8501, "alefsym","alef symbol = first transfinite cardinal, U+2135 NEW" },
680{ 8592, "larr", "leftwards arrow, U+2190 ISOnum" },
681{ 8593, "uarr", "upwards arrow, U+2191 ISOnum" },
682{ 8594, "rarr", "rightwards arrow, U+2192 ISOnum" },
683{ 8595, "darr", "downwards arrow, U+2193 ISOnum" },
684{ 8596, "harr", "left right arrow, U+2194 ISOamsa" },
685{ 8629, "crarr","downwards arrow with corner leftwards = carriage return, U+21B5 NEW" },
686{ 8656, "lArr", "leftwards double arrow, U+21D0 ISOtech" },
687{ 8657, "uArr", "upwards double arrow, U+21D1 ISOamsa" },
688{ 8658, "rArr", "rightwards double arrow, U+21D2 ISOtech" },
689{ 8659, "dArr", "downwards double arrow, U+21D3 ISOamsa" },
690{ 8660, "hArr", "left right double arrow, U+21D4 ISOamsa" },
691
692
693{ 8704, "forall","for all, U+2200 ISOtech" },
694{ 8706, "part", "partial differential, U+2202 ISOtech" },
695{ 8707, "exist","there exists, U+2203 ISOtech" },
696{ 8709, "empty","empty set = null set = diameter, U+2205 ISOamso" },
697{ 8711, "nabla","nabla = backward difference, U+2207 ISOtech" },
698{ 8712, "isin", "element of, U+2208 ISOtech" },
699{ 8713, "notin","not an element of, U+2209 ISOtech" },
700{ 8715, "ni", "contains as member, U+220B ISOtech" },
701{ 8719, "prod", "n-ary product = product sign, U+220F ISOamsb" },
702{ 8721, "sum", "n-ary sumation, U+2211 ISOamsb" },
703{ 8722, "minus","minus sign, U+2212 ISOtech" },
704{ 8727, "lowast","asterisk operator, U+2217 ISOtech" },
705{ 8730, "radic","square root = radical sign, U+221A ISOtech" },
706{ 8733, "prop", "proportional to, U+221D ISOtech" },
707{ 8734, "infin","infinity, U+221E ISOtech" },
708{ 8736, "ang", "angle, U+2220 ISOamso" },
709{ 8743, "and", "logical and = wedge, U+2227 ISOtech" },
710{ 8744, "or", "logical or = vee, U+2228 ISOtech" },
711{ 8745, "cap", "intersection = cap, U+2229 ISOtech" },
712{ 8746, "cup", "union = cup, U+222A ISOtech" },
713{ 8747, "int", "integral, U+222B ISOtech" },
714{ 8756, "there4","therefore, U+2234 ISOtech" },
715{ 8764, "sim", "tilde operator = varies with = similar to, U+223C ISOtech" },
716{ 8773, "cong", "approximately equal to, U+2245 ISOtech" },
717{ 8776, "asymp","almost equal to = asymptotic to, U+2248 ISOamsr" },
718{ 8800, "ne", "not equal to, U+2260 ISOtech" },
719{ 8801, "equiv","identical to, U+2261 ISOtech" },
720{ 8804, "le", "less-than or equal to, U+2264 ISOtech" },
721{ 8805, "ge", "greater-than or equal to, U+2265 ISOtech" },
722{ 8834, "sub", "subset of, U+2282 ISOtech" },
723{ 8835, "sup", "superset of, U+2283 ISOtech" },
724{ 8836, "nsub", "not a subset of, U+2284 ISOamsn" },
725{ 8838, "sube", "subset of or equal to, U+2286 ISOtech" },
726{ 8839, "supe", "superset of or equal to, U+2287 ISOtech" },
727{ 8853, "oplus","circled plus = direct sum, U+2295 ISOamsb" },
728{ 8855, "otimes","circled times = vector product, U+2297 ISOamsb" },
729{ 8869, "perp", "up tack = orthogonal to = perpendicular, U+22A5 ISOtech" },
730{ 8901, "sdot", "dot operator, U+22C5 ISOamsb" },
731{ 8968, "lceil","left ceiling = apl upstile, U+2308 ISOamsc" },
732{ 8969, "rceil","right ceiling, U+2309 ISOamsc" },
733{ 8970, "lfloor","left floor = apl downstile, U+230A ISOamsc" },
734{ 8971, "rfloor","right floor, U+230B ISOamsc" },
735{ 9001, "lang", "left-pointing angle bracket = bra, U+2329 ISOtech" },
736{ 9002, "rang", "right-pointing angle bracket = ket, U+232A ISOtech" },
737{ 9674, "loz", "lozenge, U+25CA ISOpub" },
738
739{ 9824, "spades","black spade suit, U+2660 ISOpub" },
740{ 9827, "clubs","black club suit = shamrock, U+2663 ISOpub" },
741{ 9829, "hearts","black heart suit = valentine, U+2665 ISOpub" },
742{ 9830, "diams","black diamond suit, U+2666 ISOpub" },
743
744{ 338, "OElig","latin capital ligature OE, U+0152 ISOlat2" },
745{ 339, "oelig","latin small ligature oe, U+0153 ISOlat2" },
746{ 352, "Scaron","latin capital letter S with caron, U+0160 ISOlat2" },
747{ 353, "scaron","latin small letter s with caron, U+0161 ISOlat2" },
748{ 376, "Yuml", "latin capital letter Y with diaeresis, U+0178 ISOlat2" },
749{ 710, "circ", "modifier letter circumflex accent, U+02C6 ISOpub" },
750{ 732, "tilde","small tilde, U+02DC ISOdia" },
751
752{ 8194, "ensp", "en space, U+2002 ISOpub" },
753{ 8195, "emsp", "em space, U+2003 ISOpub" },
754{ 8201, "thinsp","thin space, U+2009 ISOpub" },
755{ 8204, "zwnj", "zero width non-joiner, U+200C NEW RFC 2070" },
756{ 8205, "zwj", "zero width joiner, U+200D NEW RFC 2070" },
757{ 8206, "lrm", "left-to-right mark, U+200E NEW RFC 2070" },
758{ 8207, "rlm", "right-to-left mark, U+200F NEW RFC 2070" },
759{ 8211, "ndash","en dash, U+2013 ISOpub" },
760{ 8212, "mdash","em dash, U+2014 ISOpub" },
761{ 8216, "lsquo","left single quotation mark, U+2018 ISOnum" },
762{ 8217, "rsquo","right single quotation mark, U+2019 ISOnum" },
763{ 8218, "sbquo","single low-9 quotation mark, U+201A NEW" },
764{ 8220, "ldquo","left double quotation mark, U+201C ISOnum" },
765{ 8221, "rdquo","right double quotation mark, U+201D ISOnum" },
766{ 8222, "bdquo","double low-9 quotation mark, U+201E NEW" },
767{ 8224, "dagger","dagger, U+2020 ISOpub" },
768{ 8225, "Dagger","double dagger, U+2021 ISOpub" },
769{ 8240, "permil","per mille sign, U+2030 ISOtech" },
770{ 8249, "lsaquo","single left-pointing angle quotation mark, U+2039 ISO proposed" },
Daniel Veillardb05deb71999-08-10 19:04:08 +0000771{ 8250, "rsaquo","single right-pointing angle quotation mark, U+203A ISO proposed" },
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000772{ 8364, "euro", "euro sign, U+20AC NEW" }
773};
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000774
775/************************************************************************
776 * *
777 * Commodity functions to handle entities *
778 * *
779 ************************************************************************/
780
781/*
782 * Macro used to grow the current buffer.
783 */
784#define growBuffer(buffer) { \
785 buffer##_size *= 2; \
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000786 buffer = (xmlChar *) xmlRealloc(buffer, buffer##_size * sizeof(xmlChar)); \
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000787 if (buffer == NULL) { \
788 perror("realloc failed"); \
789 exit(1); \
790 } \
791}
792
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000793/**
794 * htmlEntityLookup:
795 * @name: the entity name
796 *
797 * Lookup the given entity in EntitiesTable
798 *
799 * TODO: the linear scan is really ugly, an hash table is really needed.
800 *
801 * Returns the associated htmlEntityDescPtr if found, NULL otherwise.
802 */
803htmlEntityDescPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000804htmlEntityLookup(const xmlChar *name) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000805 int i;
806
807 for (i = 0;i < (sizeof(html40EntitiesTable)/
808 sizeof(html40EntitiesTable[0]));i++) {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000809 if (!xmlStrcmp(name, BAD_CAST html40EntitiesTable[i].name)) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000810#ifdef DEBUG
811 printf("Found entity %s\n", name);
812#endif
813 return(&html40EntitiesTable[i]);
814 }
815 }
816 return(NULL);
817}
818
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000819
820/**
821 * htmlDecodeEntities:
822 * @ctxt: the parser context
823 * @len: the len to decode (in bytes !), -1 for no size limit
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000824 * @end: an end marker xmlChar, 0 if none
825 * @end2: an end marker xmlChar, 0 if none
826 * @end3: an end marker xmlChar, 0 if none
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000827 *
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000828 * Subtitute the HTML entities by their value
829 *
830 * TODO: once the internal representation will be UTF-8, all entities
831 * will be substituable, in the meantime we only apply the substitution
832 * to the one with values in the 0-255 UNICODE range
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000833 *
834 * Returns A newly allocated string with the substitution done. The caller
835 * must deallocate it !
836 */
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000837xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000838htmlDecodeEntities(htmlParserCtxtPtr ctxt, int len,
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000839 xmlChar end, xmlChar end2, xmlChar end3) {
840 xmlChar *buffer = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000841 int buffer_size = 0;
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000842 xmlChar *out = NULL;
843 xmlChar *name = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000844
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000845 xmlChar *cur = NULL;
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000846 htmlEntityDescPtr ent;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000847 int nbchars = 0;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000848 unsigned int max = (unsigned int) len;
849
850 /*
851 * allocate a translation buffer.
852 */
853 buffer_size = 1000;
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000854 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000855 if (buffer == NULL) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000856 perror("htmlDecodeEntities: malloc failed");
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000857 return(NULL);
858 }
859 out = buffer;
860
861 /*
862 * Ok loop until we reach one of the ending char or a size limit.
863 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000864 while ((nbchars < max) && (CUR != end) &&
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000865 (CUR != end2) && (CUR != end3)) {
866
867 if (CUR == '&') {
868 if (NXT(1) == '#') {
869 int val = htmlParseCharRef(ctxt);
Daniel Veillardb96e6431999-08-29 21:02:19 +0000870 /* invalid for UTF-8 variable encoding !!!!! */
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000871 *out++ = val;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000872 nbchars += 3; /* !!!! */
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000873 } else {
Daniel Veillard5233ffc1999-07-06 22:25:25 +0000874 ent = htmlParseEntityRef(ctxt, &name);
875 if (name != NULL) {
876 if ((ent == NULL) || (ent->value <= 0) ||
877 (ent->value >= 255)) {
878 *out++ = '&';
879 cur = name;
880 while (*cur != 0) {
881 if (out - buffer > buffer_size - 100) {
882 int index = out - buffer;
883
884 growBuffer(buffer);
885 out = &buffer[index];
886 }
887 *out++ = *cur++;
888 }
889 *out++ = ';';
890 } else {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000891 /* invalid for UTF-8 variable encoding !!!!! */
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000892 *out++ = (xmlChar)ent->value;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000893 if (out - buffer > buffer_size - 100) {
894 int index = out - buffer;
895
896 growBuffer(buffer);
897 out = &buffer[index];
898 }
899 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000900 nbchars += 2 + xmlStrlen(name);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000901 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000902 }
903 }
904 } else {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000905 /* invalid for UTF-8 , use COPY(out); !!!!! */
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000906 *out++ = CUR;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000907 nbchars++;
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000908 if (out - buffer > buffer_size - 100) {
909 int index = out - buffer;
910
911 growBuffer(buffer);
912 out = &buffer[index];
913 }
914 NEXT;
915 }
916 }
917 *out++ = 0;
918 return(buffer);
919}
920
921
922/************************************************************************
923 * *
924 * Commodity functions to handle encodings *
925 * *
926 ************************************************************************/
927
928/**
929 * htmlSwitchEncoding:
930 * @ctxt: the parser context
931 * @len: the len of @cur
932 *
933 * change the input functions when discovering the character encoding
934 * of a given entity.
935 *
936 */
937void
938htmlSwitchEncoding(htmlParserCtxtPtr ctxt, xmlCharEncoding enc)
939{
940 switch (enc) {
941 case XML_CHAR_ENCODING_ERROR:
942 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
943 ctxt->sax->error(ctxt->userData, "encoding unknown\n");
944 ctxt->wellFormed = 0;
945 break;
946 case XML_CHAR_ENCODING_NONE:
947 /* let's assume it's UTF-8 without the XML decl */
948 return;
949 case XML_CHAR_ENCODING_UTF8:
950 /* default encoding, no conversion should be needed */
951 return;
952 case XML_CHAR_ENCODING_UTF16LE:
953 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
954 ctxt->sax->error(ctxt->userData,
955 "char encoding UTF16 little endian not supported\n");
956 break;
957 case XML_CHAR_ENCODING_UTF16BE:
958 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
959 ctxt->sax->error(ctxt->userData,
960 "char encoding UTF16 big endian not supported\n");
961 break;
962 case XML_CHAR_ENCODING_UCS4LE:
963 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
964 ctxt->sax->error(ctxt->userData,
965 "char encoding USC4 little endian not supported\n");
966 break;
967 case XML_CHAR_ENCODING_UCS4BE:
968 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
969 ctxt->sax->error(ctxt->userData,
970 "char encoding USC4 big endian not supported\n");
971 break;
972 case XML_CHAR_ENCODING_EBCDIC:
973 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
974 ctxt->sax->error(ctxt->userData,
975 "char encoding EBCDIC not supported\n");
976 break;
977 case XML_CHAR_ENCODING_UCS4_2143:
978 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
979 ctxt->sax->error(ctxt->userData,
980 "char encoding UCS4 2143 not supported\n");
981 break;
982 case XML_CHAR_ENCODING_UCS4_3412:
983 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
984 ctxt->sax->error(ctxt->userData,
985 "char encoding UCS4 3412 not supported\n");
986 break;
987 case XML_CHAR_ENCODING_UCS2:
988 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
989 ctxt->sax->error(ctxt->userData,
990 "char encoding UCS2 not supported\n");
991 break;
992 case XML_CHAR_ENCODING_8859_1:
993 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
994 ctxt->sax->error(ctxt->userData,
995 "char encoding ISO_8859_1 ISO Latin 1 not supported\n");
996 break;
997 case XML_CHAR_ENCODING_8859_2:
998 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
999 ctxt->sax->error(ctxt->userData,
1000 "char encoding ISO_8859_2 ISO Latin 2 not supported\n");
1001 break;
1002 case XML_CHAR_ENCODING_8859_3:
1003 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1004 ctxt->sax->error(ctxt->userData,
1005 "char encoding ISO_8859_3 not supported\n");
1006 break;
1007 case XML_CHAR_ENCODING_8859_4:
1008 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1009 ctxt->sax->error(ctxt->userData,
1010 "char encoding ISO_8859_4 not supported\n");
1011 break;
1012 case XML_CHAR_ENCODING_8859_5:
1013 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1014 ctxt->sax->error(ctxt->userData,
1015 "char encoding ISO_8859_5 not supported\n");
1016 break;
1017 case XML_CHAR_ENCODING_8859_6:
1018 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1019 ctxt->sax->error(ctxt->userData,
1020 "char encoding ISO_8859_6 not supported\n");
1021 break;
1022 case XML_CHAR_ENCODING_8859_7:
1023 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1024 ctxt->sax->error(ctxt->userData,
1025 "char encoding ISO_8859_7 not supported\n");
1026 break;
1027 case XML_CHAR_ENCODING_8859_8:
1028 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1029 ctxt->sax->error(ctxt->userData,
1030 "char encoding ISO_8859_8 not supported\n");
1031 break;
1032 case XML_CHAR_ENCODING_8859_9:
1033 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1034 ctxt->sax->error(ctxt->userData,
1035 "char encoding ISO_8859_9 not supported\n");
1036 break;
1037 case XML_CHAR_ENCODING_2022_JP:
1038 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1039 ctxt->sax->error(ctxt->userData,
1040 "char encoding ISO-2022-JPnot supported\n");
1041 break;
1042 case XML_CHAR_ENCODING_SHIFT_JIS:
1043 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1044 ctxt->sax->error(ctxt->userData,
1045 "char encoding Shift_JISnot supported\n");
1046 break;
1047 case XML_CHAR_ENCODING_EUC_JP:
1048 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1049 ctxt->sax->error(ctxt->userData,
1050 "char encoding EUC-JPnot supported\n");
1051 break;
1052 }
1053}
1054
1055
1056/************************************************************************
1057 * *
1058 * Commodity functions, cleanup needed ? *
1059 * *
1060 ************************************************************************/
1061
1062/**
1063 * areBlanks:
1064 * @ctxt: an HTML parser context
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001065 * @str: a xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001066 * @len: the size of @str
1067 *
1068 * Is this a sequence of blank chars that one can ignore ?
1069 *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001070 * Returns 1 if ignorable 0 otherwise.
1071 */
1072
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001073static int areBlanks(htmlParserCtxtPtr ctxt, const xmlChar *str, int len) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001074 int i;
1075 xmlNodePtr lastChild;
1076
1077 for (i = 0;i < len;i++)
1078 if (!(IS_BLANK(str[i]))) return(0);
1079
1080 if (CUR != '<') return(0);
1081 if (ctxt->node == NULL) return(0);
1082 lastChild = xmlGetLastChild(ctxt->node);
1083 if (lastChild == NULL) {
1084 if (ctxt->node->content != NULL) return(0);
1085 } else if (xmlNodeIsText(lastChild))
1086 return(0);
1087 return(1);
1088}
1089
1090/**
1091 * htmlHandleEntity:
1092 * @ctxt: an HTML parser context
1093 * @entity: an XML entity pointer.
1094 *
1095 * Default handling of an HTML entity, call the parser with the
1096 * substitution string
1097 */
1098
1099void
1100htmlHandleEntity(htmlParserCtxtPtr ctxt, xmlEntityPtr entity) {
1101 int len;
1102
1103 if (entity->content == NULL) {
1104 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1105 ctxt->sax->error(ctxt->userData, "htmlHandleEntity %s: content == NULL\n",
1106 entity->name);
1107 ctxt->wellFormed = 0;
1108 return;
1109 }
1110 len = xmlStrlen(entity->content);
1111
1112 /*
1113 * Just handle the content as a set of chars.
1114 */
1115 if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
1116 ctxt->sax->characters(ctxt->userData, entity->content, len);
1117
1118}
1119
1120/**
1121 * htmlNewDoc:
1122 * @URI: URI for the dtd, or NULL
1123 * @ExternalID: the external ID of the DTD, or NULL
1124 *
1125 * Returns a new document
1126 */
1127htmlDocPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001128htmlNewDoc(const xmlChar *URI, const xmlChar *ExternalID) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001129 xmlDocPtr cur;
1130
1131 /*
1132 * Allocate a new document and fill the fields.
1133 */
Daniel Veillard6454aec1999-09-02 22:04:43 +00001134 cur = (xmlDocPtr) xmlMalloc(sizeof(xmlDoc));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001135 if (cur == NULL) {
1136 fprintf(stderr, "xmlNewDoc : malloc failed\n");
1137 return(NULL);
1138 }
Daniel Veillarde7a5a771999-08-30 13:05:42 +00001139 memset(cur, 0, sizeof(xmlDoc));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001140
1141 cur->type = XML_DOCUMENT_NODE;
1142 cur->version = NULL;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001143 cur->intSubset = NULL;
Daniel Veillardb96e6431999-08-29 21:02:19 +00001144 xmlCreateIntSubset(cur, BAD_CAST "HTML", ExternalID, URI);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001145 cur->name = NULL;
1146 cur->root = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001147 cur->extSubset = NULL;
1148 cur->oldNs = NULL;
1149 cur->encoding = NULL;
1150 cur->standalone = 1;
1151 cur->compression = 0;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001152 cur->ids = NULL;
1153 cur->refs = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001154#ifndef XML_WITHOUT_CORBA
1155 cur->_private = NULL;
1156 cur->vepv = NULL;
1157#endif
1158 return(cur);
1159}
1160
1161
1162/************************************************************************
1163 * *
1164 * The parser itself *
1165 * Relates to http://www.w3.org/TR/html40 *
1166 * *
1167 ************************************************************************/
1168
1169/************************************************************************
1170 * *
1171 * The parser itself *
1172 * *
1173 ************************************************************************/
1174
1175/**
1176 * htmlParseHTMLName:
1177 * @ctxt: an HTML parser context
1178 *
1179 * parse an HTML tag or attribute name, note that we convert it to uppercase
1180 * since HTML names are not case-sensitive.
1181 *
1182 * Returns the Tag Name parsed or NULL
1183 */
1184
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001185xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001186htmlParseHTMLName(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001187 xmlChar *ret = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001188 int i = 0;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001189 xmlChar loc[100];
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001190
1191 if (!IS_LETTER(CUR) && (CUR != '_') &&
1192 (CUR != ':')) return(NULL);
1193
1194 while ((i < 100) && ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)))) {
1195 if ((CUR >= 0x61) && (CUR <= 0x7a)) loc[i] = CUR - 0x20;
1196 else loc[i] = CUR;
1197 i++;
1198
1199 NEXT;
1200 }
1201
1202 ret = xmlStrndup(loc, i);
1203
1204 return(ret);
1205}
1206
1207/**
1208 * htmlParseName:
1209 * @ctxt: an HTML parser context
1210 *
1211 * parse an HTML name, this routine is case sensistive.
1212 *
1213 * Returns the Name parsed or NULL
1214 */
1215
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001216xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001217htmlParseName(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001218 xmlChar buf[HTML_MAX_NAMELEN];
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001219 int len = 0;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001220
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001221 GROW;
1222 if (!IS_LETTER(CUR) && (CUR != '_')) {
1223 return(NULL);
1224 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001225
1226 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
1227 (CUR == '.') || (CUR == '-') ||
1228 (CUR == '_') || (CUR == ':') ||
1229 (IS_COMBINING(CUR)) ||
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001230 (IS_EXTENDER(CUR))) {
1231 buf[len++] = CUR;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001232 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001233 if (len >= HTML_MAX_NAMELEN) {
1234 fprintf(stderr,
1235 "htmlParseName: reached HTML_MAX_NAMELEN limit\n");
1236 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
1237 (CUR == '.') || (CUR == '-') ||
1238 (CUR == '_') || (CUR == ':') ||
1239 (IS_COMBINING(CUR)) ||
1240 (IS_EXTENDER(CUR)))
1241 NEXT;
1242 break;
1243 }
1244 }
1245 return(xmlStrndup(buf, len));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001246}
1247
1248/**
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001249 * htmlParseHTMLAttribute:
1250 * @ctxt: an HTML parser context
1251 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001252 * parse an HTML attribute value (without quotes).
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001253 *
1254 * Returns the Nmtoken parsed or NULL
1255 */
1256
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001257xmlChar *
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001258htmlParseHTMLAttribute(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001259 xmlChar buf[HTML_MAX_NAMELEN];
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001260 int len = 0;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001261
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001262 GROW;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001263 while ((!IS_BLANK(CUR)) && (CUR != '<') &&
1264 (CUR != '&') && (CUR != '>') &&
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001265 (CUR != '\'') && (CUR != '"')) {
1266 buf[len++] = CUR;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001267 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001268 if (len >= HTML_MAX_NAMELEN) {
1269 fprintf(stderr,
1270 "htmlParseHTMLAttribute: reached HTML_MAX_NAMELEN limit\n");
1271 while ((!IS_BLANK(CUR)) && (CUR != '<') &&
1272 (CUR != '&') && (CUR != '>') &&
1273 (CUR != '\'') && (CUR != '"'))
1274 NEXT;
1275 break;
1276 }
1277 }
1278 return(xmlStrndup(buf, len));
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001279}
1280
1281/**
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001282 * htmlParseNmtoken:
1283 * @ctxt: an HTML parser context
1284 *
1285 * parse an HTML Nmtoken.
1286 *
1287 * Returns the Nmtoken parsed or NULL
1288 */
1289
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001290xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001291htmlParseNmtoken(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001292 xmlChar buf[HTML_MAX_NAMELEN];
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001293 int len = 0;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001294
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001295 GROW;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001296 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
1297 (CUR == '.') || (CUR == '-') ||
1298 (CUR == '_') || (CUR == ':') ||
1299 (IS_COMBINING(CUR)) ||
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001300 (IS_EXTENDER(CUR))) {
1301 buf[len++] = CUR;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001302 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001303 if (len >= HTML_MAX_NAMELEN) {
1304 fprintf(stderr,
1305 "htmlParseNmtoken: reached HTML_MAX_NAMELEN limit\n");
1306 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
1307 (CUR == '.') || (CUR == '-') ||
1308 (CUR == '_') || (CUR == ':') ||
1309 (IS_COMBINING(CUR)) ||
1310 (IS_EXTENDER(CUR)))
1311 NEXT;
1312 break;
1313 }
1314 }
1315 return(xmlStrndup(buf, len));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001316}
1317
1318/**
1319 * htmlParseEntityRef:
1320 * @ctxt: an HTML parser context
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001321 * @str: location to store the entity name
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001322 *
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001323 * parse an HTML ENTITY references
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001324 *
1325 * [68] EntityRef ::= '&' Name ';'
1326 *
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001327 * Returns the associated htmlEntityDescPtr if found, or NULL otherwise,
1328 * if non-NULL *str will have to be freed by the caller.
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001329 */
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001330htmlEntityDescPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001331htmlParseEntityRef(htmlParserCtxtPtr ctxt, xmlChar **str) {
1332 xmlChar *name;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001333 htmlEntityDescPtr ent = NULL;
1334 *str = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001335
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001336 if (CUR == '&') {
1337 NEXT;
1338 name = htmlParseName(ctxt);
1339 if (name == NULL) {
1340 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1341 ctxt->sax->error(ctxt->userData, "htmlParseEntityRef: no name\n");
1342 ctxt->wellFormed = 0;
1343 } else {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001344 GROW;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001345 if (CUR == ';') {
1346 NEXT;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001347 *str = name;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001348
1349 /*
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001350 * Lookup the entity in the table.
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001351 */
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001352 ent = htmlEntityLookup(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001353 } else {
1354 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1355 ctxt->sax->error(ctxt->userData,
1356 "htmlParseEntityRef: expecting ';'\n");
1357 ctxt->wellFormed = 0;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001358 if (ctxt->sax->characters != NULL) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00001359 ctxt->sax->characters(ctxt->userData, BAD_CAST "&", 1);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001360 ctxt->sax->characters(ctxt->userData, name, xmlStrlen(name));
1361 }
Daniel Veillard6454aec1999-09-02 22:04:43 +00001362 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001363 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001364 }
1365 }
1366 return(ent);
1367}
1368
1369/**
1370 * htmlParseAttValue:
1371 * @ctxt: an HTML parser context
1372 *
1373 * parse a value for an attribute
1374 * Note: the parser won't do substitution of entities here, this
1375 * will be handled later in xmlStringGetNodeList, unless it was
1376 * asked for ctxt->replaceEntities != 0
1377 *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001378 * Returns the AttValue parsed or NULL.
1379 */
1380
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001381xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001382htmlParseAttValue(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001383 xmlChar *ret = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001384
1385 if (CUR == '"') {
1386 NEXT;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001387 ret = htmlDecodeEntities(ctxt, -1, '"', '<', 0);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001388 if (CUR == '<') {
1389 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1390 ctxt->sax->error(ctxt->userData,
1391 "Unescaped '<' not allowed in attributes values\n");
1392 ctxt->wellFormed = 0;
1393 }
1394 if (CUR != '"') {
1395 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1396 ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
1397 ctxt->wellFormed = 0;
1398 } else
1399 NEXT;
1400 } else if (CUR == '\'') {
1401 NEXT;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001402 ret = htmlDecodeEntities(ctxt, -1, '\'', '<', 0);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001403 if (CUR == '<') {
1404 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1405 ctxt->sax->error(ctxt->userData,
1406 "Unescaped '<' not allowed in attributes values\n");
1407 ctxt->wellFormed = 0;
1408 }
1409 if (CUR != '\'') {
1410 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1411 ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
1412 ctxt->wellFormed = 0;
1413 } else
1414 NEXT;
1415 } else {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001416 /*
1417 * That's an HTMLism, the attribute value may not be quoted
1418 */
1419 ret = htmlParseHTMLAttribute(ctxt);
1420 if (ret == NULL) {
1421 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1422 ctxt->sax->error(ctxt->userData, "AttValue: no value found\n");
1423 ctxt->wellFormed = 0;
1424 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001425 }
1426
1427 return(ret);
1428}
1429
1430/**
1431 * htmlParseSystemLiteral:
1432 * @ctxt: an HTML parser context
1433 *
1434 * parse an HTML Literal
1435 *
1436 * [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
1437 *
1438 * Returns the SystemLiteral parsed or NULL
1439 */
1440
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001441xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001442htmlParseSystemLiteral(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001443 const xmlChar *q;
1444 xmlChar *ret = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001445
1446 if (CUR == '"') {
1447 NEXT;
1448 q = CUR_PTR;
1449 while ((IS_CHAR(CUR)) && (CUR != '"'))
1450 NEXT;
1451 if (!IS_CHAR(CUR)) {
1452 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1453 ctxt->sax->error(ctxt->userData, "Unfinished SystemLiteral\n");
1454 ctxt->wellFormed = 0;
1455 } else {
1456 ret = xmlStrndup(q, CUR_PTR - q);
1457 NEXT;
1458 }
1459 } else if (CUR == '\'') {
1460 NEXT;
1461 q = CUR_PTR;
1462 while ((IS_CHAR(CUR)) && (CUR != '\''))
1463 NEXT;
1464 if (!IS_CHAR(CUR)) {
1465 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1466 ctxt->sax->error(ctxt->userData, "Unfinished SystemLiteral\n");
1467 ctxt->wellFormed = 0;
1468 } else {
1469 ret = xmlStrndup(q, CUR_PTR - q);
1470 NEXT;
1471 }
1472 } else {
1473 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1474 ctxt->sax->error(ctxt->userData, "SystemLiteral \" or ' expected\n");
1475 ctxt->wellFormed = 0;
1476 }
1477
1478 return(ret);
1479}
1480
1481/**
1482 * htmlParsePubidLiteral:
1483 * @ctxt: an HTML parser context
1484 *
1485 * parse an HTML public literal
1486 *
1487 * [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1488 *
1489 * Returns the PubidLiteral parsed or NULL.
1490 */
1491
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001492xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001493htmlParsePubidLiteral(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001494 const xmlChar *q;
1495 xmlChar *ret = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001496 /*
1497 * Name ::= (Letter | '_') (NameChar)*
1498 */
1499 if (CUR == '"') {
1500 NEXT;
1501 q = CUR_PTR;
1502 while (IS_PUBIDCHAR(CUR)) NEXT;
1503 if (CUR != '"') {
1504 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1505 ctxt->sax->error(ctxt->userData, "Unfinished PubidLiteral\n");
1506 ctxt->wellFormed = 0;
1507 } else {
1508 ret = xmlStrndup(q, CUR_PTR - q);
1509 NEXT;
1510 }
1511 } else if (CUR == '\'') {
1512 NEXT;
1513 q = CUR_PTR;
1514 while ((IS_LETTER(CUR)) && (CUR != '\''))
1515 NEXT;
1516 if (!IS_LETTER(CUR)) {
1517 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1518 ctxt->sax->error(ctxt->userData, "Unfinished PubidLiteral\n");
1519 ctxt->wellFormed = 0;
1520 } else {
1521 ret = xmlStrndup(q, CUR_PTR - q);
1522 NEXT;
1523 }
1524 } else {
1525 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1526 ctxt->sax->error(ctxt->userData, "SystemLiteral \" or ' expected\n");
1527 ctxt->wellFormed = 0;
1528 }
1529
1530 return(ret);
1531}
1532
1533/**
1534 * htmlParseCharData:
1535 * @ctxt: an HTML parser context
1536 * @cdata: int indicating whether we are within a CDATA section
1537 *
1538 * parse a CharData section.
1539 * if we are within a CDATA section ']]>' marks an end of section.
1540 *
1541 * [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
1542 */
1543
1544void
1545htmlParseCharData(htmlParserCtxtPtr ctxt, int cdata) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001546 const xmlChar *q;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001547
1548 q = CUR_PTR;
1549 while ((IS_CHAR(CUR)) && (CUR != '<') &&
1550 (CUR != '&')) {
1551 if ((CUR == ']') && (NXT(1) == ']') &&
1552 (NXT(2) == '>')) {
1553 if (cdata) break;
1554 else {
1555 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1556 ctxt->sax->error(ctxt->userData,
1557 "Sequence ']]>' not allowed in content\n");
1558 ctxt->wellFormed = 0;
1559 }
1560 }
1561 NEXT;
1562 }
1563 if (q == CUR_PTR) return;
1564
1565 /*
1566 * Ok the segment [q CUR_PTR] is to be consumed as chars.
1567 */
1568 if (ctxt->sax != NULL) {
1569 if (areBlanks(ctxt, q, CUR_PTR - q)) {
1570 if (ctxt->sax->ignorableWhitespace != NULL)
1571 ctxt->sax->ignorableWhitespace(ctxt->userData, q, CUR_PTR - q);
1572 } else {
1573 if (ctxt->sax->characters != NULL)
1574 ctxt->sax->characters(ctxt->userData, q, CUR_PTR - q);
1575 }
1576 }
1577}
1578
1579/**
1580 * htmlParseExternalID:
1581 * @ctxt: an HTML parser context
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001582 * @publicID: a xmlChar** receiving PubidLiteral
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001583 * @strict: indicate whether we should restrict parsing to only
1584 * production [75], see NOTE below
1585 *
1586 * Parse an External ID or a Public ID
1587 *
1588 * NOTE: Productions [75] and [83] interract badly since [75] can generate
1589 * 'PUBLIC' S PubidLiteral S SystemLiteral
1590 *
1591 * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
1592 * | 'PUBLIC' S PubidLiteral S SystemLiteral
1593 *
1594 * [83] PublicID ::= 'PUBLIC' S PubidLiteral
1595 *
1596 * Returns the function returns SystemLiteral and in the second
1597 * case publicID receives PubidLiteral, is strict is off
1598 * it is possible to return NULL and have publicID set.
1599 */
1600
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001601xmlChar *
1602htmlParseExternalID(htmlParserCtxtPtr ctxt, xmlChar **publicID, int strict) {
1603 xmlChar *URI = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001604
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001605 if ((UPPER == 'S') && (UPP(1) == 'Y') &&
1606 (UPP(2) == 'S') && (UPP(3) == 'T') &&
1607 (UPP(4) == 'E') && (UPP(5) == 'M')) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001608 SKIP(6);
1609 if (!IS_BLANK(CUR)) {
1610 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1611 ctxt->sax->error(ctxt->userData,
1612 "Space required after 'SYSTEM'\n");
1613 ctxt->wellFormed = 0;
1614 }
1615 SKIP_BLANKS;
1616 URI = htmlParseSystemLiteral(ctxt);
1617 if (URI == NULL) {
1618 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1619 ctxt->sax->error(ctxt->userData,
1620 "htmlParseExternalID: SYSTEM, no URI\n");
1621 ctxt->wellFormed = 0;
1622 }
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001623 } else if ((UPPER == 'P') && (UPP(1) == 'U') &&
1624 (UPP(2) == 'B') && (UPP(3) == 'L') &&
1625 (UPP(4) == 'I') && (UPP(5) == 'C')) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001626 SKIP(6);
1627 if (!IS_BLANK(CUR)) {
1628 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1629 ctxt->sax->error(ctxt->userData,
1630 "Space required after 'PUBLIC'\n");
1631 ctxt->wellFormed = 0;
1632 }
1633 SKIP_BLANKS;
1634 *publicID = htmlParsePubidLiteral(ctxt);
1635 if (*publicID == NULL) {
1636 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1637 ctxt->sax->error(ctxt->userData,
1638 "htmlParseExternalID: PUBLIC, no Public Identifier\n");
1639 ctxt->wellFormed = 0;
1640 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001641 SKIP_BLANKS;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001642 if ((CUR == '"') || (CUR == '\'')) {
1643 URI = htmlParseSystemLiteral(ctxt);
1644 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001645 }
1646 return(URI);
1647}
1648
1649/**
1650 * htmlParseComment:
1651 * @ctxt: an HTML parser context
1652 * @create: should we create a node, or just skip the content
1653 *
1654 * Parse an XML (SGML) comment <!-- .... -->
1655 *
1656 * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
1657 */
1658void
1659htmlParseComment(htmlParserCtxtPtr ctxt, int create) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001660 const xmlChar *q, *start;
1661 const xmlChar *r;
1662 xmlChar *val;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001663
1664 /*
1665 * Check that there is a comment right here.
1666 */
1667 if ((CUR != '<') || (NXT(1) != '!') ||
1668 (NXT(2) != '-') || (NXT(3) != '-')) return;
1669
1670 SKIP(4);
1671 start = q = CUR_PTR;
1672 NEXT;
1673 r = CUR_PTR;
1674 NEXT;
1675 while (IS_CHAR(CUR) &&
1676 ((CUR == ':') || (CUR != '>') ||
1677 (*r != '-') || (*q != '-'))) {
1678 if ((*r == '-') && (*q == '-')) {
1679 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1680 ctxt->sax->error(ctxt->userData,
1681 "Comment must not contain '--' (double-hyphen)`\n");
1682 ctxt->wellFormed = 0;
1683 }
1684 NEXT;r++;q++;
1685 }
1686 if (!IS_CHAR(CUR)) {
1687 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1688 ctxt->sax->error(ctxt->userData, "Comment not terminated \n<!--%.50s\n", start);
1689 ctxt->wellFormed = 0;
1690 } else {
1691 NEXT;
1692 if (create) {
1693 val = xmlStrndup(start, q - start);
1694 if ((ctxt->sax != NULL) && (ctxt->sax->comment != NULL))
1695 ctxt->sax->comment(ctxt->userData, val);
Daniel Veillard6454aec1999-09-02 22:04:43 +00001696 xmlFree(val);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001697 }
1698 }
1699}
1700
1701/**
1702 * htmlParseCharRef:
1703 * @ctxt: an HTML parser context
1704 *
1705 * parse Reference declarations
1706 *
1707 * [66] CharRef ::= '&#' [0-9]+ ';' |
1708 * '&#x' [0-9a-fA-F]+ ';'
1709 *
1710 * Returns the value parsed (as an int)
1711 */
1712int
1713htmlParseCharRef(htmlParserCtxtPtr ctxt) {
1714 int val = 0;
1715
1716 if ((CUR == '&') && (NXT(1) == '#') &&
1717 (NXT(2) == 'x')) {
1718 SKIP(3);
1719 while (CUR != ';') {
1720 if ((CUR >= '0') && (CUR <= '9'))
1721 val = val * 16 + (CUR - '0');
1722 else if ((CUR >= 'a') && (CUR <= 'f'))
1723 val = val * 16 + (CUR - 'a') + 10;
1724 else if ((CUR >= 'A') && (CUR <= 'F'))
1725 val = val * 16 + (CUR - 'A') + 10;
1726 else {
1727 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1728 ctxt->sax->error(ctxt->userData,
1729 "htmlParseCharRef: invalid hexadecimal value\n");
1730 ctxt->wellFormed = 0;
1731 val = 0;
1732 break;
1733 }
1734 NEXT;
1735 }
1736 if (CUR == ';')
1737 NEXT;
1738 } else if ((CUR == '&') && (NXT(1) == '#')) {
1739 SKIP(2);
1740 while (CUR != ';') {
1741 if ((CUR >= '0') && (CUR <= '9'))
1742 val = val * 10 + (CUR - '0');
1743 else {
1744 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1745 ctxt->sax->error(ctxt->userData,
1746 "htmlParseCharRef: invalid decimal value\n");
1747 ctxt->wellFormed = 0;
1748 val = 0;
1749 break;
1750 }
1751 NEXT;
1752 }
1753 if (CUR == ';')
1754 NEXT;
1755 } else {
1756 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1757 ctxt->sax->error(ctxt->userData, "htmlParseCharRef: invalid value\n");
1758 ctxt->wellFormed = 0;
1759 }
1760 /*
1761 * Check the value IS_CHAR ...
1762 */
1763 if (IS_CHAR(val)) {
1764 return(val);
1765 } else {
1766 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001767 ctxt->sax->error(ctxt->userData, "htmlParseCharRef: invalid xmlChar value %d\n",
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001768 val);
1769 ctxt->wellFormed = 0;
1770 }
1771 return(0);
1772}
1773
1774
1775/**
1776 * htmlParseDocTypeDecl :
1777 * @ctxt: an HTML parser context
1778 *
1779 * parse a DOCTYPE declaration
1780 *
1781 * [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S?
1782 * ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
1783 */
1784
1785void
1786htmlParseDocTypeDecl(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001787 xmlChar *name;
1788 xmlChar *ExternalID = NULL;
1789 xmlChar *URI = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001790
1791 /*
1792 * We know that '<!DOCTYPE' has been detected.
1793 */
1794 SKIP(9);
1795
1796 SKIP_BLANKS;
1797
1798 /*
1799 * Parse the DOCTYPE name.
1800 */
1801 name = htmlParseName(ctxt);
1802 if (name == NULL) {
1803 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1804 ctxt->sax->error(ctxt->userData, "htmlParseDocTypeDecl : no DOCTYPE name !\n");
1805 ctxt->wellFormed = 0;
1806 }
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001807 /*
1808 * Check that upper(name) == "HTML" !!!!!!!!!!!!!
1809 */
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001810
1811 SKIP_BLANKS;
1812
1813 /*
1814 * Check for SystemID and ExternalID
1815 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001816 URI = htmlParseExternalID(ctxt, &ExternalID, 0);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001817 SKIP_BLANKS;
1818
1819 /*
1820 * We should be at the end of the DOCTYPE declaration.
1821 */
1822 if (CUR != '>') {
1823 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1824 ctxt->sax->error(ctxt->userData, "DOCTYPE unproperly terminated\n");
1825 ctxt->wellFormed = 0;
1826 /* We shouldn't try to resynchronize ... */
1827 } else {
1828 }
1829 NEXT;
1830
1831 /*
Daniel Veillard5233ffc1999-07-06 22:25:25 +00001832 * Create the document accordingly to the DOCTYPE
1833 */
1834 ctxt->myDoc = htmlNewDoc(URI, ExternalID);
1835
1836 /*
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001837 * Cleanup, since we don't use all those identifiers
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001838 */
Daniel Veillard6454aec1999-09-02 22:04:43 +00001839 if (URI != NULL) xmlFree(URI);
1840 if (ExternalID != NULL) xmlFree(ExternalID);
1841 if (name != NULL) xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001842}
1843
1844/**
1845 * htmlParseAttribute:
1846 * @ctxt: an HTML parser context
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001847 * @value: a xmlChar ** used to store the value of the attribute
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001848 *
1849 * parse an attribute
1850 *
1851 * [41] Attribute ::= Name Eq AttValue
1852 *
1853 * [25] Eq ::= S? '=' S?
1854 *
1855 * With namespace:
1856 *
1857 * [NS 11] Attribute ::= QName Eq AttValue
1858 *
1859 * Also the case QName == xmlns:??? is handled independently as a namespace
1860 * definition.
1861 *
1862 * Returns the attribute name, and the value in *value.
1863 */
1864
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001865xmlChar *
1866htmlParseAttribute(htmlParserCtxtPtr ctxt, xmlChar **value) {
1867 xmlChar *name, *val;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001868
1869 *value = NULL;
1870 name = htmlParseName(ctxt);
1871 if (name == NULL) {
1872 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1873 ctxt->sax->error(ctxt->userData, "error parsing attribute name\n");
1874 ctxt->wellFormed = 0;
1875 return(NULL);
1876 }
1877
1878 /*
1879 * read the value
1880 */
1881 SKIP_BLANKS;
1882 if (CUR == '=') {
1883 NEXT;
1884 SKIP_BLANKS;
1885 val = htmlParseAttValue(ctxt);
1886 } else {
1887 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1888 ctxt->sax->error(ctxt->userData,
1889 "Specification mandate value for attribute %s\n", name);
1890 ctxt->wellFormed = 0;
1891 return(NULL);
1892 }
1893
1894 *value = val;
1895 return(name);
1896}
1897
1898/**
1899 * htmlParseStartTag:
1900 * @ctxt: an HTML parser context
1901 *
1902 * parse a start of tag either for rule element or
1903 * EmptyElement. In both case we don't parse the tag closing chars.
1904 *
1905 * [40] STag ::= '<' Name (S Attribute)* S? '>'
1906 *
1907 * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
1908 *
1909 * With namespace:
1910 *
1911 * [NS 8] STag ::= '<' QName (S Attribute)* S? '>'
1912 *
1913 * [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>'
1914 *
1915 * Returns the element name parsed
1916 */
1917
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001918xmlChar *
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001919htmlParseStartTag(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001920 xmlChar *name;
1921 xmlChar *attname;
1922 xmlChar *attvalue;
1923 const xmlChar **atts = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001924 int nbatts = 0;
1925 int maxatts = 0;
1926 int i;
1927
1928 if (CUR != '<') return(NULL);
1929 NEXT;
1930
1931 name = htmlParseHTMLName(ctxt);
1932 if (name == NULL) {
1933 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1934 ctxt->sax->error(ctxt->userData,
1935 "htmlParseStartTag: invalid element name\n");
1936 ctxt->wellFormed = 0;
1937 return(NULL);
1938 }
1939
1940 /*
1941 * Check for auto-closure of HTML elements.
1942 */
1943 htmlAutoClose(ctxt, name);
1944
1945 /*
1946 * Now parse the attributes, it ends up with the ending
1947 *
1948 * (S Attribute)* S?
1949 */
1950 SKIP_BLANKS;
1951 while ((IS_CHAR(CUR)) &&
1952 (CUR != '>') &&
1953 ((CUR != '/') || (NXT(1) != '>'))) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001954 const xmlChar *q = CUR_PTR;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001955
1956 attname = htmlParseAttribute(ctxt, &attvalue);
1957 if ((attname != NULL) && (attvalue != NULL)) {
1958 /*
1959 * Well formedness requires at most one declaration of an attribute
1960 */
1961 for (i = 0; i < nbatts;i += 2) {
1962 if (!xmlStrcmp(atts[i], attname)) {
1963 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
1964 ctxt->sax->error(ctxt->userData, "Attribute %s redefined\n",
1965 name);
1966 ctxt->wellFormed = 0;
Daniel Veillard6454aec1999-09-02 22:04:43 +00001967 xmlFree(attname);
1968 xmlFree(attvalue);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001969 break;
1970 }
1971 }
1972
1973 /*
1974 * Add the pair to atts
1975 */
1976 if (atts == NULL) {
1977 maxatts = 10;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001978 atts = (const xmlChar **) xmlMalloc(maxatts * sizeof(xmlChar *));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001979 if (atts == NULL) {
1980 fprintf(stderr, "malloc of %ld byte failed\n",
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001981 maxatts * (long)sizeof(xmlChar *));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001982 return(NULL);
1983 }
1984 } else if (nbatts + 2 < maxatts) {
1985 maxatts *= 2;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001986 atts = (const xmlChar **) xmlRealloc(atts, maxatts * sizeof(xmlChar *));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001987 if (atts == NULL) {
1988 fprintf(stderr, "realloc of %ld byte failed\n",
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001989 maxatts * (long)sizeof(xmlChar *));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00001990 return(NULL);
1991 }
1992 }
1993 atts[nbatts++] = attname;
1994 atts[nbatts++] = attvalue;
1995 atts[nbatts] = NULL;
1996 atts[nbatts + 1] = NULL;
1997 }
1998
1999 SKIP_BLANKS;
2000 if (q == CUR_PTR) {
2001 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2002 ctxt->sax->error(ctxt->userData,
2003 "htmlParseStartTag: problem parsing attributes\n");
2004 ctxt->wellFormed = 0;
2005 break;
2006 }
2007 }
2008
2009 /*
2010 * SAX: Start of Element !
2011 */
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002012 htmlnamePush(ctxt, xmlStrdup(name));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002013 if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
2014 ctxt->sax->startElement(ctxt->userData, name, atts);
2015
2016 if (atts != NULL) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002017 for (i = 0;i < nbatts;i++) xmlFree((xmlChar *) atts[i]);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002018 xmlFree(atts);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002019 }
2020 return(name);
2021}
2022
2023/**
2024 * htmlParseEndTag:
2025 * @ctxt: an HTML parser context
2026 * @tagname: the tag name as parsed in the opening tag.
2027 *
2028 * parse an end of tag
2029 *
2030 * [42] ETag ::= '</' Name S? '>'
2031 *
2032 * With namespace
2033 *
2034 * [NS 9] ETag ::= '</' QName S? '>'
2035 */
2036
2037void
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002038htmlParseEndTag(htmlParserCtxtPtr ctxt, const xmlChar *tagname) {
2039 xmlChar *name;
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002040 xmlChar *oldname;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002041 int i;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002042
2043 if ((CUR != '<') || (NXT(1) != '/')) {
2044 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2045 ctxt->sax->error(ctxt->userData, "htmlParseEndTag: '</' not found\n");
2046 ctxt->wellFormed = 0;
2047 return;
2048 }
2049 SKIP(2);
2050
2051 name = htmlParseHTMLName(ctxt);
2052
2053 /*
2054 * We should definitely be at the ending "S? '>'" part
2055 */
2056 SKIP_BLANKS;
2057 if ((!IS_CHAR(CUR)) || (CUR != '>')) {
2058 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2059 ctxt->sax->error(ctxt->userData, "End tag : expected '>'\n");
2060 ctxt->wellFormed = 0;
2061 } else
2062 NEXT;
2063
2064 /*
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002065 * Check that we are not closing an already closed tag,
2066 * <p><b>...</p></b> is a really common error !
2067 */
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002068 for (i = ctxt->nameNr - 1;i >= 0;i--) {
2069 if ((ctxt->nameTab[i] != NULL) &&
2070 (!xmlStrcmp(tagname, ctxt->nameTab[i])))
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002071 break;
2072 }
2073 if (i < 0) {
2074 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2075 ctxt->sax->error(ctxt->userData,
2076 "htmlParseEndTag: unexpected close for tag %s\n",
2077 tagname);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002078 xmlFree(name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002079 ctxt->wellFormed = 0;
2080 return;
2081 }
2082
2083 /*
2084 * Check for auto-closure of HTML elements.
2085 */
2086 htmlAutoCloseOnClose(ctxt, name);
2087
2088 /*
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002089 * Well formedness constraints, opening and closing must match.
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002090 * With the exception that the autoclose may have popped stuff out
2091 * of the stack.
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002092 */
2093 if (xmlStrcmp(name, tagname)) {
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002094 if ((ctxt->name != NULL) &&
2095 (xmlStrcmp(ctxt->name, name))) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002096 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2097 ctxt->sax->error(ctxt->userData,
2098 "Opening and ending tag mismatch: %s and %s\n",
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002099 name, ctxt->name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002100 ctxt->wellFormed = 0;
2101 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002102 }
2103
2104 /*
2105 * SAX: End of Tag
2106 */
2107 if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
2108 ctxt->sax->endElement(ctxt->userData, name);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002109 oldname = ctxt->name;
2110 htmlnamePop(ctxt);
2111 xmlFree(oldname);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002112
2113 if (name != NULL)
Daniel Veillard6454aec1999-09-02 22:04:43 +00002114 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002115
2116 return;
2117}
2118
2119
2120/**
2121 * htmlParseReference:
2122 * @ctxt: an HTML parser context
2123 *
2124 * parse and handle entity references in content,
2125 * this will end-up in a call to character() since this is either a
2126 * CharRef, or a predefined entity.
2127 */
2128void
2129htmlParseReference(htmlParserCtxtPtr ctxt) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002130 htmlEntityDescPtr ent;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002131 xmlChar out[2];
2132 xmlChar *name;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002133 int val;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002134 if (CUR != '&') return;
2135
2136 if (NXT(1) == '#') {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002137 val = htmlParseCharRef(ctxt);
Daniel Veillardb96e6431999-08-29 21:02:19 +00002138 /* invalid for UTF-8 variable encoding !!!!! */
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002139 out[0] = val;
2140 out[1] = 0;
2141 if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
2142 ctxt->sax->characters(ctxt->userData, out, 1);
2143 } else {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002144 ent = htmlParseEntityRef(ctxt, &name);
2145 if (name == NULL) return; /* Shall we output & anyway ? */
2146 if ((ent == NULL) || (ent->value <= 0) || (ent->value >= 255)) {
2147 if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL)) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002148 ctxt->sax->characters(ctxt->userData, BAD_CAST "&", 1);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002149 ctxt->sax->characters(ctxt->userData, name, xmlStrlen(name));
Daniel Veillardb96e6431999-08-29 21:02:19 +00002150 ctxt->sax->characters(ctxt->userData, BAD_CAST ";", 1);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002151 }
2152 } else {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002153 /* invalid for UTF-8 variable encoding !!!!! */
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002154 out[0] = ent->value;
2155 out[1] = 0;
2156 if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL))
2157 ctxt->sax->characters(ctxt->userData, out, 1);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002158 }
Daniel Veillard6454aec1999-09-02 22:04:43 +00002159 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002160 }
2161}
2162
2163/**
2164 * htmlParseContent:
2165 * @ctxt: an HTML parser context
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002166 * @name: the node name
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002167 *
2168 * Parse a content: comment, sub-element, reference or text.
2169 *
2170 */
2171
2172void
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002173htmlParseContent(htmlParserCtxtPtr ctxt, const xmlChar *name) {
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002174 xmlChar *currentNode;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002175
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002176 currentNode = ctxt->name;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002177 while ((CUR != '<') || (NXT(1) != '/')) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002178 const xmlChar *test = CUR_PTR;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002179
2180 /*
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002181 * Has this node been popped out during parsing of
2182 * the next element
2183 */
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002184 if (currentNode != ctxt->name) return;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002185
2186 /*
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002187 * First case : a comment
2188 */
2189 if ((CUR == '<') && (NXT(1) == '!') &&
2190 (NXT(2) == '-') && (NXT(3) == '-')) {
2191 htmlParseComment(ctxt, 1);
2192 }
2193
2194 /*
2195 * Second case : a sub-element.
2196 */
2197 else if (CUR == '<') {
2198 htmlParseElement(ctxt);
2199 }
2200
2201 /*
2202 * Third case : a reference. If if has not been resolved,
2203 * parsing returns it's Name, create the node
2204 */
2205 else if (CUR == '&') {
2206 htmlParseReference(ctxt);
2207 }
2208
2209 /*
2210 * Last case, text. Note that References are handled directly.
2211 */
2212 else {
2213 htmlParseCharData(ctxt, 0);
2214 }
2215
2216 if (test == CUR_PTR) {
2217 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2218 ctxt->sax->error(ctxt->userData,
2219 "detected an error in element content\n");
2220 ctxt->wellFormed = 0;
2221 break;
2222 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002223 GROW;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002224 }
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002225
2226 /*
2227 * parse the end of tag: '</' should be here.
2228 */
2229 htmlParseEndTag(ctxt, name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002230}
2231
2232/**
2233 * htmlParseElement:
2234 * @ctxt: an HTML parser context
2235 *
2236 * parse an HTML element, this is highly recursive
2237 *
2238 * [39] element ::= EmptyElemTag | STag content ETag
2239 *
2240 * [41] Attribute ::= Name Eq AttValue
2241 */
2242
2243void
2244htmlParseElement(htmlParserCtxtPtr ctxt) {
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002245 const xmlChar *openTag = CUR_PTR;
2246 xmlChar *name;
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002247 xmlChar *oldname;
2248 xmlChar *currentNode;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002249 htmlElemDescPtr info;
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002250 htmlParserNodeInfo node_info;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002251
2252 /* Capture start position */
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002253 if (ctxt->record_info) {
2254 node_info.begin_pos = ctxt->input->consumed +
2255 (CUR_PTR - ctxt->input->base);
2256 node_info.begin_line = ctxt->input->line;
2257 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002258
2259 name = htmlParseStartTag(ctxt);
2260 if (name == NULL) {
2261 return;
2262 }
2263
2264 /*
2265 * Lookup the info for that element.
2266 */
2267 info = htmlTagLookup(name);
2268 if (info == NULL) {
2269 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2270 ctxt->sax->error(ctxt->userData, "Tag %s invalid\n",
2271 name);
2272 ctxt->wellFormed = 0;
2273 } else if (info->depr) {
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002274/***************************
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002275 if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
2276 ctxt->sax->warning(ctxt->userData, "Tag %s is deprecated\n",
2277 name);
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002278 ***************************/
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002279 }
2280
2281 /*
2282 * Check for an Empty Element labelled the XML/SGML way
2283 */
2284 if ((CUR == '/') && (NXT(1) == '>')) {
2285 SKIP(2);
2286 if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
2287 ctxt->sax->endElement(ctxt->userData, name);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002288 oldname = ctxt->name;
2289 htmlnamePop(ctxt);
2290 xmlFree(oldname);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002291 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002292 return;
2293 }
2294
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002295 if (CUR == '>') {
2296 NEXT;
2297 } else {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002298 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2299 ctxt->sax->error(ctxt->userData, "Couldn't find end of Start Tag\n%.30s\n",
2300 openTag);
2301 ctxt->wellFormed = 0;
2302
2303 /*
2304 * end of parsing of this node.
2305 */
2306 nodePop(ctxt);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002307 xmlFree(name);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002308 oldname = ctxt->name;
2309 htmlnamePop(ctxt);
2310 xmlFree(oldname);
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002311
2312 /*
2313 * Capture end position and add node
2314 */
2315 if ( currentNode != NULL && ctxt->record_info ) {
2316 node_info.end_pos = ctxt->input->consumed +
2317 (CUR_PTR - ctxt->input->base);
2318 node_info.end_line = ctxt->input->line;
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002319 node_info.node = ctxt->node;
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002320 xmlParserAddNodeInfo(ctxt, &node_info);
2321 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002322 return;
2323 }
2324
2325 /*
2326 * Check for an Empty Element from DTD definition
2327 */
2328 if ((info != NULL) && (info->empty)) {
2329 if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
2330 ctxt->sax->endElement(ctxt->userData, name);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002331 xmlFree(name);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002332 oldname = ctxt->name;
2333 htmlnamePop(ctxt);
2334 xmlFree(oldname);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002335 return;
2336 }
2337
2338 /*
2339 * Parse the content of the element:
2340 */
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002341 currentNode = ctxt->name;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002342 htmlParseContent(ctxt, name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002343
2344 /*
2345 * check whether the element get popped due to auto closure
2346 * on start tag
2347 */
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002348 if (currentNode != ctxt->name) {
Daniel Veillard6454aec1999-09-02 22:04:43 +00002349 xmlFree(name);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002350 return;
2351 }
2352
2353 if (!IS_CHAR(CUR)) {
2354 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2355 ctxt->sax->error(ctxt->userData,
2356 "Premature end of data in tag %.30s\n", openTag);
2357 ctxt->wellFormed = 0;
2358
2359 /*
2360 * end of parsing of this node.
2361 */
2362 nodePop(ctxt);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002363 xmlFree(name);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002364 oldname = ctxt->name;
2365 htmlnamePop(ctxt);
2366 xmlFree(oldname);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002367 return;
2368 }
2369
Daniel Veillard6454aec1999-09-02 22:04:43 +00002370 xmlFree(name);
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002371
2372 /*
2373 * Capture end position and add node
2374 */
2375 if ( currentNode != NULL && ctxt->record_info ) {
2376 node_info.end_pos = ctxt->input->consumed +
2377 (CUR_PTR - ctxt->input->base);
2378 node_info.end_line = ctxt->input->line;
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002379 node_info.node = ctxt->node;
Daniel Veillard1ff7ae31999-09-01 12:19:13 +00002380 xmlParserAddNodeInfo(ctxt, &node_info);
2381 }
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002382}
2383
2384/**
2385 * htmlParseDocument :
2386 * @ctxt: an HTML parser context
2387 *
2388 * parse an HTML document (and build a tree if using the standard SAX
2389 * interface).
2390 *
2391 * Returns 0, -1 in case of error. the parser context is augmented
2392 * as a result of the parsing.
2393 */
2394
2395int
2396htmlParseDocument(htmlParserCtxtPtr ctxt) {
2397 htmlDefaultSAXHandlerInit();
2398 ctxt->html = 1;
2399
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002400 GROW;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002401 /*
Daniel Veillardb96e6431999-08-29 21:02:19 +00002402 * SAX: beginning of the document processing.
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002403 */
2404 if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
2405 ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
2406
2407 /*
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002408 * Wipe out everything which is before the first '<'
2409 */
2410 if (IS_BLANK(CUR)) {
2411 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2412 ctxt->sax->error(ctxt->userData,
2413 "Extra spaces at the beginning of the document are not allowed\n");
2414 ctxt->wellFormed = 0;
2415 SKIP_BLANKS;
2416 }
2417
2418 if (CUR == 0) {
2419 if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
2420 ctxt->sax->error(ctxt->userData, "Document is empty\n");
2421 ctxt->wellFormed = 0;
2422 }
2423
2424
2425 /*
2426 * Then possibly doc type declaration(s) and more Misc
2427 * (doctypedecl Misc*)?
2428 */
2429 if ((CUR == '<') && (NXT(1) == '!') &&
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002430 (UPP(2) == 'D') && (UPP(3) == 'O') &&
2431 (UPP(4) == 'C') && (UPP(5) == 'T') &&
2432 (UPP(6) == 'Y') && (UPP(7) == 'P') &&
2433 (UPP(8) == 'E')) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002434 htmlParseDocTypeDecl(ctxt);
2435 }
2436 SKIP_BLANKS;
2437
2438 /*
2439 * Create the document if not done already.
2440 */
2441 if (ctxt->myDoc == NULL) {
2442 ctxt->myDoc = htmlNewDoc(NULL, NULL);
2443 }
2444
2445 /*
2446 * Time to start parsing the tree itself
2447 */
2448 htmlParseElement(ctxt);
2449
2450 /*
2451 * SAX: end of the document processing.
2452 */
2453 if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
2454 ctxt->sax->endDocument(ctxt->userData);
2455 if (! ctxt->wellFormed) return(-1);
2456 return(0);
2457}
2458
2459
2460/********************************************************************************
2461 * *
2462 * Parser contexts handling *
2463 * *
2464 ********************************************************************************/
2465
2466/**
2467 * xmlInitParserCtxt:
2468 * @ctxt: an HTML parser context
2469 *
2470 * Initialize a parser context
2471 */
2472
2473void
2474htmlInitParserCtxt(htmlParserCtxtPtr ctxt)
2475{
2476 htmlSAXHandler *sax;
2477
Daniel Veillard6454aec1999-09-02 22:04:43 +00002478 sax = (htmlSAXHandler *) xmlMalloc(sizeof(htmlSAXHandler));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002479 if (sax == NULL) {
2480 fprintf(stderr, "htmlInitParserCtxt: out of memory\n");
2481 }
2482
2483 /* Allocate the Input stack */
Daniel Veillard6454aec1999-09-02 22:04:43 +00002484 ctxt->inputTab = (htmlParserInputPtr *) xmlMalloc(5 * sizeof(htmlParserInputPtr));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002485 ctxt->inputNr = 0;
2486 ctxt->inputMax = 5;
2487 ctxt->input = NULL;
2488 ctxt->version = NULL;
2489 ctxt->encoding = NULL;
2490 ctxt->standalone = -1;
2491
2492 /* Allocate the Node stack */
Daniel Veillard6454aec1999-09-02 22:04:43 +00002493 ctxt->nodeTab = (htmlNodePtr *) xmlMalloc(10 * sizeof(htmlNodePtr));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002494 ctxt->nodeNr = 0;
2495 ctxt->nodeMax = 10;
2496 ctxt->node = NULL;
2497
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002498 /* Allocate the Name stack */
2499 ctxt->nameTab = (xmlChar **) xmlMalloc(10 * sizeof(xmlChar *));
2500 ctxt->nameNr = 0;
2501 ctxt->nameMax = 10;
2502 ctxt->name = NULL;
2503
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002504 if (sax == NULL) ctxt->sax = &htmlDefaultSAXHandler;
2505 else {
2506 ctxt->sax = sax;
2507 memcpy(sax, &htmlDefaultSAXHandler, sizeof(htmlSAXHandler));
2508 }
2509 ctxt->userData = ctxt;
2510 ctxt->myDoc = NULL;
2511 ctxt->wellFormed = 1;
Daniel Veillard5233ffc1999-07-06 22:25:25 +00002512 ctxt->replaceEntities = 0;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002513 ctxt->html = 1;
2514 ctxt->record_info = 0;
2515 xmlInitNodeInfoSeq(&ctxt->node_seq);
2516}
2517
2518/**
2519 * htmlFreeParserCtxt:
2520 * @ctxt: an HTML parser context
2521 *
2522 * Free all the memory used by a parser context. However the parsed
2523 * document in ctxt->myDoc is not freed.
2524 */
2525
2526void
2527htmlFreeParserCtxt(htmlParserCtxtPtr ctxt)
2528{
2529 htmlParserInputPtr input;
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002530 xmlChar *oldname;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002531
2532 if (ctxt == NULL) return;
2533
2534 while ((input = inputPop(ctxt)) != NULL) {
2535 xmlFreeInputStream(input);
2536 }
2537
Daniel Veillard6454aec1999-09-02 22:04:43 +00002538 if (ctxt->nodeTab != NULL) xmlFree(ctxt->nodeTab);
Daniel Veillard2673d3c1999-10-08 14:37:09 +00002539 while ((oldname = ctxt->name) != NULL) {
2540 htmlnamePop(ctxt);
2541 xmlFree(oldname);
2542 }
2543 if (ctxt->nameTab != NULL) xmlFree(ctxt->nameTab);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002544 if (ctxt->inputTab != NULL) xmlFree(ctxt->inputTab);
2545 if (ctxt->version != NULL) xmlFree((char *) ctxt->version);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002546 if ((ctxt->sax != NULL) && (ctxt->sax != &htmlDefaultSAXHandler))
Daniel Veillard6454aec1999-09-02 22:04:43 +00002547 xmlFree(ctxt->sax);
2548 xmlFree(ctxt);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002549}
2550
2551/**
2552 * htmlCreateDocParserCtxt :
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002553 * @cur: a pointer to an array of xmlChar
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002554 * @encoding: a free form C string describing the HTML document encoding, or NULL
2555 *
2556 * Create a parser context for an HTML document.
2557 *
2558 * Returns the new parser context or NULL
2559 */
2560htmlParserCtxtPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002561htmlCreateDocParserCtxt(xmlChar *cur, const char *encoding) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002562 htmlParserCtxtPtr ctxt;
2563 htmlParserInputPtr input;
2564 /* htmlCharEncoding enc; */
2565
Daniel Veillard6454aec1999-09-02 22:04:43 +00002566 ctxt = (htmlParserCtxtPtr) xmlMalloc(sizeof(htmlParserCtxt));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002567 if (ctxt == NULL) {
2568 perror("malloc");
2569 return(NULL);
2570 }
2571 htmlInitParserCtxt(ctxt);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002572 input = (htmlParserInputPtr) xmlMalloc(sizeof(htmlParserInput));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002573 if (input == NULL) {
2574 perror("malloc");
Daniel Veillard6454aec1999-09-02 22:04:43 +00002575 xmlFree(ctxt);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002576 return(NULL);
2577 }
2578
2579 /*
2580 * plug some encoding conversion routines here. !!!
2581 if (encoding != NULL) {
2582 enc = htmlDetectCharEncoding(cur);
2583 htmlSwitchEncoding(ctxt, enc);
2584 }
2585 */
2586
2587 input->filename = NULL;
2588 input->line = 1;
2589 input->col = 1;
2590 input->base = cur;
2591 input->cur = cur;
2592 input->free = NULL;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002593 input->buf = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002594
2595 inputPush(ctxt, input);
2596 return(ctxt);
2597}
2598
2599/********************************************************************************
2600 * *
2601 * User entry points *
2602 * *
2603 ********************************************************************************/
2604
2605/**
2606 * htmlSAXParseDoc :
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002607 * @cur: a pointer to an array of xmlChar
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002608 * @encoding: a free form C string describing the HTML document encoding, or NULL
2609 * @sax: the SAX handler block
2610 * @userData: if using SAX, this pointer will be provided on callbacks.
2611 *
2612 * parse an HTML in-memory document and build a tree.
2613 * It use the given SAX function block to handle the parsing callback.
2614 * If sax is NULL, fallback to the default DOM tree building routines.
2615 *
2616 * Returns the resulting document tree
2617 */
2618
2619htmlDocPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002620htmlSAXParseDoc(xmlChar *cur, const char *encoding, htmlSAXHandlerPtr sax, void *userData) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002621 htmlDocPtr ret;
2622 htmlParserCtxtPtr ctxt;
2623
2624 if (cur == NULL) return(NULL);
2625
2626
2627 ctxt = htmlCreateDocParserCtxt(cur, encoding);
2628 if (ctxt == NULL) return(NULL);
2629 if (sax != NULL) {
2630 ctxt->sax = sax;
2631 ctxt->userData = userData;
2632 }
2633
2634 htmlParseDocument(ctxt);
2635 ret = ctxt->myDoc;
2636 if (sax != NULL) {
2637 ctxt->sax = NULL;
2638 ctxt->userData = NULL;
2639 }
2640 htmlFreeParserCtxt(ctxt);
2641
2642 return(ret);
2643}
2644
2645/**
2646 * htmlParseDoc :
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002647 * @cur: a pointer to an array of xmlChar
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002648 * @encoding: a free form C string describing the HTML document encoding, or NULL
2649 *
2650 * parse an HTML in-memory document and build a tree.
2651 *
2652 * Returns the resulting document tree
2653 */
2654
2655htmlDocPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002656htmlParseDoc(xmlChar *cur, const char *encoding) {
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002657 return(htmlSAXParseDoc(cur, encoding, NULL, NULL));
2658}
2659
2660
2661/**
2662 * htmlCreateFileParserCtxt :
2663 * @filename: the filename
2664 * @encoding: a free form C string describing the HTML document encoding, or NULL
2665 *
2666 * Create a parser context for a file content.
2667 * Automatic support for ZLIB/Compress compressed document is provided
2668 * by default if found at compile-time.
2669 *
2670 * Returns the new parser context or NULL
2671 */
2672htmlParserCtxtPtr
2673htmlCreateFileParserCtxt(const char *filename, const char *encoding)
2674{
2675 htmlParserCtxtPtr ctxt;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002676 htmlParserInputPtr inputStream;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002677 xmlParserInputBufferPtr buf;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002678 /* htmlCharEncoding enc; */
2679
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002680 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
2681 if (buf == NULL) return(NULL);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002682
Daniel Veillard6454aec1999-09-02 22:04:43 +00002683 ctxt = (htmlParserCtxtPtr) xmlMalloc(sizeof(htmlParserCtxt));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002684 if (ctxt == NULL) {
2685 perror("malloc");
2686 return(NULL);
2687 }
2688 htmlInitParserCtxt(ctxt);
Daniel Veillard6454aec1999-09-02 22:04:43 +00002689 inputStream = (htmlParserInputPtr) xmlMalloc(sizeof(htmlParserInput));
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002690 if (inputStream == NULL) {
2691 perror("malloc");
Daniel Veillard6454aec1999-09-02 22:04:43 +00002692 xmlFree(ctxt);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002693 return(NULL);
2694 }
2695
Daniel Veillard6454aec1999-09-02 22:04:43 +00002696 inputStream->filename = xmlMemStrdup(filename);
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002697 inputStream->line = 1;
2698 inputStream->col = 1;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002699 inputStream->buf = buf;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002700
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002701 inputStream->base = inputStream->buf->buffer->content;
2702 inputStream->cur = inputStream->buf->buffer->content;
2703 inputStream->free = NULL;
Daniel Veillardbe70ff71999-07-05 16:50:46 +00002704
2705 inputPush(ctxt, inputStream);
2706 return(ctxt);
2707}
2708
2709/**
2710 * htmlSAXParseFile :
2711 * @filename: the filename
2712 * @encoding: a free form C string describing the HTML document encoding, or NULL
2713 * @sax: the SAX handler block
2714 * @userData: if using SAX, this pointer will be provided on callbacks.
2715 *
2716 * parse an HTML file and build a tree. Automatic support for ZLIB/Compress
2717 * compressed document is provided by default if found at compile-time.
2718 * It use the given SAX function block to handle the parsing callback.
2719 * If sax is NULL, fallback to the default DOM tree building routines.
2720 *
2721 * Returns the resulting document tree
2722 */
2723
2724htmlDocPtr
2725htmlSAXParseFile(const char *filename, const char *encoding, htmlSAXHandlerPtr sax,
2726 void *userData) {
2727 htmlDocPtr ret;
2728 htmlParserCtxtPtr ctxt;
2729
2730 ctxt = htmlCreateFileParserCtxt(filename, encoding);
2731 if (ctxt == NULL) return(NULL);
2732 if (sax != NULL) {
2733 ctxt->sax = sax;
2734 ctxt->userData = userData;
2735 }
2736
2737 htmlParseDocument(ctxt);
2738
2739 ret = ctxt->myDoc;
2740 if (sax != NULL) {
2741 ctxt->sax = NULL;
2742 ctxt->userData = NULL;
2743 }
2744 htmlFreeParserCtxt(ctxt);
2745
2746 return(ret);
2747}
2748
2749/**
2750 * htmlParseFile :
2751 * @filename: the filename
2752 * @encoding: a free form C string describing the HTML document encoding, or NULL
2753 *
2754 * parse an HTML file and build a tree. Automatic support for ZLIB/Compress
2755 * compressed document is provided by default if found at compile-time.
2756 *
2757 * Returns the resulting document tree
2758 */
2759
2760htmlDocPtr
2761htmlParseFile(const char *filename, const char *encoding) {
2762 return(htmlSAXParseFile(filename, encoding, NULL, NULL));
2763}