Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/libxml2/HTMLparser.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * HTMLparser.c : an HTML parser
3
 *
4
 * References:
5
 *   HTML Living Standard
6
 *     https://html.spec.whatwg.org/multipage/parsing.html
7
 *
8
 * Tokenization now conforms to HTML5. Tree construction still follows
9
 * a custom, non-standard implementation. See:
10
 *
11
 *     https://gitlab.gnome.org/GNOME/libxml2/-/issues/211
12
 *
13
 * See Copyright for the status of this software.
14
 *
15
 * daniel@veillard.com
16
 */
17
18
#define IN_LIBXML
19
#include "libxml.h"
20
#ifdef LIBXML_HTML_ENABLED
21
22
#include <string.h>
23
#include <ctype.h>
24
#include <stdlib.h>
25
26
#include <libxml/HTMLparser.h>
27
#include <libxml/xmlmemory.h>
28
#include <libxml/tree.h>
29
#include <libxml/parser.h>
30
#include <libxml/parserInternals.h>
31
#include <libxml/xmlerror.h>
32
#include <libxml/HTMLtree.h>
33
#include <libxml/entities.h>
34
#include <libxml/encoding.h>
35
#include <libxml/xmlIO.h>
36
#include <libxml/uri.h>
37
38
#include "private/buf.h"
39
#include "private/dict.h"
40
#include "private/enc.h"
41
#include "private/error.h"
42
#include "private/html.h"
43
#include "private/io.h"
44
#include "private/memory.h"
45
#include "private/parser.h"
46
#include "private/tree.h"
47
48
#define HTML_MAX_NAMELEN 1000
49
0
#define HTML_MAX_ATTRS 100000000 /* 100 million */
50
0
#define HTML_PARSER_BIG_BUFFER_SIZE 1000
51
0
#define HTML_PARSER_BUFFER_SIZE 100
52
53
#define IS_WS_HTML(c) \
54
0
    (((c) == 0x20) || \
55
0
     (((c) >= 0x09) && ((c) <= 0x0D) && ((c) != 0x0B)))
56
57
#define IS_HEX_DIGIT(c) \
58
0
    ((IS_ASCII_DIGIT(c)) || \
59
0
     ((((c) | 0x20) >= 'a') && (((c) | 0x20) <= 'f')))
60
61
#define IS_UPPER(c) \
62
0
    (((c) >= 'A') && ((c) <= 'Z'))
63
64
#define IS_ALNUM(c) \
65
0
    (IS_ASCII_LETTER(c) || IS_ASCII_DIGIT(c))
66
67
typedef enum {
68
    INSERT_INITIAL = 1,
69
    INSERT_IN_HEAD = 3,
70
    INSERT_IN_BODY = 10
71
} htmlInsertMode;
72
73
typedef const unsigned htmlAsciiMask[2];
74
75
static htmlAsciiMask MASK_DQ = {
76
    0,
77
    1u << ('"' - 32),
78
};
79
static htmlAsciiMask MASK_SQ = {
80
    0,
81
    1u << ('\'' - 32),
82
};
83
static htmlAsciiMask MASK_GT = {
84
    0,
85
    1u << ('>' - 32),
86
};
87
static htmlAsciiMask MASK_DASH = {
88
    0,
89
    1u << ('-' - 32),
90
};
91
static htmlAsciiMask MASK_WS_GT = {
92
    1u << 0x09 | 1u << 0x0A | 1u << 0x0C | 1u << 0x0D,
93
    1u << (' ' - 32) | 1u << ('>' - 32),
94
};
95
static htmlAsciiMask MASK_DQ_GT = {
96
    0,
97
    1u << ('"' - 32) | 1u << ('>' - 32),
98
};
99
static htmlAsciiMask MASK_SQ_GT = {
100
    0,
101
    1u << ('\'' - 32) | 1u << ('>' - 32),
102
};
103
104
static int htmlOmittedDefaultValue = 1;
105
106
static int
107
htmlParseElementInternal(htmlParserCtxtPtr ctxt);
108
109
/************************************************************************
110
 *                  *
111
 *    Some factorized error routines        *
112
 *                  *
113
 ************************************************************************/
114
115
/**
116
 * htmlErrMemory:
117
 * @ctxt:  an HTML parser context
118
 * @extra:  extra information
119
 *
120
 * Handle a redefinition of attribute error
121
 */
122
static void
123
htmlErrMemory(xmlParserCtxtPtr ctxt)
124
0
{
125
0
    xmlCtxtErrMemory(ctxt);
126
0
}
127
128
/**
129
 * htmlParseErr:
130
 * @ctxt:  an HTML parser context
131
 * @error:  the error number
132
 * @msg:  the error message
133
 * @str1:  string infor
134
 * @str2:  string infor
135
 *
136
 * Handle a fatal parser error, i.e. violating Well-Formedness constraints
137
 */
138
static void LIBXML_ATTR_FORMAT(3,0)
139
htmlParseErr(xmlParserCtxtPtr ctxt, xmlParserErrors error,
140
             const char *msg, const xmlChar *str1, const xmlChar *str2)
141
0
{
142
0
    xmlCtxtErr(ctxt, NULL, XML_FROM_HTML, error, XML_ERR_ERROR,
143
0
               str1, str2, NULL, 0, msg, str1, str2);
144
0
}
145
146
/************************************************************************
147
 *                  *
148
 *  Parser stacks related functions and macros    *
149
 *                  *
150
 ************************************************************************/
151
152
/**
153
 * htmlnamePush:
154
 * @ctxt:  an HTML parser context
155
 * @value:  the element name
156
 *
157
 * Pushes a new element name on top of the name stack
158
 *
159
 * Returns -1 in case of error, the index in the stack otherwise
160
 */
161
static int
162
htmlnamePush(htmlParserCtxtPtr ctxt, const xmlChar * value)
163
0
{
164
0
    if ((ctxt->html < INSERT_IN_HEAD) && (xmlStrEqual(value, BAD_CAST "head")))
165
0
        ctxt->html = INSERT_IN_HEAD;
166
0
    if ((ctxt->html < INSERT_IN_BODY) && (xmlStrEqual(value, BAD_CAST "body")))
167
0
        ctxt->html = INSERT_IN_BODY;
168
0
    if (ctxt->nameNr >= ctxt->nameMax) {
169
0
        const xmlChar **tmp;
170
0
        int newSize;
171
172
0
        newSize = xmlGrowCapacity(ctxt->nameMax, sizeof(tmp[0]),
173
0
                                  10, XML_MAX_ITEMS);
174
0
        if (newSize < 0) {
175
0
            htmlErrMemory(ctxt);
176
0
            return (-1);
177
0
        }
178
0
        tmp = xmlRealloc(ctxt->nameTab, newSize * sizeof(tmp[0]));
179
0
        if (tmp == NULL) {
180
0
            htmlErrMemory(ctxt);
181
0
            return(-1);
182
0
        }
183
0
        ctxt->nameTab = tmp;
184
0
        ctxt->nameMax = newSize;
185
0
    }
186
0
    ctxt->nameTab[ctxt->nameNr] = value;
187
0
    ctxt->name = value;
188
0
    return (ctxt->nameNr++);
189
0
}
190
/**
191
 * htmlnamePop:
192
 * @ctxt: an HTML parser context
193
 *
194
 * Pops the top element name from the name stack
195
 *
196
 * Returns the name just removed
197
 */
198
static const xmlChar *
199
htmlnamePop(htmlParserCtxtPtr ctxt)
200
0
{
201
0
    const xmlChar *ret;
202
203
0
    if (ctxt->nameNr <= 0)
204
0
        return (NULL);
205
0
    ctxt->nameNr--;
206
0
    if (ctxt->nameNr < 0)
207
0
        return (NULL);
208
0
    if (ctxt->nameNr > 0)
209
0
        ctxt->name = ctxt->nameTab[ctxt->nameNr - 1];
210
0
    else
211
0
        ctxt->name = NULL;
212
0
    ret = ctxt->nameTab[ctxt->nameNr];
213
0
    ctxt->nameTab[ctxt->nameNr] = NULL;
214
0
    return (ret);
215
0
}
216
217
/**
218
 * htmlNodeInfoPush:
219
 * @ctxt:  an HTML parser context
220
 * @value:  the node info
221
 *
222
 * Pushes a new element name on top of the node info stack
223
 *
224
 * Returns 0 in case of error, the index in the stack otherwise
225
 */
226
static int
227
htmlNodeInfoPush(htmlParserCtxtPtr ctxt, htmlParserNodeInfo *value)
228
0
{
229
0
    if (ctxt->nodeInfoNr >= ctxt->nodeInfoMax) {
230
0
        xmlParserNodeInfo *tmp;
231
0
        int newSize;
232
233
0
        newSize = xmlGrowCapacity(ctxt->nodeInfoMax, sizeof(tmp[0]),
234
0
                                  5, XML_MAX_ITEMS);
235
0
        if (newSize < 0) {
236
0
            htmlErrMemory(ctxt);
237
0
            return (0);
238
0
        }
239
0
        tmp = xmlRealloc(ctxt->nodeInfoTab, newSize * sizeof(tmp[0]));
240
0
        if (tmp == NULL) {
241
0
            htmlErrMemory(ctxt);
242
0
            return (0);
243
0
        }
244
0
        ctxt->nodeInfoTab = tmp;
245
0
        ctxt->nodeInfoMax = newSize;
246
0
    }
247
0
    ctxt->nodeInfoTab[ctxt->nodeInfoNr] = *value;
248
0
    ctxt->nodeInfo = &ctxt->nodeInfoTab[ctxt->nodeInfoNr];
249
0
    return (ctxt->nodeInfoNr++);
250
0
}
251
252
/**
253
 * htmlNodeInfoPop:
254
 * @ctxt:  an HTML parser context
255
 *
256
 * Pops the top element name from the node info stack
257
 *
258
 * Returns 0 in case of error, the pointer to NodeInfo otherwise
259
 */
260
static htmlParserNodeInfo *
261
htmlNodeInfoPop(htmlParserCtxtPtr ctxt)
262
0
{
263
0
    if (ctxt->nodeInfoNr <= 0)
264
0
        return (NULL);
265
0
    ctxt->nodeInfoNr--;
266
0
    if (ctxt->nodeInfoNr < 0)
267
0
        return (NULL);
268
0
    if (ctxt->nodeInfoNr > 0)
269
0
        ctxt->nodeInfo = &ctxt->nodeInfoTab[ctxt->nodeInfoNr - 1];
270
0
    else
271
0
        ctxt->nodeInfo = NULL;
272
0
    return &ctxt->nodeInfoTab[ctxt->nodeInfoNr];
273
0
}
274
275
/*
276
 * Macros for accessing the content. Those should be used only by the parser,
277
 * and not exported.
278
 *
279
 * Dirty macros, i.e. one need to make assumption on the context to use them
280
 *
281
 *   CUR_PTR return the current pointer to the xmlChar to be parsed.
282
 *   CUR     returns the current xmlChar value, i.e. a 8 bit value if compiled
283
 *           in ISO-Latin or UTF-8, and the current 16 bit value if compiled
284
 *           in UNICODE mode. This should be used internally by the parser
285
 *           only to compare to ASCII values otherwise it would break when
286
 *           running with UTF-8 encoding.
287
 *   NXT(n)  returns the n'th next xmlChar. Same as CUR is should be used only
288
 *           to compare on ASCII based substring.
289
 *   UPP(n)  returns the n'th next xmlChar converted to uppercase. Same as CUR
290
 *           it should be used only to compare on ASCII based substring.
291
 *   SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
292
 *           strings without newlines within the parser.
293
 *
294
 * Clean macros, not dependent of an ASCII context, expect UTF-8 encoding
295
 *
296
 *   COPY(to) copy one char to *to, increment CUR_PTR and to accordingly
297
 */
298
299
0
#define UPPER (toupper(*ctxt->input->cur))
300
301
0
#define SKIP(val) ctxt->input->cur += (val),ctxt->input->col+=(val)
302
303
0
#define NXT(val) ctxt->input->cur[(val)]
304
305
0
#define UPP(val) (toupper(ctxt->input->cur[(val)]))
306
307
0
#define CUR_PTR ctxt->input->cur
308
#define BASE_PTR ctxt->input->base
309
310
#define SHRINK \
311
0
    if ((!PARSER_PROGRESSIVE(ctxt)) && \
312
0
        (ctxt->input->cur - ctxt->input->base > 2 * INPUT_CHUNK) && \
313
0
  (ctxt->input->end - ctxt->input->cur < 2 * INPUT_CHUNK)) \
314
0
  xmlParserShrink(ctxt);
315
316
#define GROW \
317
0
    if ((!PARSER_PROGRESSIVE(ctxt)) && \
318
0
        (ctxt->input->end - ctxt->input->cur < INPUT_CHUNK)) \
319
0
  xmlParserGrow(ctxt);
320
321
0
#define SKIP_BLANKS htmlSkipBlankChars(ctxt)
322
323
/* Imported from XML */
324
325
0
#define CUR (*ctxt->input->cur)
326
327
/**
328
 * htmlFindEncoding:
329
 * @the HTML parser context
330
 *
331
 * Ty to find and encoding in the current data available in the input
332
 * buffer this is needed to try to switch to the proper encoding when
333
 * one face a character error.
334
 * That's an heuristic, since it's operating outside of parsing it could
335
 * try to use a meta which had been commented out, that's the reason it
336
 * should only be used in case of error, not as a default.
337
 *
338
 * Returns an encoding string or NULL if not found, the string need to
339
 *   be freed
340
 */
341
static xmlChar *
342
0
htmlFindEncoding(xmlParserCtxtPtr ctxt) {
343
0
    const xmlChar *start, *cur, *end;
344
0
    xmlChar *ret;
345
0
346
0
    if ((ctxt == NULL) || (ctxt->input == NULL) ||
347
0
        (ctxt->input->flags & XML_INPUT_HAS_ENCODING))
348
0
        return(NULL);
349
0
    if ((ctxt->input->cur == NULL) || (ctxt->input->end == NULL))
350
0
        return(NULL);
351
0
352
0
    start = ctxt->input->cur;
353
0
    end = ctxt->input->end;
354
0
    /* we also expect the input buffer to be zero terminated */
355
0
    if (*end != 0)
356
0
        return(NULL);
357
0
358
0
    cur = xmlStrcasestr(start, BAD_CAST "HTTP-EQUIV");
359
0
    if (cur == NULL)
360
0
        return(NULL);
361
0
    cur = xmlStrcasestr(cur, BAD_CAST  "CONTENT");
362
0
    if (cur == NULL)
363
0
        return(NULL);
364
0
    cur = xmlStrcasestr(cur, BAD_CAST  "CHARSET=");
365
0
    if (cur == NULL)
366
0
        return(NULL);
367
0
    cur += 8;
368
0
    start = cur;
369
0
    while ((IS_ALNUM(*cur)) ||
370
0
           (*cur == '-') || (*cur == '_') || (*cur == ':') || (*cur == '/'))
371
0
           cur++;
372
0
    if (cur == start)
373
0
        return(NULL);
374
0
    ret = xmlStrndup(start, cur - start);
375
0
    if (ret == NULL)
376
0
        htmlErrMemory(ctxt);
377
0
    return(ret);
378
0
}
379
380
static int
381
0
htmlMaskMatch(htmlAsciiMask mask, unsigned c) {
382
0
    if (c >= 64)
383
0
        return(0);
384
0
    return((mask[c/32] >> (c & 31)) & 1);
385
0
}
386
387
static int
388
htmlValidateUtf8(xmlParserCtxtPtr ctxt, const xmlChar *str, size_t len,
389
0
                 int partial) {
390
0
    unsigned c = str[0];
391
0
    int size;
392
393
0
    if (c < 0xC2) {
394
0
        goto invalid;
395
0
    } else if (c < 0xE0) {
396
0
        if (len < 2)
397
0
            goto incomplete;
398
0
        if ((str[1] & 0xC0) != 0x80)
399
0
            goto invalid;
400
0
        size = 2;
401
0
    } else if (c < 0xF0) {
402
0
        unsigned v;
403
404
0
        if (len < 3)
405
0
            goto incomplete;
406
407
0
        v = str[1] << 8 | str[2]; /* hint to generate 16-bit load */
408
0
        v |= c << 16;
409
410
0
        if (((v & 0x00C0C0) != 0x008080) ||
411
0
            ((v & 0x0F2000) == 0x000000) ||
412
0
            ((v & 0x0F2000) == 0x0D2000))
413
0
            goto invalid;
414
415
0
        size = 3;
416
0
    } else {
417
0
        unsigned v;
418
419
0
        if (len < 4)
420
0
            goto incomplete;
421
422
0
        v = c << 24 | str[1] << 16 | str[2] << 8 | str[3];
423
424
0
        if (((v & 0x00C0C0C0) != 0x00808080) ||
425
0
            (v < 0xF0900000) || (v >= 0xF4900000))
426
0
            goto invalid;
427
428
0
        size = 4;
429
0
    }
430
431
0
    return(size);
432
433
0
incomplete:
434
0
    if (partial)
435
0
        return(0);
436
437
0
invalid:
438
    /* Only report the first error */
439
0
    if ((ctxt->input->flags & XML_INPUT_ENCODING_ERROR) == 0) {
440
0
        htmlParseErr(ctxt, XML_ERR_INVALID_ENCODING,
441
0
                     "Invalid bytes in character encoding", NULL, NULL);
442
0
        ctxt->input->flags |= XML_INPUT_ENCODING_ERROR;
443
0
    }
444
445
0
    return(-1);
446
0
}
447
448
/**
449
 * htmlSkipBlankChars:
450
 * @ctxt:  the HTML parser context
451
 *
452
 * skip all blanks character found at that point in the input streams.
453
 *
454
 * Returns the number of space chars skipped
455
 */
456
457
static int
458
0
htmlSkipBlankChars(xmlParserCtxtPtr ctxt) {
459
0
    const xmlChar *cur = ctxt->input->cur;
460
0
    size_t avail = ctxt->input->end - cur;
461
0
    int res = 0;
462
0
    int line = ctxt->input->line;
463
0
    int col = ctxt->input->col;
464
465
0
    while (!PARSER_STOPPED(ctxt)) {
466
0
        if (avail == 0) {
467
0
            ctxt->input->cur = cur;
468
0
            GROW;
469
0
            cur = ctxt->input->cur;
470
0
            avail = ctxt->input->end - cur;
471
472
0
            if (avail == 0)
473
0
                break;
474
0
        }
475
476
0
        if (*cur == '\n') {
477
0
            line++;
478
0
            col = 1;
479
0
        } else if (IS_WS_HTML(*cur)) {
480
0
            col++;
481
0
        } else {
482
0
            break;
483
0
        }
484
485
0
        cur += 1;
486
0
        avail -= 1;
487
488
0
  if (res < INT_MAX)
489
0
      res++;
490
0
    }
491
492
0
    ctxt->input->cur = cur;
493
0
    ctxt->input->line = line;
494
0
    ctxt->input->col = col;
495
496
0
    if (res > 8)
497
0
        GROW;
498
499
0
    return(res);
500
0
}
501
502
503
504
/************************************************************************
505
 *                  *
506
 *  The list of HTML elements and their properties    *
507
 *                  *
508
 ************************************************************************/
509
510
/*
511
 *  Start Tag: 1 means the start tag can be omitted
512
 *  End Tag:   1 means the end tag can be omitted
513
 *             2 means it's forbidden (empty elements)
514
 *             3 means the tag is stylistic and should be closed easily
515
 *  Depr:      this element is deprecated
516
 *  DTD:       1 means that this element is valid only in the Loose DTD
517
 *             2 means that this element is valid only in the Frameset DTD
518
 *
519
 * Name,Start Tag,End Tag,Save End,Empty,Deprecated,DTD,inline,Description
520
 */
521
522
0
#define DATA_RCDATA         1
523
#define DATA_RAWTEXT        2
524
0
#define DATA_PLAINTEXT      3
525
0
#define DATA_SCRIPT         4
526
0
#define DATA_SCRIPT_ESC1    5
527
0
#define DATA_SCRIPT_ESC2    6
528
529
static const htmlElemDesc
530
html40ElementTable[] = {
531
{ "a",    0, 0, 0, 0, 0, 0, 1, "anchor ",
532
  NULL, NULL, NULL, NULL, NULL,
533
  0
534
},
535
{ "abbr", 0, 0, 0, 0, 0, 0, 1, "abbreviated form",
536
  NULL, NULL, NULL, NULL, NULL,
537
  0
538
},
539
{ "acronym",  0, 0, 0, 0, 0, 0, 1, "",
540
  NULL, NULL, NULL, NULL, NULL,
541
  0
542
},
543
{ "address",  0, 0, 0, 0, 0, 0, 0, "information on author ",
544
  NULL, NULL, NULL, NULL, NULL,
545
  0
546
},
547
{ "applet", 0, 0, 0, 0, 1, 1, 2, "java applet ",
548
  NULL, NULL, NULL, NULL, NULL,
549
  0
550
},
551
{ "area", 0, 2, 2, 1, 0, 0, 0, "client-side image map area ",
552
  NULL, NULL, NULL, NULL, NULL,
553
  0
554
},
555
{ "b",    0, 3, 0, 0, 0, 0, 1, "bold text style",
556
  NULL, NULL, NULL, NULL, NULL,
557
  0
558
},
559
{ "base", 0, 2, 2, 1, 0, 0, 0, "document base uri ",
560
  NULL, NULL, NULL, NULL, NULL,
561
  0
562
},
563
{ "basefont", 0, 2, 2, 1, 1, 1, 1, "base font size " ,
564
  NULL, NULL, NULL, NULL, NULL,
565
  0
566
},
567
{ "bdo",  0, 0, 0, 0, 0, 0, 1, "i18n bidi over-ride ",
568
  NULL, NULL, NULL, NULL, NULL,
569
  0
570
},
571
{ "big",  0, 3, 0, 0, 0, 0, 1, "large text style",
572
  NULL, NULL, NULL, NULL, NULL,
573
  0
574
},
575
{ "blockquote", 0, 0, 0, 0, 0, 0, 0, "long quotation ",
576
  NULL, NULL, NULL, NULL, NULL,
577
  0
578
},
579
{ "body", 1, 1, 0, 0, 0, 0, 0, "document body ",
580
  NULL, NULL, NULL, NULL, NULL,
581
  0
582
},
583
{ "br",   0, 2, 2, 1, 0, 0, 1, "forced line break ",
584
  NULL, NULL, NULL, NULL, NULL,
585
  0
586
},
587
{ "button", 0, 0, 0, 0, 0, 0, 2, "push button ",
588
  NULL, NULL, NULL, NULL, NULL,
589
  0
590
},
591
{ "caption",  0, 0, 0, 0, 0, 0, 0, "table caption ",
592
  NULL, NULL, NULL, NULL, NULL,
593
  0
594
},
595
{ "center", 0, 3, 0, 0, 1, 1, 0, "shorthand for div align=center ",
596
  NULL, NULL, NULL, NULL, NULL,
597
  0
598
},
599
{ "cite", 0, 0, 0, 0, 0, 0, 1, "citation",
600
  NULL, NULL, NULL, NULL, NULL,
601
  0
602
},
603
{ "code", 0, 0, 0, 0, 0, 0, 1, "computer code fragment",
604
  NULL, NULL, NULL, NULL, NULL,
605
  0
606
},
607
{ "col",  0, 2, 2, 1, 0, 0, 0, "table column ",
608
  NULL, NULL, NULL, NULL, NULL,
609
  0
610
},
611
{ "colgroup", 0, 1, 0, 0, 0, 0, 0, "table column group ",
612
  NULL, NULL, NULL, NULL, NULL,
613
  0
614
},
615
{ "dd",   0, 1, 0, 0, 0, 0, 0, "definition description ",
616
  NULL, NULL, NULL, NULL, NULL,
617
  0
618
},
619
{ "del",  0, 0, 0, 0, 0, 0, 2, "deleted text ",
620
  NULL, NULL, NULL, NULL, NULL,
621
  0
622
},
623
{ "dfn",  0, 0, 0, 0, 0, 0, 1, "instance definition",
624
  NULL, NULL, NULL, NULL, NULL,
625
  0
626
},
627
{ "dir",  0, 0, 0, 0, 1, 1, 0, "directory list",
628
  NULL, NULL, NULL, NULL, NULL,
629
  0
630
},
631
{ "div",  0, 0, 0, 0, 0, 0, 0, "generic language/style container",
632
  NULL, NULL, NULL, NULL, NULL,
633
  0
634
},
635
{ "dl",   0, 0, 0, 0, 0, 0, 0, "definition list ",
636
  NULL, NULL, NULL, NULL, NULL,
637
  0
638
},
639
{ "dt",   0, 1, 0, 0, 0, 0, 0, "definition term ",
640
  NULL, NULL, NULL, NULL, NULL,
641
  0
642
},
643
{ "em",   0, 3, 0, 0, 0, 0, 1, "emphasis",
644
  NULL, NULL, NULL, NULL, NULL,
645
  0
646
},
647
{ "embed",  0, 1, 0, 0, 1, 1, 1, "generic embedded object ",
648
  NULL, NULL, NULL, NULL, NULL,
649
  0
650
},
651
{ "fieldset", 0, 0, 0, 0, 0, 0, 0, "form control group ",
652
  NULL, NULL, NULL, NULL, NULL,
653
  0
654
},
655
{ "font", 0, 3, 0, 0, 1, 1, 1, "local change to font ",
656
  NULL, NULL, NULL, NULL, NULL,
657
  0
658
},
659
{ "form", 0, 0, 0, 0, 0, 0, 0, "interactive form ",
660
  NULL, NULL, NULL, NULL, NULL,
661
  0
662
},
663
{ "frame",  0, 2, 2, 1, 0, 2, 0, "subwindow " ,
664
  NULL, NULL, NULL, NULL, NULL,
665
  0
666
},
667
{ "frameset", 0, 0, 0, 0, 0, 2, 0, "window subdivision" ,
668
  NULL, NULL, NULL, NULL, NULL,
669
  0
670
},
671
{ "h1",   0, 0, 0, 0, 0, 0, 0, "heading ",
672
  NULL, NULL, NULL, NULL, NULL,
673
  0
674
},
675
{ "h2",   0, 0, 0, 0, 0, 0, 0, "heading ",
676
  NULL, NULL, NULL, NULL, NULL,
677
  0
678
},
679
{ "h3",   0, 0, 0, 0, 0, 0, 0, "heading ",
680
  NULL, NULL, NULL, NULL, NULL,
681
  0
682
},
683
{ "h4",   0, 0, 0, 0, 0, 0, 0, "heading ",
684
  NULL, NULL, NULL, NULL, NULL,
685
  0
686
},
687
{ "h5",   0, 0, 0, 0, 0, 0, 0, "heading ",
688
  NULL, NULL, NULL, NULL, NULL,
689
  0
690
},
691
{ "h6",   0, 0, 0, 0, 0, 0, 0, "heading ",
692
  NULL, NULL, NULL, NULL, NULL,
693
  0
694
},
695
{ "head", 1, 1, 0, 0, 0, 0, 0, "document head ",
696
  NULL, NULL, NULL, NULL, NULL,
697
  0
698
},
699
{ "hr",   0, 2, 2, 1, 0, 0, 0, "horizontal rule " ,
700
  NULL, NULL, NULL, NULL, NULL,
701
  0
702
},
703
{ "html", 1, 1, 0, 0, 0, 0, 0, "document root element ",
704
  NULL, NULL, NULL, NULL, NULL,
705
  0
706
},
707
{ "i",    0, 3, 0, 0, 0, 0, 1, "italic text style",
708
  NULL, NULL, NULL, NULL, NULL,
709
  0
710
},
711
{ "iframe", 0, 0, 0, 0, 0, 1, 2, "inline subwindow ",
712
  NULL, NULL, NULL, NULL, NULL,
713
  DATA_RAWTEXT
714
},
715
{ "img",  0, 2, 2, 1, 0, 0, 1, "embedded image ",
716
  NULL, NULL, NULL, NULL, NULL,
717
  0
718
},
719
{ "input",  0, 2, 2, 1, 0, 0, 1, "form control ",
720
  NULL, NULL, NULL, NULL, NULL,
721
  0
722
},
723
{ "ins",  0, 0, 0, 0, 0, 0, 2, "inserted text",
724
  NULL, NULL, NULL, NULL, NULL,
725
  0
726
},
727
{ "isindex",  0, 2, 2, 1, 1, 1, 0, "single line prompt ",
728
  NULL, NULL, NULL, NULL, NULL,
729
  0
730
},
731
{ "kbd",  0, 0, 0, 0, 0, 0, 1, "text to be entered by the user",
732
  NULL, NULL, NULL, NULL, NULL,
733
  0
734
},
735
{ "label",  0, 0, 0, 0, 0, 0, 1, "form field label text ",
736
  NULL, NULL, NULL, NULL, NULL,
737
  0
738
},
739
{ "legend", 0, 0, 0, 0, 0, 0, 0, "fieldset legend ",
740
  NULL, NULL, NULL, NULL, NULL,
741
  0
742
},
743
{ "li",   0, 1, 1, 0, 0, 0, 0, "list item ",
744
  NULL, NULL, NULL, NULL, NULL,
745
  0
746
},
747
{ "link", 0, 2, 2, 1, 0, 0, 0, "a media-independent link ",
748
  NULL, NULL, NULL, NULL, NULL,
749
  0
750
},
751
{ "map",  0, 0, 0, 0, 0, 0, 2, "client-side image map ",
752
  NULL, NULL, NULL, NULL, NULL,
753
  0
754
},
755
{ "menu", 0, 0, 0, 0, 1, 1, 0, "menu list ",
756
  NULL, NULL, NULL, NULL, NULL,
757
  0
758
},
759
{ "meta", 0, 2, 2, 1, 0, 0, 0, "generic metainformation ",
760
  NULL, NULL, NULL, NULL, NULL,
761
  0
762
},
763
{ "noembed",  0, 0, 0, 0, 0, 0, 0, "",
764
  NULL, NULL, NULL, NULL, NULL,
765
  DATA_RAWTEXT
766
},
767
{ "noframes", 0, 0, 0, 0, 0, 2, 0, "alternate content container for non frame-based rendering ",
768
  NULL, NULL, NULL, NULL, NULL,
769
  DATA_RAWTEXT
770
},
771
{ "noscript", 0, 0, 0, 0, 0, 0, 0, "alternate content container for non script-based rendering ",
772
  NULL, NULL, NULL, NULL, NULL,
773
  0
774
},
775
{ "object", 0, 0, 0, 0, 0, 0, 2, "generic embedded object ",
776
  NULL, NULL, NULL, NULL, NULL,
777
  0
778
},
779
{ "ol",   0, 0, 0, 0, 0, 0, 0, "ordered list ",
780
  NULL, NULL, NULL, NULL, NULL,
781
  0
782
},
783
{ "optgroup", 0, 0, 0, 0, 0, 0, 0, "option group ",
784
  NULL, NULL, NULL, NULL, NULL,
785
  0
786
},
787
{ "option", 0, 1, 0, 0, 0, 0, 0, "selectable choice " ,
788
  NULL, NULL, NULL, NULL, NULL,
789
  0
790
},
791
{ "p",    0, 1, 0, 0, 0, 0, 0, "paragraph ",
792
  NULL, NULL, NULL, NULL, NULL,
793
  0
794
},
795
{ "param",  0, 2, 2, 1, 0, 0, 0, "named property value ",
796
  NULL, NULL, NULL, NULL, NULL,
797
  0
798
},
799
{ "plaintext",  0, 0, 0, 0, 0, 0, 0, "",
800
  NULL, NULL, NULL, NULL, NULL,
801
  DATA_PLAINTEXT
802
},
803
{ "pre",  0, 0, 0, 0, 0, 0, 0, "preformatted text ",
804
  NULL, NULL, NULL, NULL, NULL,
805
  0
806
},
807
{ "q",    0, 0, 0, 0, 0, 0, 1, "short inline quotation ",
808
  NULL, NULL, NULL, NULL, NULL,
809
  0
810
},
811
{ "s",    0, 3, 0, 0, 1, 1, 1, "strike-through text style",
812
  NULL, NULL, NULL, NULL, NULL,
813
  0
814
},
815
{ "samp", 0, 0, 0, 0, 0, 0, 1, "sample program output, scripts, etc.",
816
  NULL, NULL, NULL, NULL, NULL,
817
  0
818
},
819
{ "script", 0, 0, 0, 0, 0, 0, 2, "script statements ",
820
  NULL, NULL, NULL, NULL, NULL,
821
  DATA_SCRIPT
822
},
823
{ "select", 0, 0, 0, 0, 0, 0, 1, "option selector ",
824
  NULL, NULL, NULL, NULL, NULL,
825
  0
826
},
827
{ "small",  0, 3, 0, 0, 0, 0, 1, "small text style",
828
  NULL, NULL, NULL, NULL, NULL,
829
  0
830
},
831
{ "span", 0, 0, 0, 0, 0, 0, 1, "generic language/style container ",
832
  NULL, NULL, NULL, NULL, NULL,
833
  0
834
},
835
{ "strike", 0, 3, 0, 0, 1, 1, 1, "strike-through text",
836
  NULL, NULL, NULL, NULL, NULL,
837
  0
838
},
839
{ "strong", 0, 3, 0, 0, 0, 0, 1, "strong emphasis",
840
  NULL, NULL, NULL, NULL, NULL,
841
  0
842
},
843
{ "style",  0, 0, 0, 0, 0, 0, 0, "style info ",
844
  NULL, NULL, NULL, NULL, NULL,
845
  DATA_RAWTEXT
846
},
847
{ "sub",  0, 3, 0, 0, 0, 0, 1, "subscript",
848
  NULL, NULL, NULL, NULL, NULL,
849
  0
850
},
851
{ "sup",  0, 3, 0, 0, 0, 0, 1, "superscript ",
852
  NULL, NULL, NULL, NULL, NULL,
853
  0
854
},
855
{ "table",  0, 0, 0, 0, 0, 0, 0, "",
856
  NULL, NULL, NULL, NULL, NULL,
857
  0
858
},
859
{ "tbody",  1, 0, 0, 0, 0, 0, 0, "table body ",
860
  NULL, NULL, NULL, NULL, NULL,
861
  0
862
},
863
{ "td",   0, 0, 0, 0, 0, 0, 0, "table data cell",
864
  NULL, NULL, NULL, NULL, NULL,
865
  0
866
},
867
{ "textarea", 0, 0, 0, 0, 0, 0, 1, "multi-line text field ",
868
  NULL, NULL, NULL, NULL, NULL,
869
  DATA_RCDATA
870
},
871
{ "tfoot",  0, 1, 0, 0, 0, 0, 0, "table footer ",
872
  NULL, NULL, NULL, NULL, NULL,
873
  0
874
},
875
{ "th",   0, 1, 0, 0, 0, 0, 0, "table header cell",
876
  NULL, NULL, NULL, NULL, NULL,
877
  0
878
},
879
{ "thead",  0, 1, 0, 0, 0, 0, 0, "table header ",
880
  NULL, NULL, NULL, NULL, NULL,
881
  0
882
},
883
{ "title",  0, 0, 0, 0, 0, 0, 0, "document title ",
884
  NULL, NULL, NULL, NULL, NULL,
885
  DATA_RCDATA
886
},
887
{ "tr",   0, 0, 0, 0, 0, 0, 0, "table row ",
888
  NULL, NULL, NULL, NULL, NULL,
889
  0
890
},
891
{ "tt",   0, 3, 0, 0, 0, 0, 1, "teletype or monospaced text style",
892
  NULL, NULL, NULL, NULL, NULL,
893
  0
894
},
895
{ "u",    0, 3, 0, 0, 1, 1, 1, "underlined text style",
896
  NULL, NULL, NULL, NULL, NULL,
897
  0
898
},
899
{ "ul",   0, 0, 0, 0, 0, 0, 0, "unordered list ",
900
  NULL, NULL, NULL, NULL, NULL,
901
  0
902
},
903
{ "var",  0, 0, 0, 0, 0, 0, 1, "instance of a variable or program argument",
904
  NULL, NULL, NULL, NULL, NULL,
905
  0
906
},
907
{ "xmp",  0, 0, 0, 0, 0, 0, 1, "",
908
  NULL, NULL, NULL, NULL, NULL,
909
  DATA_RAWTEXT
910
}
911
};
912
913
typedef struct {
914
    const char *oldTag;
915
    const char *newTag;
916
} htmlStartCloseEntry;
917
918
/*
919
 * start tags that imply the end of current element
920
 */
921
static const htmlStartCloseEntry htmlStartClose[] = {
922
    { "a", "a" },
923
    { "a", "fieldset" },
924
    { "a", "table" },
925
    { "a", "td" },
926
    { "a", "th" },
927
    { "address", "dd" },
928
    { "address", "dl" },
929
    { "address", "dt" },
930
    { "address", "form" },
931
    { "address", "li" },
932
    { "address", "ul" },
933
    { "b", "center" },
934
    { "b", "p" },
935
    { "b", "td" },
936
    { "b", "th" },
937
    { "big", "p" },
938
    { "caption", "col" },
939
    { "caption", "colgroup" },
940
    { "caption", "tbody" },
941
    { "caption", "tfoot" },
942
    { "caption", "thead" },
943
    { "caption", "tr" },
944
    { "col", "col" },
945
    { "col", "colgroup" },
946
    { "col", "tbody" },
947
    { "col", "tfoot" },
948
    { "col", "thead" },
949
    { "col", "tr" },
950
    { "colgroup", "colgroup" },
951
    { "colgroup", "tbody" },
952
    { "colgroup", "tfoot" },
953
    { "colgroup", "thead" },
954
    { "colgroup", "tr" },
955
    { "dd", "dt" },
956
    { "dir", "dd" },
957
    { "dir", "dl" },
958
    { "dir", "dt" },
959
    { "dir", "form" },
960
    { "dir", "ul" },
961
    { "dl", "form" },
962
    { "dl", "li" },
963
    { "dt", "dd" },
964
    { "dt", "dl" },
965
    { "font", "center" },
966
    { "font", "td" },
967
    { "font", "th" },
968
    { "form", "form" },
969
    { "h1", "fieldset" },
970
    { "h1", "form" },
971
    { "h1", "li" },
972
    { "h1", "p" },
973
    { "h1", "table" },
974
    { "h2", "fieldset" },
975
    { "h2", "form" },
976
    { "h2", "li" },
977
    { "h2", "p" },
978
    { "h2", "table" },
979
    { "h3", "fieldset" },
980
    { "h3", "form" },
981
    { "h3", "li" },
982
    { "h3", "p" },
983
    { "h3", "table" },
984
    { "h4", "fieldset" },
985
    { "h4", "form" },
986
    { "h4", "li" },
987
    { "h4", "p" },
988
    { "h4", "table" },
989
    { "h5", "fieldset" },
990
    { "h5", "form" },
991
    { "h5", "li" },
992
    { "h5", "p" },
993
    { "h5", "table" },
994
    { "h6", "fieldset" },
995
    { "h6", "form" },
996
    { "h6", "li" },
997
    { "h6", "p" },
998
    { "h6", "table" },
999
    { "head", "a" },
1000
    { "head", "abbr" },
1001
    { "head", "acronym" },
1002
    { "head", "address" },
1003
    { "head", "b" },
1004
    { "head", "bdo" },
1005
    { "head", "big" },
1006
    { "head", "blockquote" },
1007
    { "head", "body" },
1008
    { "head", "br" },
1009
    { "head", "center" },
1010
    { "head", "cite" },
1011
    { "head", "code" },
1012
    { "head", "dd" },
1013
    { "head", "dfn" },
1014
    { "head", "dir" },
1015
    { "head", "div" },
1016
    { "head", "dl" },
1017
    { "head", "dt" },
1018
    { "head", "em" },
1019
    { "head", "fieldset" },
1020
    { "head", "font" },
1021
    { "head", "form" },
1022
    { "head", "frameset" },
1023
    { "head", "h1" },
1024
    { "head", "h2" },
1025
    { "head", "h3" },
1026
    { "head", "h4" },
1027
    { "head", "h5" },
1028
    { "head", "h6" },
1029
    { "head", "hr" },
1030
    { "head", "i" },
1031
    { "head", "iframe" },
1032
    { "head", "img" },
1033
    { "head", "kbd" },
1034
    { "head", "li" },
1035
    { "head", "listing" },
1036
    { "head", "map" },
1037
    { "head", "menu" },
1038
    { "head", "ol" },
1039
    { "head", "p" },
1040
    { "head", "pre" },
1041
    { "head", "q" },
1042
    { "head", "s" },
1043
    { "head", "samp" },
1044
    { "head", "small" },
1045
    { "head", "span" },
1046
    { "head", "strike" },
1047
    { "head", "strong" },
1048
    { "head", "sub" },
1049
    { "head", "sup" },
1050
    { "head", "table" },
1051
    { "head", "tt" },
1052
    { "head", "u" },
1053
    { "head", "ul" },
1054
    { "head", "var" },
1055
    { "head", "xmp" },
1056
    { "hr", "form" },
1057
    { "i", "center" },
1058
    { "i", "p" },
1059
    { "i", "td" },
1060
    { "i", "th" },
1061
    { "legend", "fieldset" },
1062
    { "li", "li" },
1063
    { "link", "body" },
1064
    { "link", "frameset" },
1065
    { "listing", "dd" },
1066
    { "listing", "dl" },
1067
    { "listing", "dt" },
1068
    { "listing", "fieldset" },
1069
    { "listing", "form" },
1070
    { "listing", "li" },
1071
    { "listing", "table" },
1072
    { "listing", "ul" },
1073
    { "menu", "dd" },
1074
    { "menu", "dl" },
1075
    { "menu", "dt" },
1076
    { "menu", "form" },
1077
    { "menu", "ul" },
1078
    { "ol", "form" },
1079
    { "option", "optgroup" },
1080
    { "option", "option" },
1081
    { "p", "address" },
1082
    { "p", "blockquote" },
1083
    { "p", "body" },
1084
    { "p", "caption" },
1085
    { "p", "center" },
1086
    { "p", "col" },
1087
    { "p", "colgroup" },
1088
    { "p", "dd" },
1089
    { "p", "dir" },
1090
    { "p", "div" },
1091
    { "p", "dl" },
1092
    { "p", "dt" },
1093
    { "p", "fieldset" },
1094
    { "p", "form" },
1095
    { "p", "frameset" },
1096
    { "p", "h1" },
1097
    { "p", "h2" },
1098
    { "p", "h3" },
1099
    { "p", "h4" },
1100
    { "p", "h5" },
1101
    { "p", "h6" },
1102
    { "p", "head" },
1103
    { "p", "hr" },
1104
    { "p", "li" },
1105
    { "p", "listing" },
1106
    { "p", "menu" },
1107
    { "p", "ol" },
1108
    { "p", "p" },
1109
    { "p", "pre" },
1110
    { "p", "table" },
1111
    { "p", "tbody" },
1112
    { "p", "td" },
1113
    { "p", "tfoot" },
1114
    { "p", "th" },
1115
    { "p", "title" },
1116
    { "p", "tr" },
1117
    { "p", "ul" },
1118
    { "p", "xmp" },
1119
    { "pre", "dd" },
1120
    { "pre", "dl" },
1121
    { "pre", "dt" },
1122
    { "pre", "fieldset" },
1123
    { "pre", "form" },
1124
    { "pre", "li" },
1125
    { "pre", "table" },
1126
    { "pre", "ul" },
1127
    { "s", "p" },
1128
    { "script", "noscript" },
1129
    { "small", "p" },
1130
    { "span", "td" },
1131
    { "span", "th" },
1132
    { "strike", "p" },
1133
    { "style", "body" },
1134
    { "style", "frameset" },
1135
    { "tbody", "tbody" },
1136
    { "tbody", "tfoot" },
1137
    { "td", "tbody" },
1138
    { "td", "td" },
1139
    { "td", "tfoot" },
1140
    { "td", "th" },
1141
    { "td", "tr" },
1142
    { "tfoot", "tbody" },
1143
    { "th", "tbody" },
1144
    { "th", "td" },
1145
    { "th", "tfoot" },
1146
    { "th", "th" },
1147
    { "th", "tr" },
1148
    { "thead", "tbody" },
1149
    { "thead", "tfoot" },
1150
    { "title", "body" },
1151
    { "title", "frameset" },
1152
    { "tr", "tbody" },
1153
    { "tr", "tfoot" },
1154
    { "tr", "tr" },
1155
    { "tt", "p" },
1156
    { "u", "p" },
1157
    { "u", "td" },
1158
    { "u", "th" },
1159
    { "ul", "address" },
1160
    { "ul", "form" },
1161
    { "ul", "menu" },
1162
    { "ul", "pre" },
1163
    { "xmp", "dd" },
1164
    { "xmp", "dl" },
1165
    { "xmp", "dt" },
1166
    { "xmp", "fieldset" },
1167
    { "xmp", "form" },
1168
    { "xmp", "li" },
1169
    { "xmp", "table" },
1170
    { "xmp", "ul" }
1171
};
1172
1173
/*
1174
 * The list of HTML attributes which are of content %Script;
1175
 * NOTE: when adding ones, check htmlIsScriptAttribute() since
1176
 *       it assumes the name starts with 'on'
1177
 */
1178
static const char *const htmlScriptAttributes[] = {
1179
    "onclick",
1180
    "ondblclick",
1181
    "onmousedown",
1182
    "onmouseup",
1183
    "onmouseover",
1184
    "onmousemove",
1185
    "onmouseout",
1186
    "onkeypress",
1187
    "onkeydown",
1188
    "onkeyup",
1189
    "onload",
1190
    "onunload",
1191
    "onfocus",
1192
    "onblur",
1193
    "onsubmit",
1194
    "onreset",
1195
    "onchange",
1196
    "onselect"
1197
};
1198
1199
/*
1200
 * This table is used by the htmlparser to know what to do with
1201
 * broken html pages. By assigning different priorities to different
1202
 * elements the parser can decide how to handle extra endtags.
1203
 * Endtags are only allowed to close elements with lower or equal
1204
 * priority.
1205
 */
1206
1207
typedef struct {
1208
    const char *name;
1209
    int priority;
1210
} elementPriority;
1211
1212
static const elementPriority htmlEndPriority[] = {
1213
    {"div",   150},
1214
    {"td",    160},
1215
    {"th",    160},
1216
    {"tr",    170},
1217
    {"thead", 180},
1218
    {"tbody", 180},
1219
    {"tfoot", 180},
1220
    {"table", 190},
1221
    {"head",  200},
1222
    {"body",  200},
1223
    {"html",  220},
1224
    {NULL,    100} /* Default priority */
1225
};
1226
1227
/************************************************************************
1228
 *                  *
1229
 *  functions to handle HTML specific data      *
1230
 *                  *
1231
 ************************************************************************/
1232
1233
static void
1234
0
htmlParserFinishElementParsing(htmlParserCtxtPtr ctxt) {
1235
    /*
1236
     * Capture end position and add node
1237
     */
1238
0
    if ( ctxt->node != NULL && ctxt->record_info ) {
1239
0
       ctxt->nodeInfo->end_pos = ctxt->input->consumed +
1240
0
                                (CUR_PTR - ctxt->input->base);
1241
0
       ctxt->nodeInfo->end_line = ctxt->input->line;
1242
0
       ctxt->nodeInfo->node = ctxt->node;
1243
0
       xmlParserAddNodeInfo(ctxt, ctxt->nodeInfo);
1244
0
       htmlNodeInfoPop(ctxt);
1245
0
    }
1246
0
}
1247
1248
/**
1249
 * htmlInitAutoClose:
1250
 *
1251
 * DEPRECATED: This is a no-op.
1252
 */
1253
void
1254
0
htmlInitAutoClose(void) {
1255
0
}
1256
1257
static int
1258
0
htmlCompareTags(const void *key, const void *member) {
1259
0
    const xmlChar *tag = (const xmlChar *) key;
1260
0
    const htmlElemDesc *desc = (const htmlElemDesc *) member;
1261
1262
0
    return(xmlStrcasecmp(tag, BAD_CAST desc->name));
1263
0
}
1264
1265
/**
1266
 * htmlTagLookup:
1267
 * @tag:  The tag name in lowercase
1268
 *
1269
 * Lookup the HTML tag in the ElementTable
1270
 *
1271
 * Returns the related htmlElemDescPtr or NULL if not found.
1272
 */
1273
const htmlElemDesc *
1274
0
htmlTagLookup(const xmlChar *tag) {
1275
0
    if (tag == NULL)
1276
0
        return(NULL);
1277
1278
0
    return((const htmlElemDesc *) bsearch(tag, html40ElementTable,
1279
0
                sizeof(html40ElementTable) / sizeof(htmlElemDesc),
1280
0
                sizeof(htmlElemDesc), htmlCompareTags));
1281
0
}
1282
1283
/**
1284
 * htmlGetEndPriority:
1285
 * @name: The name of the element to look up the priority for.
1286
 *
1287
 * Return value: The "endtag" priority.
1288
 **/
1289
static int
1290
0
htmlGetEndPriority (const xmlChar *name) {
1291
0
    int i = 0;
1292
1293
0
    while ((htmlEndPriority[i].name != NULL) &&
1294
0
     (!xmlStrEqual((const xmlChar *)htmlEndPriority[i].name, name)))
1295
0
  i++;
1296
1297
0
    return(htmlEndPriority[i].priority);
1298
0
}
1299
1300
1301
static int
1302
0
htmlCompareStartClose(const void *vkey, const void *member) {
1303
0
    const htmlStartCloseEntry *key = (const htmlStartCloseEntry *) vkey;
1304
0
    const htmlStartCloseEntry *entry = (const htmlStartCloseEntry *) member;
1305
0
    int ret;
1306
1307
0
    ret = strcmp(key->oldTag, entry->oldTag);
1308
0
    if (ret == 0)
1309
0
        ret = strcmp(key->newTag, entry->newTag);
1310
1311
0
    return(ret);
1312
0
}
1313
1314
/**
1315
 * htmlCheckAutoClose:
1316
 * @newtag:  The new tag name
1317
 * @oldtag:  The old tag name
1318
 *
1319
 * Checks whether the new tag is one of the registered valid tags for
1320
 * closing old.
1321
 *
1322
 * Returns 0 if no, 1 if yes.
1323
 */
1324
static int
1325
htmlCheckAutoClose(const xmlChar * newtag, const xmlChar * oldtag)
1326
0
{
1327
0
    htmlStartCloseEntry key;
1328
0
    void *res;
1329
1330
0
    key.oldTag = (const char *) oldtag;
1331
0
    key.newTag = (const char *) newtag;
1332
0
    res = bsearch(&key, htmlStartClose,
1333
0
            sizeof(htmlStartClose) / sizeof(htmlStartCloseEntry),
1334
0
            sizeof(htmlStartCloseEntry), htmlCompareStartClose);
1335
0
    return(res != NULL);
1336
0
}
1337
1338
/**
1339
 * htmlAutoCloseOnClose:
1340
 * @ctxt:  an HTML parser context
1341
 * @newtag:  The new tag name
1342
 * @force:  force the tag closure
1343
 *
1344
 * The HTML DTD allows an ending tag to implicitly close other tags.
1345
 */
1346
static void
1347
htmlAutoCloseOnClose(htmlParserCtxtPtr ctxt, const xmlChar * newtag)
1348
0
{
1349
0
    const htmlElemDesc *info;
1350
0
    int i, priority;
1351
1352
0
    if (ctxt->options & HTML_PARSE_HTML5)
1353
0
        return;
1354
1355
0
    priority = htmlGetEndPriority(newtag);
1356
1357
0
    for (i = (ctxt->nameNr - 1); i >= 0; i--) {
1358
1359
0
        if (xmlStrEqual(newtag, ctxt->nameTab[i]))
1360
0
            break;
1361
        /*
1362
         * A misplaced endtag can only close elements with lower
1363
         * or equal priority, so if we find an element with higher
1364
         * priority before we find an element with
1365
         * matching name, we just ignore this endtag
1366
         */
1367
0
        if (htmlGetEndPriority(ctxt->nameTab[i]) > priority)
1368
0
            return;
1369
0
    }
1370
0
    if (i < 0)
1371
0
        return;
1372
1373
0
    while (!xmlStrEqual(newtag, ctxt->name)) {
1374
0
        info = htmlTagLookup(ctxt->name);
1375
0
        if ((info != NULL) && (info->endTag == 3)) {
1376
0
            htmlParseErr(ctxt, XML_ERR_TAG_NAME_MISMATCH,
1377
0
                   "Opening and ending tag mismatch: %s and %s\n",
1378
0
       newtag, ctxt->name);
1379
0
        }
1380
0
  htmlParserFinishElementParsing(ctxt);
1381
0
        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1382
0
            ctxt->sax->endElement(ctxt->userData, ctxt->name);
1383
0
  htmlnamePop(ctxt);
1384
0
    }
1385
0
}
1386
1387
/**
1388
 * htmlAutoCloseOnEnd:
1389
 * @ctxt:  an HTML parser context
1390
 *
1391
 * Close all remaining tags at the end of the stream
1392
 */
1393
static void
1394
htmlAutoCloseOnEnd(htmlParserCtxtPtr ctxt)
1395
0
{
1396
0
    int i;
1397
1398
0
    if (ctxt->options & HTML_PARSE_HTML5)
1399
0
        return;
1400
1401
0
    if (ctxt->nameNr == 0)
1402
0
        return;
1403
0
    for (i = (ctxt->nameNr - 1); i >= 0; i--) {
1404
0
  htmlParserFinishElementParsing(ctxt);
1405
0
        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1406
0
            ctxt->sax->endElement(ctxt->userData, ctxt->name);
1407
0
  htmlnamePop(ctxt);
1408
0
    }
1409
0
}
1410
1411
/**
1412
 * htmlAutoClose:
1413
 * @ctxt:  an HTML parser context
1414
 * @newtag:  The new tag name or NULL
1415
 *
1416
 * The HTML DTD allows a tag to implicitly close other tags.
1417
 * The list is kept in htmlStartClose array. This function is
1418
 * called when a new tag has been detected and generates the
1419
 * appropriates closes if possible/needed.
1420
 * If newtag is NULL this mean we are at the end of the resource
1421
 * and we should check
1422
 */
1423
static void
1424
htmlAutoClose(htmlParserCtxtPtr ctxt, const xmlChar * newtag)
1425
0
{
1426
0
    if (ctxt->options & HTML_PARSE_HTML5)
1427
0
        return;
1428
1429
0
    if (newtag == NULL)
1430
0
        return;
1431
1432
0
    while ((ctxt->name != NULL) &&
1433
0
           (htmlCheckAutoClose(newtag, ctxt->name))) {
1434
0
  htmlParserFinishElementParsing(ctxt);
1435
0
        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
1436
0
            ctxt->sax->endElement(ctxt->userData, ctxt->name);
1437
0
  htmlnamePop(ctxt);
1438
0
    }
1439
0
}
1440
1441
/**
1442
 * htmlAutoCloseTag:
1443
 * @doc:  the HTML document
1444
 * @name:  The tag name
1445
 * @elem:  the HTML element
1446
 *
1447
 * DEPRECATED: Internal function, don't use.
1448
 *
1449
 * The HTML DTD allows a tag to implicitly close other tags.
1450
 * The list is kept in htmlStartClose array. This function checks
1451
 * if the element or one of it's children would autoclose the
1452
 * given tag.
1453
 *
1454
 * Returns 1 if autoclose, 0 otherwise
1455
 */
1456
int
1457
0
htmlAutoCloseTag(htmlDocPtr doc, const xmlChar *name, htmlNodePtr elem) {
1458
0
    htmlNodePtr child;
1459
1460
0
    if (elem == NULL) return(1);
1461
0
    if (xmlStrEqual(name, elem->name)) return(0);
1462
0
    if (htmlCheckAutoClose(elem->name, name)) return(1);
1463
0
    child = elem->children;
1464
0
    while (child != NULL) {
1465
0
        if (htmlAutoCloseTag(doc, name, child)) return(1);
1466
0
  child = child->next;
1467
0
    }
1468
0
    return(0);
1469
0
}
1470
1471
/**
1472
 * htmlIsAutoClosed:
1473
 * @doc:  the HTML document
1474
 * @elem:  the HTML element
1475
 *
1476
 * DEPRECATED: Internal function, don't use.
1477
 *
1478
 * The HTML DTD allows a tag to implicitly close other tags.
1479
 * The list is kept in htmlStartClose array. This function checks
1480
 * if a tag is autoclosed by one of it's child
1481
 *
1482
 * Returns 1 if autoclosed, 0 otherwise
1483
 */
1484
int
1485
0
htmlIsAutoClosed(htmlDocPtr doc, htmlNodePtr elem) {
1486
0
    htmlNodePtr child;
1487
1488
0
    if (elem == NULL) return(1);
1489
0
    child = elem->children;
1490
0
    while (child != NULL) {
1491
0
  if (htmlAutoCloseTag(doc, elem->name, child)) return(1);
1492
0
  child = child->next;
1493
0
    }
1494
0
    return(0);
1495
0
}
1496
1497
/**
1498
 * htmlCheckImplied:
1499
 * @ctxt:  an HTML parser context
1500
 * @newtag:  The new tag name
1501
 *
1502
 * The HTML DTD allows a tag to exists only implicitly
1503
 * called when a new tag has been detected and generates the
1504
 * appropriates implicit tags if missing
1505
 */
1506
static void
1507
0
htmlCheckImplied(htmlParserCtxtPtr ctxt, const xmlChar *newtag) {
1508
0
    int i;
1509
1510
0
    if (ctxt->options & (HTML_PARSE_NOIMPLIED | HTML_PARSE_HTML5))
1511
0
        return;
1512
0
    if (!htmlOmittedDefaultValue)
1513
0
  return;
1514
0
    if (xmlStrEqual(newtag, BAD_CAST"html"))
1515
0
  return;
1516
0
    if (ctxt->nameNr <= 0) {
1517
0
  htmlnamePush(ctxt, BAD_CAST"html");
1518
0
  if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1519
0
      ctxt->sax->startElement(ctxt->userData, BAD_CAST"html", NULL);
1520
0
    }
1521
0
    if ((xmlStrEqual(newtag, BAD_CAST"body")) || (xmlStrEqual(newtag, BAD_CAST"head")))
1522
0
        return;
1523
0
    if ((ctxt->nameNr <= 1) &&
1524
0
        ((xmlStrEqual(newtag, BAD_CAST"script")) ||
1525
0
   (xmlStrEqual(newtag, BAD_CAST"style")) ||
1526
0
   (xmlStrEqual(newtag, BAD_CAST"meta")) ||
1527
0
   (xmlStrEqual(newtag, BAD_CAST"link")) ||
1528
0
   (xmlStrEqual(newtag, BAD_CAST"title")) ||
1529
0
   (xmlStrEqual(newtag, BAD_CAST"base")))) {
1530
0
        if (ctxt->html >= INSERT_IN_HEAD) {
1531
            /* we already saw or generated an <head> before */
1532
0
            return;
1533
0
        }
1534
        /*
1535
         * dropped OBJECT ... i you put it first BODY will be
1536
         * assumed !
1537
         */
1538
0
        htmlnamePush(ctxt, BAD_CAST"head");
1539
0
        if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1540
0
            ctxt->sax->startElement(ctxt->userData, BAD_CAST"head", NULL);
1541
0
    } else if ((!xmlStrEqual(newtag, BAD_CAST"noframes")) &&
1542
0
         (!xmlStrEqual(newtag, BAD_CAST"frame")) &&
1543
0
         (!xmlStrEqual(newtag, BAD_CAST"frameset"))) {
1544
0
        if (ctxt->html >= INSERT_IN_BODY) {
1545
            /* we already saw or generated a <body> before */
1546
0
            return;
1547
0
        }
1548
0
  for (i = 0;i < ctxt->nameNr;i++) {
1549
0
      if (xmlStrEqual(ctxt->nameTab[i], BAD_CAST"body")) {
1550
0
    return;
1551
0
      }
1552
0
      if (xmlStrEqual(ctxt->nameTab[i], BAD_CAST"head")) {
1553
0
    return;
1554
0
      }
1555
0
  }
1556
1557
0
  htmlnamePush(ctxt, BAD_CAST"body");
1558
0
  if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL))
1559
0
      ctxt->sax->startElement(ctxt->userData, BAD_CAST"body", NULL);
1560
0
    }
1561
0
}
1562
1563
/**
1564
 * htmlStartCharData
1565
 * @ctxt:  an HTML parser context
1566
 *
1567
 * Prepare for non-whitespace character data.
1568
 */
1569
1570
static void
1571
0
htmlStartCharData(htmlParserCtxtPtr ctxt) {
1572
0
    if (ctxt->options & (HTML_PARSE_NOIMPLIED | HTML_PARSE_HTML5))
1573
0
        return;
1574
0
    if (!htmlOmittedDefaultValue)
1575
0
  return;
1576
1577
0
    if (xmlStrEqual(ctxt->name, BAD_CAST "head"))
1578
0
        htmlAutoClose(ctxt, BAD_CAST "p");
1579
0
    htmlCheckImplied(ctxt, BAD_CAST "p");
1580
0
}
1581
1582
/**
1583
 * htmlIsScriptAttribute:
1584
 * @name:  an attribute name
1585
 *
1586
 * Check if an attribute is of content type Script
1587
 *
1588
 * Returns 1 is the attribute is a script 0 otherwise
1589
 */
1590
int
1591
0
htmlIsScriptAttribute(const xmlChar *name) {
1592
0
    unsigned int i;
1593
1594
0
    if (name == NULL)
1595
0
      return(0);
1596
    /*
1597
     * all script attributes start with 'on'
1598
     */
1599
0
    if ((name[0] != 'o') || (name[1] != 'n'))
1600
0
      return(0);
1601
0
    for (i = 0;
1602
0
   i < sizeof(htmlScriptAttributes)/sizeof(htmlScriptAttributes[0]);
1603
0
   i++) {
1604
0
  if (xmlStrEqual(name, (const xmlChar *) htmlScriptAttributes[i]))
1605
0
      return(1);
1606
0
    }
1607
0
    return(0);
1608
0
}
1609
1610
/************************************************************************
1611
 *                  *
1612
 *  The list of HTML predefined entities      *
1613
 *                  *
1614
 ************************************************************************/
1615
1616
1617
static const htmlEntityDesc  html40EntitiesTable[] = {
1618
/*
1619
 * the 4 absolute ones, plus apostrophe.
1620
 */
1621
{ 34, "quot", "quotation mark = APL quote, U+0022 ISOnum" },
1622
{ 38, "amp",  "ampersand, U+0026 ISOnum" },
1623
{ 39, "apos", "single quote" },
1624
{ 60, "lt", "less-than sign, U+003C ISOnum" },
1625
{ 62, "gt", "greater-than sign, U+003E ISOnum" },
1626
1627
/*
1628
 * A bunch still in the 128-255 range
1629
 * Replacing them depend really on the charset used.
1630
 */
1631
{ 160,  "nbsp", "no-break space = non-breaking space, U+00A0 ISOnum" },
1632
{ 161,  "iexcl","inverted exclamation mark, U+00A1 ISOnum" },
1633
{ 162,  "cent", "cent sign, U+00A2 ISOnum" },
1634
{ 163,  "pound","pound sign, U+00A3 ISOnum" },
1635
{ 164,  "curren","currency sign, U+00A4 ISOnum" },
1636
{ 165,  "yen",  "yen sign = yuan sign, U+00A5 ISOnum" },
1637
{ 166,  "brvbar","broken bar = broken vertical bar, U+00A6 ISOnum" },
1638
{ 167,  "sect", "section sign, U+00A7 ISOnum" },
1639
{ 168,  "uml",  "diaeresis = spacing diaeresis, U+00A8 ISOdia" },
1640
{ 169,  "copy", "copyright sign, U+00A9 ISOnum" },
1641
{ 170,  "ordf", "feminine ordinal indicator, U+00AA ISOnum" },
1642
{ 171,  "laquo","left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum" },
1643
{ 172,  "not",  "not sign, U+00AC ISOnum" },
1644
{ 173,  "shy",  "soft hyphen = discretionary hyphen, U+00AD ISOnum" },
1645
{ 174,  "reg",  "registered sign = registered trade mark sign, U+00AE ISOnum" },
1646
{ 175,  "macr", "macron = spacing macron = overline = APL overbar, U+00AF ISOdia" },
1647
{ 176,  "deg",  "degree sign, U+00B0 ISOnum" },
1648
{ 177,  "plusmn","plus-minus sign = plus-or-minus sign, U+00B1 ISOnum" },
1649
{ 178,  "sup2", "superscript two = superscript digit two = squared, U+00B2 ISOnum" },
1650
{ 179,  "sup3", "superscript three = superscript digit three = cubed, U+00B3 ISOnum" },
1651
{ 180,  "acute","acute accent = spacing acute, U+00B4 ISOdia" },
1652
{ 181,  "micro","micro sign, U+00B5 ISOnum" },
1653
{ 182,  "para", "pilcrow sign = paragraph sign, U+00B6 ISOnum" },
1654
{ 183,  "middot","middle dot = Georgian comma Greek middle dot, U+00B7 ISOnum" },
1655
{ 184,  "cedil","cedilla = spacing cedilla, U+00B8 ISOdia" },
1656
{ 185,  "sup1", "superscript one = superscript digit one, U+00B9 ISOnum" },
1657
{ 186,  "ordm", "masculine ordinal indicator, U+00BA ISOnum" },
1658
{ 187,  "raquo","right-pointing double angle quotation mark right pointing guillemet, U+00BB ISOnum" },
1659
{ 188,  "frac14","vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum" },
1660
{ 189,  "frac12","vulgar fraction one half = fraction one half, U+00BD ISOnum" },
1661
{ 190,  "frac34","vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum" },
1662
{ 191,  "iquest","inverted question mark = turned question mark, U+00BF ISOnum" },
1663
{ 192,  "Agrave","latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1" },
1664
{ 193,  "Aacute","latin capital letter A with acute, U+00C1 ISOlat1" },
1665
{ 194,  "Acirc","latin capital letter A with circumflex, U+00C2 ISOlat1" },
1666
{ 195,  "Atilde","latin capital letter A with tilde, U+00C3 ISOlat1" },
1667
{ 196,  "Auml", "latin capital letter A with diaeresis, U+00C4 ISOlat1" },
1668
{ 197,  "Aring","latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1" },
1669
{ 198,  "AElig","latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1" },
1670
{ 199,  "Ccedil","latin capital letter C with cedilla, U+00C7 ISOlat1" },
1671
{ 200,  "Egrave","latin capital letter E with grave, U+00C8 ISOlat1" },
1672
{ 201,  "Eacute","latin capital letter E with acute, U+00C9 ISOlat1" },
1673
{ 202,  "Ecirc","latin capital letter E with circumflex, U+00CA ISOlat1" },
1674
{ 203,  "Euml", "latin capital letter E with diaeresis, U+00CB ISOlat1" },
1675
{ 204,  "Igrave","latin capital letter I with grave, U+00CC ISOlat1" },
1676
{ 205,  "Iacute","latin capital letter I with acute, U+00CD ISOlat1" },
1677
{ 206,  "Icirc","latin capital letter I with circumflex, U+00CE ISOlat1" },
1678
{ 207,  "Iuml", "latin capital letter I with diaeresis, U+00CF ISOlat1" },
1679
{ 208,  "ETH",  "latin capital letter ETH, U+00D0 ISOlat1" },
1680
{ 209,  "Ntilde","latin capital letter N with tilde, U+00D1 ISOlat1" },
1681
{ 210,  "Ograve","latin capital letter O with grave, U+00D2 ISOlat1" },
1682
{ 211,  "Oacute","latin capital letter O with acute, U+00D3 ISOlat1" },
1683
{ 212,  "Ocirc","latin capital letter O with circumflex, U+00D4 ISOlat1" },
1684
{ 213,  "Otilde","latin capital letter O with tilde, U+00D5 ISOlat1" },
1685
{ 214,  "Ouml", "latin capital letter O with diaeresis, U+00D6 ISOlat1" },
1686
{ 215,  "times","multiplication sign, U+00D7 ISOnum" },
1687
{ 216,  "Oslash","latin capital letter O with stroke latin capital letter O slash, U+00D8 ISOlat1" },
1688
{ 217,  "Ugrave","latin capital letter U with grave, U+00D9 ISOlat1" },
1689
{ 218,  "Uacute","latin capital letter U with acute, U+00DA ISOlat1" },
1690
{ 219,  "Ucirc","latin capital letter U with circumflex, U+00DB ISOlat1" },
1691
{ 220,  "Uuml", "latin capital letter U with diaeresis, U+00DC ISOlat1" },
1692
{ 221,  "Yacute","latin capital letter Y with acute, U+00DD ISOlat1" },
1693
{ 222,  "THORN","latin capital letter THORN, U+00DE ISOlat1" },
1694
{ 223,  "szlig","latin small letter sharp s = ess-zed, U+00DF ISOlat1" },
1695
{ 224,  "agrave","latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1" },
1696
{ 225,  "aacute","latin small letter a with acute, U+00E1 ISOlat1" },
1697
{ 226,  "acirc","latin small letter a with circumflex, U+00E2 ISOlat1" },
1698
{ 227,  "atilde","latin small letter a with tilde, U+00E3 ISOlat1" },
1699
{ 228,  "auml", "latin small letter a with diaeresis, U+00E4 ISOlat1" },
1700
{ 229,  "aring","latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1" },
1701
{ 230,  "aelig","latin small letter ae = latin small ligature ae, U+00E6 ISOlat1" },
1702
{ 231,  "ccedil","latin small letter c with cedilla, U+00E7 ISOlat1" },
1703
{ 232,  "egrave","latin small letter e with grave, U+00E8 ISOlat1" },
1704
{ 233,  "eacute","latin small letter e with acute, U+00E9 ISOlat1" },
1705
{ 234,  "ecirc","latin small letter e with circumflex, U+00EA ISOlat1" },
1706
{ 235,  "euml", "latin small letter e with diaeresis, U+00EB ISOlat1" },
1707
{ 236,  "igrave","latin small letter i with grave, U+00EC ISOlat1" },
1708
{ 237,  "iacute","latin small letter i with acute, U+00ED ISOlat1" },
1709
{ 238,  "icirc","latin small letter i with circumflex, U+00EE ISOlat1" },
1710
{ 239,  "iuml", "latin small letter i with diaeresis, U+00EF ISOlat1" },
1711
{ 240,  "eth",  "latin small letter eth, U+00F0 ISOlat1" },
1712
{ 241,  "ntilde","latin small letter n with tilde, U+00F1 ISOlat1" },
1713
{ 242,  "ograve","latin small letter o with grave, U+00F2 ISOlat1" },
1714
{ 243,  "oacute","latin small letter o with acute, U+00F3 ISOlat1" },
1715
{ 244,  "ocirc","latin small letter o with circumflex, U+00F4 ISOlat1" },
1716
{ 245,  "otilde","latin small letter o with tilde, U+00F5 ISOlat1" },
1717
{ 246,  "ouml", "latin small letter o with diaeresis, U+00F6 ISOlat1" },
1718
{ 247,  "divide","division sign, U+00F7 ISOnum" },
1719
{ 248,  "oslash","latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1" },
1720
{ 249,  "ugrave","latin small letter u with grave, U+00F9 ISOlat1" },
1721
{ 250,  "uacute","latin small letter u with acute, U+00FA ISOlat1" },
1722
{ 251,  "ucirc","latin small letter u with circumflex, U+00FB ISOlat1" },
1723
{ 252,  "uuml", "latin small letter u with diaeresis, U+00FC ISOlat1" },
1724
{ 253,  "yacute","latin small letter y with acute, U+00FD ISOlat1" },
1725
{ 254,  "thorn","latin small letter thorn with, U+00FE ISOlat1" },
1726
{ 255,  "yuml", "latin small letter y with diaeresis, U+00FF ISOlat1" },
1727
1728
{ 338,  "OElig","latin capital ligature OE, U+0152 ISOlat2" },
1729
{ 339,  "oelig","latin small ligature oe, U+0153 ISOlat2" },
1730
{ 352,  "Scaron","latin capital letter S with caron, U+0160 ISOlat2" },
1731
{ 353,  "scaron","latin small letter s with caron, U+0161 ISOlat2" },
1732
{ 376,  "Yuml", "latin capital letter Y with diaeresis, U+0178 ISOlat2" },
1733
1734
/*
1735
 * Anything below should really be kept as entities references
1736
 */
1737
{ 402,  "fnof", "latin small f with hook = function = florin, U+0192 ISOtech" },
1738
1739
{ 710,  "circ", "modifier letter circumflex accent, U+02C6 ISOpub" },
1740
{ 732,  "tilde","small tilde, U+02DC ISOdia" },
1741
1742
{ 913,  "Alpha","greek capital letter alpha, U+0391" },
1743
{ 914,  "Beta", "greek capital letter beta, U+0392" },
1744
{ 915,  "Gamma","greek capital letter gamma, U+0393 ISOgrk3" },
1745
{ 916,  "Delta","greek capital letter delta, U+0394 ISOgrk3" },
1746
{ 917,  "Epsilon","greek capital letter epsilon, U+0395" },
1747
{ 918,  "Zeta", "greek capital letter zeta, U+0396" },
1748
{ 919,  "Eta",  "greek capital letter eta, U+0397" },
1749
{ 920,  "Theta","greek capital letter theta, U+0398 ISOgrk3" },
1750
{ 921,  "Iota", "greek capital letter iota, U+0399" },
1751
{ 922,  "Kappa","greek capital letter kappa, U+039A" },
1752
{ 923,  "Lambda", "greek capital letter lambda, U+039B ISOgrk3" },
1753
{ 924,  "Mu", "greek capital letter mu, U+039C" },
1754
{ 925,  "Nu", "greek capital letter nu, U+039D" },
1755
{ 926,  "Xi", "greek capital letter xi, U+039E ISOgrk3" },
1756
{ 927,  "Omicron","greek capital letter omicron, U+039F" },
1757
{ 928,  "Pi", "greek capital letter pi, U+03A0 ISOgrk3" },
1758
{ 929,  "Rho",  "greek capital letter rho, U+03A1" },
1759
{ 931,  "Sigma","greek capital letter sigma, U+03A3 ISOgrk3" },
1760
{ 932,  "Tau",  "greek capital letter tau, U+03A4" },
1761
{ 933,  "Upsilon","greek capital letter upsilon, U+03A5 ISOgrk3" },
1762
{ 934,  "Phi",  "greek capital letter phi, U+03A6 ISOgrk3" },
1763
{ 935,  "Chi",  "greek capital letter chi, U+03A7" },
1764
{ 936,  "Psi",  "greek capital letter psi, U+03A8 ISOgrk3" },
1765
{ 937,  "Omega","greek capital letter omega, U+03A9 ISOgrk3" },
1766
1767
{ 945,  "alpha","greek small letter alpha, U+03B1 ISOgrk3" },
1768
{ 946,  "beta", "greek small letter beta, U+03B2 ISOgrk3" },
1769
{ 947,  "gamma","greek small letter gamma, U+03B3 ISOgrk3" },
1770
{ 948,  "delta","greek small letter delta, U+03B4 ISOgrk3" },
1771
{ 949,  "epsilon","greek small letter epsilon, U+03B5 ISOgrk3" },
1772
{ 950,  "zeta", "greek small letter zeta, U+03B6 ISOgrk3" },
1773
{ 951,  "eta",  "greek small letter eta, U+03B7 ISOgrk3" },
1774
{ 952,  "theta","greek small letter theta, U+03B8 ISOgrk3" },
1775
{ 953,  "iota", "greek small letter iota, U+03B9 ISOgrk3" },
1776
{ 954,  "kappa","greek small letter kappa, U+03BA ISOgrk3" },
1777
{ 955,  "lambda","greek small letter lambda, U+03BB ISOgrk3" },
1778
{ 956,  "mu", "greek small letter mu, U+03BC ISOgrk3" },
1779
{ 957,  "nu", "greek small letter nu, U+03BD ISOgrk3" },
1780
{ 958,  "xi", "greek small letter xi, U+03BE ISOgrk3" },
1781
{ 959,  "omicron","greek small letter omicron, U+03BF NEW" },
1782
{ 960,  "pi", "greek small letter pi, U+03C0 ISOgrk3" },
1783
{ 961,  "rho",  "greek small letter rho, U+03C1 ISOgrk3" },
1784
{ 962,  "sigmaf","greek small letter final sigma, U+03C2 ISOgrk3" },
1785
{ 963,  "sigma","greek small letter sigma, U+03C3 ISOgrk3" },
1786
{ 964,  "tau",  "greek small letter tau, U+03C4 ISOgrk3" },
1787
{ 965,  "upsilon","greek small letter upsilon, U+03C5 ISOgrk3" },
1788
{ 966,  "phi",  "greek small letter phi, U+03C6 ISOgrk3" },
1789
{ 967,  "chi",  "greek small letter chi, U+03C7 ISOgrk3" },
1790
{ 968,  "psi",  "greek small letter psi, U+03C8 ISOgrk3" },
1791
{ 969,  "omega","greek small letter omega, U+03C9 ISOgrk3" },
1792
{ 977,  "thetasym","greek small letter theta symbol, U+03D1 NEW" },
1793
{ 978,  "upsih","greek upsilon with hook symbol, U+03D2 NEW" },
1794
{ 982,  "piv",  "greek pi symbol, U+03D6 ISOgrk3" },
1795
1796
{ 8194, "ensp", "en space, U+2002 ISOpub" },
1797
{ 8195, "emsp", "em space, U+2003 ISOpub" },
1798
{ 8201, "thinsp","thin space, U+2009 ISOpub" },
1799
{ 8204, "zwnj", "zero width non-joiner, U+200C NEW RFC 2070" },
1800
{ 8205, "zwj",  "zero width joiner, U+200D NEW RFC 2070" },
1801
{ 8206, "lrm",  "left-to-right mark, U+200E NEW RFC 2070" },
1802
{ 8207, "rlm",  "right-to-left mark, U+200F NEW RFC 2070" },
1803
{ 8211, "ndash","en dash, U+2013 ISOpub" },
1804
{ 8212, "mdash","em dash, U+2014 ISOpub" },
1805
{ 8216, "lsquo","left single quotation mark, U+2018 ISOnum" },
1806
{ 8217, "rsquo","right single quotation mark, U+2019 ISOnum" },
1807
{ 8218, "sbquo","single low-9 quotation mark, U+201A NEW" },
1808
{ 8220, "ldquo","left double quotation mark, U+201C ISOnum" },
1809
{ 8221, "rdquo","right double quotation mark, U+201D ISOnum" },
1810
{ 8222, "bdquo","double low-9 quotation mark, U+201E NEW" },
1811
{ 8224, "dagger","dagger, U+2020 ISOpub" },
1812
{ 8225, "Dagger","double dagger, U+2021 ISOpub" },
1813
1814
{ 8226, "bull", "bullet = black small circle, U+2022 ISOpub" },
1815
{ 8230, "hellip","horizontal ellipsis = three dot leader, U+2026 ISOpub" },
1816
1817
{ 8240, "permil","per mille sign, U+2030 ISOtech" },
1818
1819
{ 8242, "prime","prime = minutes = feet, U+2032 ISOtech" },
1820
{ 8243, "Prime","double prime = seconds = inches, U+2033 ISOtech" },
1821
1822
{ 8249, "lsaquo","single left-pointing angle quotation mark, U+2039 ISO proposed" },
1823
{ 8250, "rsaquo","single right-pointing angle quotation mark, U+203A ISO proposed" },
1824
1825
{ 8254, "oline","overline = spacing overscore, U+203E NEW" },
1826
{ 8260, "frasl","fraction slash, U+2044 NEW" },
1827
1828
{ 8364, "euro", "euro sign, U+20AC NEW" },
1829
1830
{ 8465, "image","blackletter capital I = imaginary part, U+2111 ISOamso" },
1831
{ 8472, "weierp","script capital P = power set = Weierstrass p, U+2118 ISOamso" },
1832
{ 8476, "real", "blackletter capital R = real part symbol, U+211C ISOamso" },
1833
{ 8482, "trade","trade mark sign, U+2122 ISOnum" },
1834
{ 8501, "alefsym","alef symbol = first transfinite cardinal, U+2135 NEW" },
1835
{ 8592, "larr", "leftwards arrow, U+2190 ISOnum" },
1836
{ 8593, "uarr", "upwards arrow, U+2191 ISOnum" },
1837
{ 8594, "rarr", "rightwards arrow, U+2192 ISOnum" },
1838
{ 8595, "darr", "downwards arrow, U+2193 ISOnum" },
1839
{ 8596, "harr", "left right arrow, U+2194 ISOamsa" },
1840
{ 8629, "crarr","downwards arrow with corner leftwards = carriage return, U+21B5 NEW" },
1841
{ 8656, "lArr", "leftwards double arrow, U+21D0 ISOtech" },
1842
{ 8657, "uArr", "upwards double arrow, U+21D1 ISOamsa" },
1843
{ 8658, "rArr", "rightwards double arrow, U+21D2 ISOtech" },
1844
{ 8659, "dArr", "downwards double arrow, U+21D3 ISOamsa" },
1845
{ 8660, "hArr", "left right double arrow, U+21D4 ISOamsa" },
1846
1847
{ 8704, "forall","for all, U+2200 ISOtech" },
1848
{ 8706, "part", "partial differential, U+2202 ISOtech" },
1849
{ 8707, "exist","there exists, U+2203 ISOtech" },
1850
{ 8709, "empty","empty set = null set = diameter, U+2205 ISOamso" },
1851
{ 8711, "nabla","nabla = backward difference, U+2207 ISOtech" },
1852
{ 8712, "isin", "element of, U+2208 ISOtech" },
1853
{ 8713, "notin","not an element of, U+2209 ISOtech" },
1854
{ 8715, "ni", "contains as member, U+220B ISOtech" },
1855
{ 8719, "prod", "n-ary product = product sign, U+220F ISOamsb" },
1856
{ 8721, "sum",  "n-ary summation, U+2211 ISOamsb" },
1857
{ 8722, "minus","minus sign, U+2212 ISOtech" },
1858
{ 8727, "lowast","asterisk operator, U+2217 ISOtech" },
1859
{ 8730, "radic","square root = radical sign, U+221A ISOtech" },
1860
{ 8733, "prop", "proportional to, U+221D ISOtech" },
1861
{ 8734, "infin","infinity, U+221E ISOtech" },
1862
{ 8736, "ang",  "angle, U+2220 ISOamso" },
1863
{ 8743, "and",  "logical and = wedge, U+2227 ISOtech" },
1864
{ 8744, "or", "logical or = vee, U+2228 ISOtech" },
1865
{ 8745, "cap",  "intersection = cap, U+2229 ISOtech" },
1866
{ 8746, "cup",  "union = cup, U+222A ISOtech" },
1867
{ 8747, "int",  "integral, U+222B ISOtech" },
1868
{ 8756, "there4","therefore, U+2234 ISOtech" },
1869
{ 8764, "sim",  "tilde operator = varies with = similar to, U+223C ISOtech" },
1870
{ 8773, "cong", "approximately equal to, U+2245 ISOtech" },
1871
{ 8776, "asymp","almost equal to = asymptotic to, U+2248 ISOamsr" },
1872
{ 8800, "ne", "not equal to, U+2260 ISOtech" },
1873
{ 8801, "equiv","identical to, U+2261 ISOtech" },
1874
{ 8804, "le", "less-than or equal to, U+2264 ISOtech" },
1875
{ 8805, "ge", "greater-than or equal to, U+2265 ISOtech" },
1876
{ 8834, "sub",  "subset of, U+2282 ISOtech" },
1877
{ 8835, "sup",  "superset of, U+2283 ISOtech" },
1878
{ 8836, "nsub", "not a subset of, U+2284 ISOamsn" },
1879
{ 8838, "sube", "subset of or equal to, U+2286 ISOtech" },
1880
{ 8839, "supe", "superset of or equal to, U+2287 ISOtech" },
1881
{ 8853, "oplus","circled plus = direct sum, U+2295 ISOamsb" },
1882
{ 8855, "otimes","circled times = vector product, U+2297 ISOamsb" },
1883
{ 8869, "perp", "up tack = orthogonal to = perpendicular, U+22A5 ISOtech" },
1884
{ 8901, "sdot", "dot operator, U+22C5 ISOamsb" },
1885
{ 8968, "lceil","left ceiling = apl upstile, U+2308 ISOamsc" },
1886
{ 8969, "rceil","right ceiling, U+2309 ISOamsc" },
1887
{ 8970, "lfloor","left floor = apl downstile, U+230A ISOamsc" },
1888
{ 8971, "rfloor","right floor, U+230B ISOamsc" },
1889
{ 9001, "lang", "left-pointing angle bracket = bra, U+2329 ISOtech" },
1890
{ 9002, "rang", "right-pointing angle bracket = ket, U+232A ISOtech" },
1891
{ 9674, "loz",  "lozenge, U+25CA ISOpub" },
1892
1893
{ 9824, "spades","black spade suit, U+2660 ISOpub" },
1894
{ 9827, "clubs","black club suit = shamrock, U+2663 ISOpub" },
1895
{ 9829, "hearts","black heart suit = valentine, U+2665 ISOpub" },
1896
{ 9830, "diams","black diamond suit, U+2666 ISOpub" },
1897
1898
};
1899
1900
/************************************************************************
1901
 *                  *
1902
 *    Commodity functions to handle entities      *
1903
 *                  *
1904
 ************************************************************************/
1905
1906
/**
1907
 * htmlEntityLookup:
1908
 * @name: the entity name
1909
 *
1910
 * Lookup the given entity in EntitiesTable
1911
 *
1912
 * TODO: the linear scan is really ugly, an hash table is really needed.
1913
 *
1914
 * Returns the associated htmlEntityDescPtr if found, NULL otherwise.
1915
 */
1916
const htmlEntityDesc *
1917
0
htmlEntityLookup(const xmlChar *name) {
1918
0
    unsigned int i;
1919
1920
0
    for (i = 0;i < (sizeof(html40EntitiesTable)/
1921
0
                    sizeof(html40EntitiesTable[0]));i++) {
1922
0
        if (xmlStrEqual(name, BAD_CAST html40EntitiesTable[i].name)) {
1923
0
            return((htmlEntityDescPtr) &html40EntitiesTable[i]);
1924
0
  }
1925
0
    }
1926
0
    return(NULL);
1927
0
}
1928
1929
static int
1930
0
htmlCompareEntityDesc(const void *vkey, const void *vdesc) {
1931
0
    const unsigned *key = vkey;
1932
0
    const htmlEntityDesc *desc = vdesc;
1933
1934
0
    return((int) *key - (int) desc->value);
1935
0
}
1936
1937
/**
1938
 * htmlEntityValueLookup:
1939
 * @value: the entity's unicode value
1940
 *
1941
 * Lookup the given entity in EntitiesTable
1942
 *
1943
 * TODO: the linear scan is really ugly, an hash table is really needed.
1944
 *
1945
 * Returns the associated htmlEntityDescPtr if found, NULL otherwise.
1946
 */
1947
const htmlEntityDesc *
1948
0
htmlEntityValueLookup(unsigned int value) {
1949
0
    const htmlEntityDesc *desc;
1950
0
    size_t nmemb;
1951
1952
0
    nmemb = sizeof(html40EntitiesTable) / sizeof(html40EntitiesTable[0]);
1953
0
    desc = bsearch(&value, html40EntitiesTable, nmemb, sizeof(htmlEntityDesc),
1954
0
                   htmlCompareEntityDesc);
1955
1956
0
    return(desc);
1957
0
}
1958
1959
/**
1960
 * htmlUTF8ToHtml:
1961
 * @out:  a pointer to an array of bytes to store the result
1962
 * @outlen:  the length of @out
1963
 * @in:  a pointer to an array of UTF-8 chars
1964
 * @inlen:  the length of @in
1965
 *
1966
 * Take a block of UTF-8 chars in and try to convert it to an ASCII
1967
 * plus HTML entities block of chars out.
1968
 *
1969
 * Returns 0 if success, -2 if the transcoding fails, or -1 otherwise
1970
 * The value of @inlen after return is the number of octets consumed
1971
 *     as the return value is positive, else unpredictable.
1972
 * The value of @outlen after return is the number of octets consumed.
1973
 */
1974
int
1975
htmlUTF8ToHtml(unsigned char* out, int *outlen,
1976
0
               const unsigned char* in, int *inlen) {
1977
0
    const unsigned char* instart = in;
1978
0
    const unsigned char* inend;
1979
0
    unsigned char* outstart = out;
1980
0
    unsigned char* outend;
1981
0
    int ret = XML_ENC_ERR_SPACE;
1982
1983
0
    if ((out == NULL) || (outlen == NULL) || (inlen == NULL))
1984
0
        return(XML_ENC_ERR_INTERNAL);
1985
1986
0
    if (in == NULL) {
1987
        /*
1988
   * initialization nothing to do
1989
   */
1990
0
  *outlen = 0;
1991
0
  *inlen = 0;
1992
0
  return(XML_ENC_ERR_SUCCESS);
1993
0
    }
1994
1995
0
    inend = in + *inlen;
1996
0
    outend = out + *outlen;
1997
0
    while (in < inend) {
1998
0
        const htmlEntityDesc *ent;
1999
0
        const char *cp;
2000
0
        char nbuf[16];
2001
0
        unsigned c, d;
2002
0
        int seqlen, len, i;
2003
2004
0
  d = *in;
2005
2006
0
  if (d < 0x80) {
2007
0
            if (out >= outend)
2008
0
                goto done;
2009
0
            *out++ = d;
2010
0
            in += 1;
2011
0
            continue;
2012
0
        }
2013
2014
0
        if (d < 0xE0)      { c = d & 0x1F; seqlen = 2; }
2015
0
        else if (d < 0xF0) { c = d & 0x0F; seqlen = 3; }
2016
0
        else               { c = d & 0x07; seqlen = 4; }
2017
2018
0
  if (inend - in < seqlen)
2019
0
      break;
2020
2021
0
  for (i = 1; i < seqlen; i++) {
2022
0
      d = in[i];
2023
0
      c <<= 6;
2024
0
      c |= d & 0x3F;
2025
0
  }
2026
2027
        /*
2028
         * Try to lookup a predefined HTML entity for it
2029
         */
2030
0
        ent = htmlEntityValueLookup(c);
2031
2032
0
        if (ent == NULL) {
2033
0
          snprintf(nbuf, sizeof(nbuf), "#%u", c);
2034
0
          cp = nbuf;
2035
0
        } else {
2036
0
          cp = ent->name;
2037
0
        }
2038
2039
0
        len = strlen(cp);
2040
0
        if (outend - out < len + 2)
2041
0
            goto done;
2042
2043
0
        *out++ = '&';
2044
0
        memcpy(out, cp, len);
2045
0
        out += len;
2046
0
        *out++ = ';';
2047
2048
0
        in += seqlen;
2049
0
    }
2050
2051
0
    ret = out - outstart;
2052
2053
0
done:
2054
0
    *outlen = out - outstart;
2055
0
    *inlen = in - instart;
2056
0
    return(ret);
2057
0
}
2058
2059
/**
2060
 * htmlEncodeEntities:
2061
 * @out:  a pointer to an array of bytes to store the result
2062
 * @outlen:  the length of @out
2063
 * @in:  a pointer to an array of UTF-8 chars
2064
 * @inlen:  the length of @in
2065
 * @quoteChar: the quote character to escape (' or ") or zero.
2066
 *
2067
 * Take a block of UTF-8 chars in and try to convert it to an ASCII
2068
 * plus HTML entities block of chars out.
2069
 *
2070
 * Returns 0 if success, -2 if the transcoding fails, or -1 otherwise
2071
 * The value of @inlen after return is the number of octets consumed
2072
 *     as the return value is positive, else unpredictable.
2073
 * The value of @outlen after return is the number of octets consumed.
2074
 */
2075
int
2076
htmlEncodeEntities(unsigned char* out, int *outlen,
2077
0
       const unsigned char* in, int *inlen, int quoteChar) {
2078
0
    const unsigned char* processed = in;
2079
0
    const unsigned char* outend;
2080
0
    const unsigned char* outstart = out;
2081
0
    const unsigned char* instart = in;
2082
0
    const unsigned char* inend;
2083
0
    unsigned int c, d;
2084
0
    int trailing;
2085
2086
0
    if ((out == NULL) || (outlen == NULL) || (inlen == NULL) || (in == NULL))
2087
0
        return(-1);
2088
0
    outend = out + (*outlen);
2089
0
    inend = in + (*inlen);
2090
0
    while (in < inend) {
2091
0
  d = *in++;
2092
0
  if      (d < 0x80)  { c= d; trailing= 0; }
2093
0
  else if (d < 0xC0) {
2094
      /* trailing byte in leading position */
2095
0
      *outlen = out - outstart;
2096
0
      *inlen = processed - instart;
2097
0
      return(-2);
2098
0
        } else if (d < 0xE0)  { c= d & 0x1F; trailing= 1; }
2099
0
        else if (d < 0xF0)  { c= d & 0x0F; trailing= 2; }
2100
0
        else if (d < 0xF8)  { c= d & 0x07; trailing= 3; }
2101
0
  else {
2102
      /* no chance for this in Ascii */
2103
0
      *outlen = out - outstart;
2104
0
      *inlen = processed - instart;
2105
0
      return(-2);
2106
0
  }
2107
2108
0
  if (inend - in < trailing)
2109
0
      break;
2110
2111
0
  while (trailing--) {
2112
0
      if (((d= *in++) & 0xC0) != 0x80) {
2113
0
    *outlen = out - outstart;
2114
0
    *inlen = processed - instart;
2115
0
    return(-2);
2116
0
      }
2117
0
      c <<= 6;
2118
0
      c |= d & 0x3F;
2119
0
  }
2120
2121
  /* assertion: c is a single UTF-4 value */
2122
0
  if ((c < 0x80) && (c != (unsigned int) quoteChar) &&
2123
0
      (c != '&') && (c != '<') && (c != '>')) {
2124
0
      if (out >= outend)
2125
0
    break;
2126
0
      *out++ = c;
2127
0
  } else {
2128
0
      const htmlEntityDesc * ent;
2129
0
      const char *cp;
2130
0
      char nbuf[16];
2131
0
      int len;
2132
2133
      /*
2134
       * Try to lookup a predefined HTML entity for it
2135
       */
2136
0
      ent = htmlEntityValueLookup(c);
2137
0
      if (ent == NULL) {
2138
0
    snprintf(nbuf, sizeof(nbuf), "#%u", c);
2139
0
    cp = nbuf;
2140
0
      }
2141
0
      else
2142
0
    cp = ent->name;
2143
0
      len = strlen(cp);
2144
0
      if (outend - out < len + 2)
2145
0
    break;
2146
0
      *out++ = '&';
2147
0
      memcpy(out, cp, len);
2148
0
      out += len;
2149
0
      *out++ = ';';
2150
0
  }
2151
0
  processed = in;
2152
0
    }
2153
0
    *outlen = out - outstart;
2154
0
    *inlen = processed - instart;
2155
0
    return(0);
2156
0
}
2157
2158
/************************************************************************
2159
 *                  *
2160
 *    Commodity functions, cleanup needed ?     *
2161
 *                  *
2162
 ************************************************************************/
2163
/*
2164
 * all tags allowing pc data from the html 4.01 loose dtd
2165
 * NOTE: it might be more appropriate to integrate this information
2166
 * into the html40ElementTable array but I don't want to risk any
2167
 * binary incompatibility
2168
 */
2169
static const char *allowPCData[] = {
2170
    "a", "abbr", "acronym", "address", "applet", "b", "bdo", "big",
2171
    "blockquote", "body", "button", "caption", "center", "cite", "code",
2172
    "dd", "del", "dfn", "div", "dt", "em", "font", "form", "h1", "h2",
2173
    "h3", "h4", "h5", "h6", "i", "iframe", "ins", "kbd", "label", "legend",
2174
    "li", "noframes", "noscript", "object", "p", "pre", "q", "s", "samp",
2175
    "small", "span", "strike", "strong", "td", "th", "tt", "u", "var"
2176
};
2177
2178
/**
2179
 * areBlanks:
2180
 * @ctxt:  an HTML parser context
2181
 * @str:  a xmlChar *
2182
 * @len:  the size of @str
2183
 *
2184
 * Is this a sequence of blank chars that one can ignore ?
2185
 *
2186
 * Returns 1 if ignorable 0 if whitespace, -1 otherwise.
2187
 */
2188
2189
0
static int areBlanks(htmlParserCtxtPtr ctxt, const xmlChar *str, int len) {
2190
0
    unsigned int i;
2191
0
    int j;
2192
0
    xmlNodePtr lastChild;
2193
0
    xmlDtdPtr dtd;
2194
2195
0
    for (j = 0;j < len;j++)
2196
0
        if (!(IS_WS_HTML(str[j]))) return(-1);
2197
2198
0
    if (CUR == 0) return(1);
2199
0
    if (CUR != '<') return(0);
2200
0
    if (ctxt->name == NULL)
2201
0
  return(1);
2202
0
    if (xmlStrEqual(ctxt->name, BAD_CAST"html"))
2203
0
  return(1);
2204
0
    if (xmlStrEqual(ctxt->name, BAD_CAST"head"))
2205
0
  return(1);
2206
2207
    /* Only strip CDATA children of the body tag for strict HTML DTDs */
2208
0
    if (xmlStrEqual(ctxt->name, BAD_CAST "body") && ctxt->myDoc != NULL) {
2209
0
        dtd = xmlGetIntSubset(ctxt->myDoc);
2210
0
        if (dtd != NULL && dtd->ExternalID != NULL) {
2211
0
            if (!xmlStrcasecmp(dtd->ExternalID, BAD_CAST "-//W3C//DTD HTML 4.01//EN") ||
2212
0
                    !xmlStrcasecmp(dtd->ExternalID, BAD_CAST "-//W3C//DTD HTML 4//EN"))
2213
0
                return(1);
2214
0
        }
2215
0
    }
2216
2217
0
    if (ctxt->node == NULL) return(0);
2218
0
    lastChild = xmlGetLastChild(ctxt->node);
2219
0
    while ((lastChild) && (lastChild->type == XML_COMMENT_NODE))
2220
0
  lastChild = lastChild->prev;
2221
0
    if (lastChild == NULL) {
2222
0
        if ((ctxt->node->type != XML_ELEMENT_NODE) &&
2223
0
            (ctxt->node->content != NULL)) return(0);
2224
  /* keep ws in constructs like ...<b> </b>...
2225
     for all tags "b" allowing PCDATA */
2226
0
  for ( i = 0; i < sizeof(allowPCData)/sizeof(allowPCData[0]); i++ ) {
2227
0
      if ( xmlStrEqual(ctxt->name, BAD_CAST allowPCData[i]) ) {
2228
0
    return(0);
2229
0
      }
2230
0
  }
2231
0
    } else if (xmlNodeIsText(lastChild)) {
2232
0
        return(0);
2233
0
    } else {
2234
  /* keep ws in constructs like <p><b>xy</b> <i>z</i><p>
2235
     for all tags "p" allowing PCDATA */
2236
0
  for ( i = 0; i < sizeof(allowPCData)/sizeof(allowPCData[0]); i++ ) {
2237
0
      if ( xmlStrEqual(lastChild->name, BAD_CAST allowPCData[i]) ) {
2238
0
    return(0);
2239
0
      }
2240
0
  }
2241
0
    }
2242
0
    return(1);
2243
0
}
2244
2245
/**
2246
 * htmlNewDocNoDtD:
2247
 * @URI:  URI for the dtd, or NULL
2248
 * @ExternalID:  the external ID of the DTD, or NULL
2249
 *
2250
 * Creates a new HTML document without a DTD node if @URI and @ExternalID
2251
 * are NULL
2252
 *
2253
 * Returns a new document, do not initialize the DTD if not provided
2254
 */
2255
htmlDocPtr
2256
0
htmlNewDocNoDtD(const xmlChar *URI, const xmlChar *ExternalID) {
2257
0
    xmlDocPtr cur;
2258
2259
    /*
2260
     * Allocate a new document and fill the fields.
2261
     */
2262
0
    cur = (xmlDocPtr) xmlMalloc(sizeof(xmlDoc));
2263
0
    if (cur == NULL)
2264
0
  return(NULL);
2265
0
    memset(cur, 0, sizeof(xmlDoc));
2266
2267
0
    cur->type = XML_HTML_DOCUMENT_NODE;
2268
0
    cur->version = NULL;
2269
0
    cur->intSubset = NULL;
2270
0
    cur->doc = cur;
2271
0
    cur->name = NULL;
2272
0
    cur->children = NULL;
2273
0
    cur->extSubset = NULL;
2274
0
    cur->oldNs = NULL;
2275
0
    cur->encoding = NULL;
2276
0
    cur->standalone = 1;
2277
0
    cur->compression = 0;
2278
0
    cur->ids = NULL;
2279
0
    cur->refs = NULL;
2280
0
    cur->_private = NULL;
2281
0
    cur->charset = XML_CHAR_ENCODING_UTF8;
2282
0
    cur->properties = XML_DOC_HTML | XML_DOC_USERBUILT;
2283
0
    if ((ExternalID != NULL) ||
2284
0
  (URI != NULL)) {
2285
0
        xmlDtdPtr intSubset;
2286
2287
0
  intSubset = xmlCreateIntSubset(cur, BAD_CAST "html", ExternalID, URI);
2288
0
        if (intSubset == NULL) {
2289
0
            xmlFree(cur);
2290
0
            return(NULL);
2291
0
        }
2292
0
    }
2293
0
    if ((xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
2294
0
  xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
2295
0
    return(cur);
2296
0
}
2297
2298
/**
2299
 * htmlNewDoc:
2300
 * @URI:  URI for the dtd, or NULL
2301
 * @ExternalID:  the external ID of the DTD, or NULL
2302
 *
2303
 * Creates a new HTML document
2304
 *
2305
 * Returns a new document
2306
 */
2307
htmlDocPtr
2308
0
htmlNewDoc(const xmlChar *URI, const xmlChar *ExternalID) {
2309
0
    if ((URI == NULL) && (ExternalID == NULL))
2310
0
  return(htmlNewDocNoDtD(
2311
0
        BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd",
2312
0
        BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN"));
2313
2314
0
    return(htmlNewDocNoDtD(URI, ExternalID));
2315
0
}
2316
2317
2318
/************************************************************************
2319
 *                  *
2320
 *      The parser itself       *
2321
 *  Relates to http://www.w3.org/TR/html40        *
2322
 *                  *
2323
 ************************************************************************/
2324
2325
/************************************************************************
2326
 *                  *
2327
 *      The parser itself       *
2328
 *                  *
2329
 ************************************************************************/
2330
2331
/**
2332
 * htmlParseHTMLName:
2333
 * @ctxt:  an HTML parser context
2334
 *
2335
 * parse an HTML tag or attribute name, note that we convert it to lowercase
2336
 * since HTML names are not case-sensitive.
2337
 *
2338
 * Returns the Tag Name parsed or NULL
2339
 */
2340
2341
static xmlHashedString
2342
0
htmlParseHTMLName(htmlParserCtxtPtr ctxt, int attr) {
2343
0
    xmlHashedString ret;
2344
0
    xmlChar buf[HTML_PARSER_BUFFER_SIZE];
2345
0
    const xmlChar *in;
2346
0
    size_t avail;
2347
0
    int eof = PARSER_PROGRESSIVE(ctxt);
2348
0
    int nbchar = 0;
2349
0
    int stop = attr ? '=' : ' ';
2350
2351
0
    in = ctxt->input->cur;
2352
0
    avail = ctxt->input->end - in;
2353
2354
0
    while (1) {
2355
0
        int c, size;
2356
2357
0
        if ((!eof) && (avail < 32)) {
2358
0
            size_t oldAvail = avail;
2359
2360
0
            ctxt->input->cur = in;
2361
2362
0
            SHRINK;
2363
0
            xmlParserGrow(ctxt);
2364
2365
0
            in = ctxt->input->cur;
2366
0
            avail = ctxt->input->end - in;
2367
2368
0
            if (oldAvail == avail)
2369
0
                eof = 1;
2370
0
        }
2371
2372
0
        if (avail == 0)
2373
0
            break;
2374
2375
0
        c = *in;
2376
0
        size = 1;
2377
2378
0
        if ((nbchar != 0) &&
2379
0
            ((c == '/') || (c == '>') || (c == stop) ||
2380
0
             (IS_WS_HTML(c))))
2381
0
            break;
2382
2383
0
        if (c == 0) {
2384
0
            if (nbchar + 3 <= HTML_PARSER_BUFFER_SIZE) {
2385
0
                buf[nbchar++] = 0xEF;
2386
0
                buf[nbchar++] = 0xBF;
2387
0
                buf[nbchar++] = 0xBD;
2388
0
            }
2389
0
        } else if (c < 0x80) {
2390
0
            if (nbchar < HTML_PARSER_BUFFER_SIZE) {
2391
0
                if (IS_UPPER(c))
2392
0
                    c += 0x20;
2393
0
                buf[nbchar++] = c;
2394
0
            }
2395
0
        } else {
2396
0
            size = htmlValidateUtf8(ctxt, in, avail, /* partial */ 0);
2397
2398
0
            if (size > 0) {
2399
0
                if (nbchar + size <= HTML_PARSER_BUFFER_SIZE) {
2400
0
                    memcpy(buf + nbchar, in, size);
2401
0
                    nbchar += size;
2402
0
                }
2403
0
            } else {
2404
0
                size = 1;
2405
2406
0
                if (nbchar + 3 <= HTML_PARSER_BUFFER_SIZE) {
2407
0
                    buf[nbchar++] = 0xEF;
2408
0
                    buf[nbchar++] = 0xBF;
2409
0
                    buf[nbchar++] = 0xBD;
2410
0
                }
2411
0
            }
2412
0
        }
2413
2414
0
        in += size;
2415
0
        avail -= size;
2416
0
    }
2417
2418
0
    ctxt->input->cur = in;
2419
2420
0
    SHRINK;
2421
2422
0
    ret = xmlDictLookupHashed(ctxt->dict, buf, nbchar);
2423
0
    if (ret.name == NULL)
2424
0
        htmlErrMemory(ctxt);
2425
2426
0
    return(ret);
2427
0
}
2428
2429
static const short htmlC1Remap[32] = {
2430
    0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
2431
    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
2432
    0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
2433
    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178
2434
};
2435
2436
static const xmlChar *
2437
0
htmlCodePointToUtf8(int c, xmlChar *out, int *osize) {
2438
0
    int i = 0;
2439
0
    int bits, hi;
2440
2441
0
    if ((c >= 0x80) && (c < 0xA0)) {
2442
0
        c = htmlC1Remap[c - 0x80];
2443
0
    } else if ((c <= 0) ||
2444
0
               ((c >= 0xD800) && (c < 0xE000)) ||
2445
0
               (c > 0x10FFFF)) {
2446
0
        c = 0xFFFD;
2447
0
    }
2448
2449
0
    if      (c <    0x80) { bits =  0; hi = 0x00; }
2450
0
    else if (c <   0x800) { bits =  6; hi = 0xC0; }
2451
0
    else if (c < 0x10000) { bits = 12; hi = 0xE0; }
2452
0
    else                  { bits = 18; hi = 0xF0; }
2453
2454
0
    out[i++] = (c >> bits) | hi;
2455
2456
0
    while (bits > 0) {
2457
0
        bits -= 6;
2458
0
        out[i++] = ((c >> bits) & 0x3F) | 0x80;
2459
0
    }
2460
2461
0
    *osize = i;
2462
0
    return(out);
2463
0
}
2464
2465
#include "html5ent.inc"
2466
2467
0
#define ENT_F_SEMICOLON 0x80u
2468
0
#define ENT_F_SUBTABLE  0x40u
2469
0
#define ENT_F_ALL       0xC0u
2470
2471
static const xmlChar *
2472
htmlFindEntityPrefix(const xmlChar *string, size_t slen, int isAttr,
2473
0
                     int *nlen, int *rlen) {
2474
0
    const xmlChar *match = NULL;
2475
0
    unsigned left, right;
2476
0
    int first = string[0];
2477
0
    size_t matchLen = 0;
2478
0
    size_t soff = 1;
2479
2480
0
    if (slen < 2)
2481
0
        return(NULL);
2482
0
    if (!IS_ASCII_LETTER(first))
2483
0
        return(NULL);
2484
2485
    /*
2486
     * Look up range by first character
2487
     */
2488
0
    first &= 63;
2489
0
    left = htmlEntAlpha[first*3] | htmlEntAlpha[first*3+1] << 8;
2490
0
    right = left + htmlEntAlpha[first*3+2];
2491
2492
    /*
2493
     * Binary search
2494
     */
2495
0
    while (left < right) {
2496
0
        const xmlChar *bytes;
2497
0
        unsigned mid;
2498
0
        size_t len;
2499
0
        int cmp;
2500
2501
0
        mid = left + (right - left) / 2;
2502
0
        bytes = htmlEntStrings + htmlEntValues[mid];
2503
0
        len = bytes[0] & ~ENT_F_ALL;
2504
2505
0
        cmp = string[soff] - bytes[1];
2506
2507
0
        if (cmp == 0) {
2508
0
            if (slen < len) {
2509
0
                cmp = strncmp((const char *) string + soff + 1,
2510
0
                              (const char *) bytes + 2,
2511
0
                              slen - 1);
2512
                /* Prefix can never match */
2513
0
                if (cmp == 0)
2514
0
                    break;
2515
0
            } else {
2516
0
                cmp = strncmp((const char *) string + soff + 1,
2517
0
                              (const char *) bytes + 2,
2518
0
                              len - 1);
2519
0
            }
2520
0
        }
2521
2522
0
        if (cmp < 0) {
2523
0
            right = mid;
2524
0
        } else if (cmp > 0) {
2525
0
            left = mid + 1;
2526
0
        } else {
2527
0
            int term = soff + len < slen ? string[soff + len] : 0;
2528
0
            int isAlnum, isTerm;
2529
2530
0
            isAlnum = IS_ALNUM(term);
2531
0
            isTerm = ((term == ';') ||
2532
0
                      ((bytes[0] & ENT_F_SEMICOLON) &&
2533
0
                       ((!isAttr) ||
2534
0
                        ((!isAlnum) && (term != '=')))));
2535
2536
0
            if (isTerm) {
2537
0
                match = bytes + len + 1;
2538
0
                matchLen = soff + len;
2539
0
                if (term == ';')
2540
0
                    matchLen += 1;
2541
0
            }
2542
2543
0
            if (bytes[0] & ENT_F_SUBTABLE) {
2544
0
                if (isTerm)
2545
0
                    match += 2;
2546
2547
0
                if ((isAlnum) && (soff + len < slen)) {
2548
0
                    left = mid + bytes[len + 1];
2549
0
                    right = left + bytes[len + 2];
2550
0
                    soff += len;
2551
0
                    continue;
2552
0
                }
2553
0
            }
2554
2555
0
            break;
2556
0
        }
2557
0
    }
2558
2559
0
    if (match == NULL)
2560
0
        return(NULL);
2561
2562
0
    *nlen = matchLen;
2563
0
    *rlen = match[0];
2564
0
    return(match + 1);
2565
0
}
2566
2567
/**
2568
 * htmlParseData:
2569
 * @ctxt:  an HTML parser context
2570
 * @mask:  mask of terminating characters
2571
 * @comment:  true if parsing a comment
2572
 * @refs:  true if references are allowed
2573
 * @maxLength:  maximum output length
2574
 *
2575
 * Parse data until terminator is reached.
2576
 *
2577
 * Returns the parsed string or NULL in case of errors.
2578
 */
2579
2580
static xmlChar *
2581
htmlParseData(htmlParserCtxtPtr ctxt, htmlAsciiMask mask,
2582
0
              int comment, int refs, int maxLength) {
2583
0
    xmlParserInputPtr input = ctxt->input;
2584
0
    xmlChar *ret = NULL;
2585
0
    xmlChar *buffer;
2586
0
    xmlChar utf8Char[4];
2587
0
    size_t buffer_size;
2588
0
    size_t used;
2589
0
    int eof = PARSER_PROGRESSIVE(ctxt);
2590
0
    int line, col;
2591
0
    int termSkip = -1;
2592
2593
0
    used = 0;
2594
0
    buffer_size = ctxt->spaceMax;
2595
0
    buffer = (xmlChar *) ctxt->spaceTab;
2596
0
    if (buffer == NULL) {
2597
0
        buffer_size = 500;
2598
0
        buffer = xmlMalloc(buffer_size + 1);
2599
0
        if (buffer == NULL) {
2600
0
            htmlErrMemory(ctxt);
2601
0
            return(NULL);
2602
0
        }
2603
0
    }
2604
2605
0
    line = input->line;
2606
0
    col = input->col;
2607
2608
0
    while (!PARSER_STOPPED(ctxt)) {
2609
0
        const xmlChar *chunk, *in, *repl;
2610
0
        size_t avail, chunkSize, extraSize;
2611
0
        int replSize;
2612
0
        int skip = 0;
2613
0
        int ncr = 0;
2614
0
        int ncrSize = 0;
2615
0
        int cp = 0;
2616
2617
0
        chunk = input->cur;
2618
0
        avail = input->end - chunk;
2619
0
        in = chunk;
2620
2621
0
        repl = BAD_CAST "";
2622
0
        replSize = 0;
2623
2624
0
        while (!PARSER_STOPPED(ctxt)) {
2625
0
            size_t j;
2626
0
            int cur, size;
2627
2628
0
            if ((!eof) && (avail <= 64)) {
2629
0
                size_t oldAvail = avail;
2630
0
                size_t off = in - chunk;
2631
2632
0
                input->cur = in;
2633
2634
0
                xmlParserGrow(ctxt);
2635
2636
0
                in = input->cur;
2637
0
                chunk = in - off;
2638
0
                input->cur = chunk;
2639
0
                avail = input->end - in;
2640
2641
0
                if (oldAvail == avail)
2642
0
                    eof = 1;
2643
0
            }
2644
2645
0
            if (avail == 0) {
2646
0
                termSkip = 0;
2647
0
                break;
2648
0
            }
2649
2650
0
            cur = *in;
2651
0
            size = 1;
2652
0
            col += 1;
2653
2654
0
            if (htmlMaskMatch(mask, cur)) {
2655
0
                if (comment) {
2656
0
                    if (avail < 2) {
2657
0
                        termSkip = 1;
2658
0
                    } else if (in[1] == '-') {
2659
0
                        if  (avail < 3) {
2660
0
                            termSkip = 2;
2661
0
                        } else if (in[2] == '>') {
2662
0
                            termSkip = 3;
2663
0
                        } else if (in[2] == '!') {
2664
0
                            if (avail < 4)
2665
0
                                termSkip = 3;
2666
0
                            else if (in[3] == '>')
2667
0
                                termSkip = 4;
2668
0
                        }
2669
0
                    }
2670
2671
0
                    if (termSkip >= 0)
2672
0
                        break;
2673
0
                } else {
2674
0
                    termSkip = 0;
2675
0
                    break;
2676
0
                }
2677
0
            }
2678
2679
0
            if (ncr) {
2680
0
                int lc = cur | 0x20;
2681
0
                int digit;
2682
2683
0
                if ((cur >= '0') && (cur <= '9')) {
2684
0
                    digit = cur - '0';
2685
0
                } else if ((ncr == 16) && (lc >= 'a') && (lc <= 'f')) {
2686
0
                    digit = (lc - 'a') + 10;
2687
0
                } else {
2688
0
                    if (cur == ';') {
2689
0
                        in += 1;
2690
0
                        size += 1;
2691
0
                        ncrSize += 1;
2692
0
                    }
2693
0
                    goto next_chunk;
2694
0
                }
2695
2696
0
                cp = cp * ncr + digit;
2697
0
                if (cp >= 0x110000)
2698
0
                    cp = 0x110000;
2699
2700
0
                ncrSize += 1;
2701
2702
0
                goto next_char;
2703
0
            }
2704
2705
0
            switch (cur) {
2706
0
            case '&':
2707
0
                if (!refs)
2708
0
                    break;
2709
2710
0
                j = 1;
2711
2712
0
                if ((j < avail) && (in[j] == '#')) {
2713
0
                    j += 1;
2714
0
                    if (j < avail) {
2715
0
                        if ((in[j] | 0x20) == 'x') {
2716
0
                            j += 1;
2717
0
                            if ((j < avail) && (IS_HEX_DIGIT(in[j]))) {
2718
0
                                ncr = 16;
2719
0
                                size = 3;
2720
0
                                ncrSize = 3;
2721
0
                                cp = 0;
2722
0
                            }
2723
0
                        } else if (IS_ASCII_DIGIT(in[j])) {
2724
0
                            ncr = 10;
2725
0
                            size = 2;
2726
0
                            ncrSize = 2;
2727
0
                            cp = 0;
2728
0
                        }
2729
0
                    }
2730
0
                } else {
2731
0
                    repl = htmlFindEntityPrefix(in + j,
2732
0
                                                avail - j,
2733
0
                                                /* isAttr */ 1,
2734
0
                                                &skip, &replSize);
2735
0
                    if (repl != NULL) {
2736
0
                        skip += 1;
2737
0
                        goto next_chunk;
2738
0
                    }
2739
2740
0
                    skip = 0;
2741
0
                }
2742
2743
0
                break;
2744
2745
0
            case '\0':
2746
0
                skip = 1;
2747
0
                repl = BAD_CAST "\xEF\xBF\xBD";
2748
0
                replSize = 3;
2749
0
                goto next_chunk;
2750
2751
0
            case '\n':
2752
0
                line += 1;
2753
0
                col = 1;
2754
0
                break;
2755
2756
0
            case '\r':
2757
0
                skip = 1;
2758
0
                if (in[1] != 0x0A) {
2759
0
                    repl = BAD_CAST "\x0A";
2760
0
                    replSize = 1;
2761
0
                }
2762
0
                goto next_chunk;
2763
2764
0
            default:
2765
0
                if (cur < 0x80)
2766
0
                    break;
2767
2768
0
                if ((input->flags & XML_INPUT_HAS_ENCODING) == 0) {
2769
0
                    xmlChar * guess;
2770
2771
0
                    if (in > chunk)
2772
0
                        goto next_chunk;
2773
2774
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2775
0
                    guess = NULL;
2776
#else
2777
                    guess = htmlFindEncoding(ctxt);
2778
#endif
2779
0
                    if (guess == NULL) {
2780
0
                        xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
2781
0
                    } else {
2782
0
                        xmlSwitchEncodingName(ctxt, (const char *) guess);
2783
0
                        xmlFree(guess);
2784
0
                    }
2785
0
                    input->flags |= XML_INPUT_HAS_ENCODING;
2786
2787
0
                    eof = PARSER_PROGRESSIVE(ctxt);
2788
0
                    goto restart;
2789
0
                }
2790
2791
0
                size = htmlValidateUtf8(ctxt, in, avail, /* partial */ 0);
2792
2793
0
                if (size <= 0) {
2794
0
                    skip = 1;
2795
0
                    repl = BAD_CAST "\xEF\xBF\xBD";
2796
0
                    replSize = 3;
2797
0
                    goto next_chunk;
2798
0
                }
2799
2800
0
                break;
2801
0
            }
2802
2803
0
next_char:
2804
0
            in += size;
2805
0
            avail -= size;
2806
0
        }
2807
2808
0
next_chunk:
2809
0
        if (ncrSize > 0) {
2810
0
            skip = ncrSize;
2811
0
            in -= ncrSize;
2812
2813
0
            repl = htmlCodePointToUtf8(cp, utf8Char, &replSize);
2814
0
        }
2815
2816
0
        chunkSize = in - chunk;
2817
0
        extraSize = chunkSize + replSize;
2818
2819
0
        if (extraSize > maxLength - used) {
2820
0
            htmlParseErr(ctxt, XML_ERR_RESOURCE_LIMIT,
2821
0
                         "value too long\n", NULL, NULL);
2822
0
            goto error;
2823
0
        }
2824
2825
0
        if (extraSize > buffer_size - used) {
2826
0
            size_t newSize = (used + extraSize) * 2;
2827
0
            xmlChar *tmp = xmlRealloc(buffer, newSize + 1);
2828
2829
0
            if (tmp == NULL) {
2830
0
                htmlErrMemory(ctxt);
2831
0
                goto error;
2832
0
            }
2833
0
            buffer = tmp;
2834
0
            buffer_size = newSize;
2835
0
        }
2836
2837
0
        if (chunkSize > 0) {
2838
0
            input->cur += chunkSize;
2839
0
            memcpy(buffer + used, chunk, chunkSize);
2840
0
            used += chunkSize;
2841
0
        }
2842
2843
0
        input->cur += skip;
2844
0
        if (replSize > 0) {
2845
0
            memcpy(buffer + used, repl, replSize);
2846
0
            used += replSize;
2847
0
        }
2848
2849
0
        SHRINK;
2850
2851
0
        if (termSkip >= 0)
2852
0
            break;
2853
2854
0
restart:
2855
0
        ;
2856
0
    }
2857
2858
0
    if (termSkip > 0) {
2859
0
        input->cur += termSkip;
2860
0
        col += termSkip;
2861
0
    }
2862
2863
0
    input->line = line;
2864
0
    input->col = col;
2865
2866
0
    ret = xmlMalloc(used + 1);
2867
0
    if (ret == NULL) {
2868
0
        htmlErrMemory(ctxt);
2869
0
    } else {
2870
0
        memcpy(ret, buffer, used);
2871
0
        ret[used] = 0;
2872
0
    }
2873
2874
0
error:
2875
0
    ctxt->spaceTab = (void *) buffer;
2876
0
    ctxt->spaceMax = buffer_size;
2877
2878
0
    return(ret);
2879
0
}
2880
2881
/**
2882
 * htmlParseEntityRef:
2883
 * @ctxt:  an HTML parser context
2884
 * @str:  location to store the entity name
2885
 *
2886
 * DEPRECATED: Internal function, don't use.
2887
 *
2888
 * Returns NULL.
2889
 */
2890
const htmlEntityDesc *
2891
htmlParseEntityRef(htmlParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2892
0
                   const xmlChar **str ATTRIBUTE_UNUSED) {
2893
0
    return(NULL);
2894
0
}
2895
2896
/**
2897
 * htmlParseAttValue:
2898
 * @ctxt:  an HTML parser context
2899
 *
2900
 * parse a value for an attribute
2901
 * Note: the parser won't do substitution of entities here, this
2902
 * will be handled later in xmlStringGetNodeList, unless it was
2903
 * asked for ctxt->replaceEntities != 0
2904
 *
2905
 * Returns the AttValue parsed or NULL.
2906
 */
2907
2908
static xmlChar *
2909
0
htmlParseAttValue(htmlParserCtxtPtr ctxt) {
2910
0
    xmlChar *ret = NULL;
2911
0
    int maxLength = (ctxt->options & HTML_PARSE_HUGE) ?
2912
0
                    XML_MAX_HUGE_LENGTH :
2913
0
                    XML_MAX_TEXT_LENGTH;
2914
2915
0
    if (CUR == '"') {
2916
0
        SKIP(1);
2917
0
  ret = htmlParseData(ctxt, MASK_DQ, 0, 1, maxLength);
2918
0
        if (CUR == '"')
2919
0
            SKIP(1);
2920
0
    } else if (CUR == '\'') {
2921
0
        SKIP(1);
2922
0
  ret = htmlParseData(ctxt, MASK_SQ, 0, 1, maxLength);
2923
0
        if (CUR == '\'')
2924
0
            SKIP(1);
2925
0
    } else {
2926
0
  ret = htmlParseData(ctxt, MASK_WS_GT, 0, 1, maxLength);
2927
0
    }
2928
0
    return(ret);
2929
0
}
2930
2931
static void
2932
htmlCharDataSAXCallback(htmlParserCtxtPtr ctxt, const xmlChar *buf,
2933
0
                        int size, int mode) {
2934
0
    if ((ctxt->sax == NULL) || (ctxt->disableSAX))
2935
0
        return;
2936
2937
0
    if ((mode == 0) || (mode == DATA_RCDATA) ||
2938
0
        (ctxt->sax->cdataBlock == NULL)) {
2939
0
        if ((ctxt->name == NULL) ||
2940
0
            (xmlStrEqual(ctxt->name, BAD_CAST "html")) ||
2941
0
            (xmlStrEqual(ctxt->name, BAD_CAST "head"))) {
2942
0
            int i;
2943
2944
            /*
2945
             * Add leading whitespace to html or head elements before
2946
             * calling htmlStartCharData.
2947
             */
2948
0
            for (i = 0; i < size; i++)
2949
0
                if (!IS_WS_HTML(buf[i]))
2950
0
                    break;
2951
2952
0
            if (i > 0) {
2953
0
                if (!ctxt->keepBlanks) {
2954
0
                    if (ctxt->sax->ignorableWhitespace != NULL)
2955
0
                        ctxt->sax->ignorableWhitespace(ctxt->userData, buf, i);
2956
0
                } else {
2957
0
                    if (ctxt->sax->characters != NULL)
2958
0
                        ctxt->sax->characters(ctxt->userData, buf, i);
2959
0
                }
2960
2961
0
                buf += i;
2962
0
                size -= i;
2963
0
            }
2964
2965
0
            if (size <= 0)
2966
0
                return;
2967
2968
0
            htmlStartCharData(ctxt);
2969
2970
0
            if (PARSER_STOPPED(ctxt))
2971
0
                return;
2972
0
        }
2973
2974
0
        if ((mode == 0) &&
2975
0
            (!ctxt->keepBlanks) &&
2976
0
            (areBlanks(ctxt, buf, size) > 0)) {
2977
0
            if (ctxt->sax->ignorableWhitespace != NULL)
2978
0
                ctxt->sax->ignorableWhitespace(ctxt->userData, buf, size);
2979
0
        } else {
2980
0
            if (ctxt->sax->characters != NULL)
2981
0
                ctxt->sax->characters(ctxt->userData, buf, size);
2982
0
        }
2983
0
    } else {
2984
        /*
2985
         * Insert as CDATA, which is the same as HTML_PRESERVE_NODE
2986
         */
2987
0
        ctxt->sax->cdataBlock(ctxt->userData, buf, size);
2988
0
    }
2989
0
}
2990
2991
/**
2992
 * htmlParseCharData:
2993
 * @ctxt:  an HTML parser context
2994
 * @partial: true if the input buffer is incomplete
2995
 *
2996
 * Parse character data and references.
2997
 *
2998
 * Returns 1 if all data was parsed, 0 otherwise.
2999
 */
3000
3001
static int
3002
0
htmlParseCharData(htmlParserCtxtPtr ctxt, int partial) {
3003
0
    xmlParserInputPtr input = ctxt->input;
3004
0
    xmlChar utf8Char[4];
3005
0
    int complete = 0;
3006
0
    int done = 0;
3007
0
    int mode;
3008
0
    int eof = PARSER_PROGRESSIVE(ctxt);
3009
0
    int line, col;
3010
3011
0
    mode = ctxt->endCheckState;
3012
3013
0
    line = input->line;
3014
0
    col = input->col;
3015
3016
0
    while (!PARSER_STOPPED(ctxt)) {
3017
0
        const xmlChar *chunk, *in, *repl;
3018
0
        size_t avail;
3019
0
        int replSize;
3020
0
        int skip = 0;
3021
0
        int ncr = 0;
3022
0
        int ncrSize = 0;
3023
0
        int cp = 0;
3024
3025
0
        chunk = input->cur;
3026
0
        avail = input->end - chunk;
3027
0
        in = chunk;
3028
3029
0
        repl = BAD_CAST "";
3030
0
        replSize = 0;
3031
3032
0
        while (!PARSER_STOPPED(ctxt)) {
3033
0
            size_t j;
3034
0
            int cur, size;
3035
3036
0
            if (avail <= 64) {
3037
0
                if (!eof) {
3038
0
                    size_t oldAvail = avail;
3039
0
                    size_t off = in - chunk;
3040
3041
0
                    input->cur = in;
3042
3043
0
                    xmlParserGrow(ctxt);
3044
3045
0
                    in = input->cur;
3046
0
                    chunk = in - off;
3047
0
                    input->cur = chunk;
3048
0
                    avail = input->end - in;
3049
3050
0
                    if (oldAvail == avail)
3051
0
                        eof = 1;
3052
0
                }
3053
3054
0
                if (avail == 0) {
3055
0
                    if ((partial) && (ncr)) {
3056
0
                        in -= ncrSize;
3057
0
                        ncrSize = 0;
3058
0
                    }
3059
3060
0
                    done = 1;
3061
0
                    break;
3062
0
                }
3063
0
            }
3064
3065
            /* Accelerator */
3066
0
            if (!ncr) {
3067
0
                while (avail > 0) {
3068
0
                    static const unsigned mask[8] = {
3069
0
                        0x00002401, 0x10002040,
3070
0
                        0x00000000, 0x00000000,
3071
0
                        0xFFFFFFFF, 0xFFFFFFFF,
3072
0
                        0xFFFFFFFF, 0xFFFFFFFF
3073
0
                    };
3074
0
                    cur = *in;
3075
0
                    if ((1u << (cur & 0x1F)) & mask[cur >> 5])
3076
0
                        break;
3077
0
                    col += 1;
3078
0
                    in += 1;
3079
0
                    avail -= 1;
3080
0
                }
3081
3082
0
                if ((!eof) && (avail <= 64))
3083
0
                    continue;
3084
0
                if (avail == 0)
3085
0
                    continue;
3086
0
            }
3087
3088
0
            cur = *in;
3089
0
            size = 1;
3090
0
            col += 1;
3091
3092
0
            if (ncr) {
3093
0
                int lc = cur | 0x20;
3094
0
                int digit;
3095
3096
0
                if ((cur >= '0') && (cur <= '9')) {
3097
0
                    digit = cur - '0';
3098
0
                } else if ((ncr == 16) && (lc >= 'a') && (lc <= 'f')) {
3099
0
                    digit = (lc - 'a') + 10;
3100
0
                } else {
3101
0
                    if (cur == ';') {
3102
0
                        in += 1;
3103
0
                        size += 1;
3104
0
                        ncrSize += 1;
3105
0
                    }
3106
0
                    goto next_chunk;
3107
0
                }
3108
3109
0
                cp = cp * ncr + digit;
3110
0
                if (cp >= 0x110000)
3111
0
                    cp = 0x110000;
3112
3113
0
                ncrSize += 1;
3114
3115
0
                goto next_char;
3116
0
            }
3117
3118
0
            switch (cur) {
3119
0
            case '<':
3120
0
                if (mode == 0) {
3121
0
                    done = 1;
3122
0
                    complete = 1;
3123
0
                    goto next_chunk;
3124
0
                }
3125
0
                if (mode == DATA_PLAINTEXT)
3126
0
                    break;
3127
3128
0
                j = 1;
3129
0
                if (j < avail) {
3130
0
                    if ((mode == DATA_SCRIPT) && (in[j] == '!')) {
3131
                        /* Check for comment start */
3132
3133
0
                        j += 1;
3134
0
                        if ((j < avail) && (in[j] == '-')) {
3135
0
                            j += 1;
3136
0
                            if ((j < avail) && (in[j] == '-'))
3137
0
                                mode = DATA_SCRIPT_ESC1;
3138
0
                        }
3139
0
                    } else {
3140
0
                        int i = 0;
3141
0
                        int solidus = 0;
3142
3143
                        /* Check for tag */
3144
3145
0
                        if (in[j] == '/') {
3146
0
                            j += 1;
3147
0
                            solidus = 1;
3148
0
                        }
3149
3150
0
                        if ((solidus) || (mode == DATA_SCRIPT_ESC1)) {
3151
0
                            while ((j < avail) &&
3152
0
                                   (ctxt->name[i] != 0) &&
3153
0
                                   (ctxt->name[i] == (in[j] | 0x20))) {
3154
0
                                i += 1;
3155
0
                                j += 1;
3156
0
                            }
3157
3158
0
                            if ((ctxt->name[i] == 0) && (j < avail)) {
3159
0
                                int c = in[j];
3160
3161
0
                                if ((c == '>') || (c == '/') ||
3162
0
                                    (IS_WS_HTML(c))) {
3163
0
                                    if ((mode == DATA_SCRIPT_ESC1) &&
3164
0
                                        (!solidus)) {
3165
0
                                        mode = DATA_SCRIPT_ESC2;
3166
0
                                    } else if (mode == DATA_SCRIPT_ESC2) {
3167
0
                                        mode = DATA_SCRIPT_ESC1;
3168
0
                                    } else {
3169
0
                                        complete = 1;
3170
0
                                        done = 1;
3171
0
                                        goto next_chunk;
3172
0
                                    }
3173
0
                                }
3174
0
                            }
3175
0
                        }
3176
0
                    }
3177
0
                }
3178
3179
0
                if ((partial) && (j >= avail)) {
3180
0
                    done = 1;
3181
0
                    goto next_chunk;
3182
0
                }
3183
3184
0
                break;
3185
3186
0
            case '-':
3187
0
                if ((mode != DATA_SCRIPT_ESC1) && (mode != DATA_SCRIPT_ESC2))
3188
0
                    break;
3189
3190
                /* Check for comment end */
3191
3192
0
                j = 1;
3193
0
                if ((j < avail) && (in[j] == '-')) {
3194
0
                    j += 1;
3195
0
                    if ((j < avail) && (in[j] == '>'))
3196
0
                        mode = DATA_SCRIPT;
3197
0
                }
3198
3199
0
                if ((partial) && (j >= avail)) {
3200
0
                    done = 1;
3201
0
                    goto next_chunk;
3202
0
                }
3203
3204
0
                break;
3205
3206
0
            case '&':
3207
0
                if ((mode != 0) && (mode != DATA_RCDATA))
3208
0
                    break;
3209
3210
0
                j = 1;
3211
3212
0
                if ((j < avail) && (in[j] == '#')) {
3213
0
                    j += 1;
3214
0
                    if (j < avail) {
3215
0
                        if ((in[j] | 0x20) == 'x') {
3216
0
                            j += 1;
3217
0
                            if ((j < avail) && (IS_HEX_DIGIT(in[j]))) {
3218
0
                                ncr = 16;
3219
0
                                size = 3;
3220
0
                                ncrSize = 3;
3221
0
                                cp = 0;
3222
0
                            }
3223
0
                        } else if (IS_ASCII_DIGIT(in[j])) {
3224
0
                            ncr = 10;
3225
0
                            size = 2;
3226
0
                            ncrSize = 2;
3227
0
                            cp = 0;
3228
0
                        }
3229
0
                    }
3230
0
                } else {
3231
0
                    if (partial) {
3232
0
                        int terminated = 0;
3233
0
                        size_t i;
3234
3235
                        /*
3236
                         * &CounterClockwiseContourIntegral; has 33 bytes.
3237
                         */
3238
0
                        for (i = 1; i < avail; i++) {
3239
0
                            if ((i >= 32) ||
3240
0
                                (!IS_ASCII_LETTER(in[i]) &&
3241
0
                                 ((i < 2) || !IS_ASCII_DIGIT(in[i])))) {
3242
0
                                terminated = 1;
3243
0
                                break;
3244
0
                            }
3245
0
                        }
3246
3247
0
                        if (!terminated) {
3248
0
                            done = 1;
3249
0
                            goto next_chunk;
3250
0
                        }
3251
0
                    }
3252
3253
0
                    repl = htmlFindEntityPrefix(in + j,
3254
0
                                                avail - j,
3255
0
                                                /* isAttr */ 0,
3256
0
                                                &skip, &replSize);
3257
0
                    if (repl != NULL) {
3258
0
                        skip += 1;
3259
0
                        goto next_chunk;
3260
0
                    }
3261
3262
0
                    skip = 0;
3263
0
                }
3264
3265
0
                if ((partial) && (j >= avail)) {
3266
0
                    done = 1;
3267
0
                    goto next_chunk;
3268
0
                }
3269
3270
0
                break;
3271
3272
0
            case '\0':
3273
0
                skip = 1;
3274
0
                repl = BAD_CAST "\xEF\xBF\xBD";
3275
0
                replSize = 3;
3276
0
                goto next_chunk;
3277
3278
0
            case '\n':
3279
0
                line += 1;
3280
0
                col = 1;
3281
0
                break;
3282
3283
0
            case '\r':
3284
0
                if (partial && avail < 2) {
3285
0
                    done = 1;
3286
0
                    goto next_chunk;
3287
0
                }
3288
3289
0
                skip = 1;
3290
0
                if (in[1] != 0x0A) {
3291
0
                    repl = BAD_CAST "\x0A";
3292
0
                    replSize = 1;
3293
0
                }
3294
0
                goto next_chunk;
3295
3296
0
            default:
3297
0
                if (cur < 0x80)
3298
0
                    break;
3299
3300
0
                if ((input->flags & XML_INPUT_HAS_ENCODING) == 0) {
3301
0
                    xmlChar * guess;
3302
3303
0
                    if (in > chunk)
3304
0
                        goto next_chunk;
3305
3306
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
3307
0
                    guess = NULL;
3308
#else
3309
                    guess = htmlFindEncoding(ctxt);
3310
#endif
3311
0
                    if (guess == NULL) {
3312
0
                        xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
3313
0
                    } else {
3314
0
                        xmlSwitchEncodingName(ctxt, (const char *) guess);
3315
0
                        xmlFree(guess);
3316
0
                    }
3317
0
                    input->flags |= XML_INPUT_HAS_ENCODING;
3318
3319
0
                    eof = PARSER_PROGRESSIVE(ctxt);
3320
0
                    goto restart;
3321
0
                }
3322
3323
0
                size = htmlValidateUtf8(ctxt, in, avail, partial);
3324
3325
0
                if ((partial) && (size == 0)) {
3326
0
                    done = 1;
3327
0
                    goto next_chunk;
3328
0
                }
3329
3330
0
                if (size <= 0) {
3331
0
                    skip = 1;
3332
0
                    repl = BAD_CAST "\xEF\xBF\xBD";
3333
0
                    replSize = 3;
3334
0
                    goto next_chunk;
3335
0
                }
3336
3337
0
                break;
3338
0
            }
3339
3340
0
next_char:
3341
0
            in += size;
3342
0
            avail -= size;
3343
0
        }
3344
3345
0
next_chunk:
3346
0
        if (ncrSize > 0) {
3347
0
            skip = ncrSize;
3348
0
            in -= ncrSize;
3349
3350
0
            repl = htmlCodePointToUtf8(cp, utf8Char, &replSize);
3351
0
        }
3352
3353
0
        if (in > chunk) {
3354
0
            input->cur += in - chunk;
3355
0
            htmlCharDataSAXCallback(ctxt, chunk, in - chunk, mode);
3356
0
        }
3357
3358
0
        input->cur += skip;
3359
0
        if (replSize > 0)
3360
0
            htmlCharDataSAXCallback(ctxt, repl, replSize, mode);
3361
3362
0
        SHRINK;
3363
3364
0
        if (done)
3365
0
            break;
3366
3367
0
restart:
3368
0
        ;
3369
0
    }
3370
3371
0
    input->line = line;
3372
0
    input->col = col;
3373
3374
0
    if (complete)
3375
0
        ctxt->endCheckState = 0;
3376
0
    else
3377
0
        ctxt->endCheckState = mode;
3378
3379
0
    return(complete);
3380
0
}
3381
3382
/**
3383
 * htmlParseComment:
3384
 * @ctxt:  an HTML parser context
3385
 * @bogus:  true if this is a bogus comment
3386
 *
3387
 * Parse an HTML comment
3388
 */
3389
static void
3390
0
htmlParseComment(htmlParserCtxtPtr ctxt, int bogus) {
3391
0
    const xmlChar *comment = BAD_CAST "";
3392
0
    xmlChar *buf = NULL;
3393
0
    int maxLength = (ctxt->options & HTML_PARSE_HUGE) ?
3394
0
                    XML_MAX_HUGE_LENGTH :
3395
0
                    XML_MAX_TEXT_LENGTH;
3396
3397
0
    if (bogus) {
3398
0
        buf = htmlParseData(ctxt, MASK_GT, 0, 0, maxLength);
3399
0
        if (CUR == '>')
3400
0
            SKIP(1);
3401
0
        comment = buf;
3402
0
    } else {
3403
0
        if (CUR == '>') {
3404
0
            SKIP(1);
3405
0
        } else if ((CUR == '-') && (NXT(1) == '>')) {
3406
0
            SKIP(2);
3407
0
        } else {
3408
0
            buf = htmlParseData(ctxt, MASK_DASH, 1, 0, maxLength);
3409
0
            comment = buf;
3410
0
        }
3411
0
    }
3412
3413
0
    if (comment == NULL)
3414
0
        return;
3415
3416
0
    if ((ctxt->sax != NULL) && (ctxt->sax->comment != NULL) &&
3417
0
        (!ctxt->disableSAX))
3418
0
        ctxt->sax->comment(ctxt->userData, comment);
3419
3420
0
    xmlFree(buf);
3421
0
}
3422
3423
/**
3424
 * htmlParseCharRef:
3425
 * @ctxt:  an HTML parser context
3426
 *
3427
 * DEPRECATED: Internal function, don't use.
3428
 *
3429
 * Returns 0
3430
 */
3431
int
3432
0
htmlParseCharRef(htmlParserCtxtPtr ctxt ATTRIBUTE_UNUSED) {
3433
0
    return(0);
3434
0
}
3435
3436
3437
/**
3438
 * htmlParseDoctypeLiteral:
3439
 * @ctxt:  an HTML parser context
3440
 *
3441
 * Parse a DOCTYPE SYTSTEM or PUBLIC literal.
3442
 *
3443
 * Returns the literal or NULL in case of error.
3444
 */
3445
3446
static xmlChar *
3447
0
htmlParseDoctypeLiteral(htmlParserCtxtPtr ctxt) {
3448
0
    xmlChar *ret;
3449
0
    int maxLength = (ctxt->options & HTML_PARSE_HUGE) ?
3450
0
                    XML_MAX_TEXT_LENGTH :
3451
0
                    XML_MAX_NAME_LENGTH;
3452
3453
0
    if (CUR == '"') {
3454
0
        SKIP(1);
3455
0
        ret = htmlParseData(ctxt, MASK_DQ_GT, 0, 0, maxLength);
3456
0
        if (CUR == '"')
3457
0
            SKIP(1);
3458
0
    } else if (CUR == '\'') {
3459
0
        SKIP(1);
3460
0
        ret = htmlParseData(ctxt, MASK_SQ_GT, 0, 0, maxLength);
3461
0
        if (CUR == '\'')
3462
0
            SKIP(1);
3463
0
    } else {
3464
0
        return(NULL);
3465
0
    }
3466
3467
0
    return(ret);
3468
0
}
3469
3470
static void
3471
0
htmlSkipBogusDoctype(htmlParserCtxtPtr ctxt) {
3472
0
    const xmlChar *in;
3473
0
    size_t avail;
3474
0
    int eof = PARSER_PROGRESSIVE(ctxt);
3475
0
    int line, col;
3476
3477
0
    line = ctxt->input->line;
3478
0
    col = ctxt->input->col;
3479
3480
0
    in = ctxt->input->cur;
3481
0
    avail = ctxt->input->end - in;
3482
3483
0
    while (!PARSER_STOPPED(ctxt)) {
3484
0
        int cur;
3485
3486
0
        if ((!eof) && (avail <= 64)) {
3487
0
            size_t oldAvail = avail;
3488
3489
0
            ctxt->input->cur = in;
3490
3491
0
            xmlParserGrow(ctxt);
3492
3493
0
            in = ctxt->input->cur;
3494
0
            avail = ctxt->input->end - in;
3495
3496
0
            if (oldAvail == avail)
3497
0
                eof = 1;
3498
0
        }
3499
3500
0
        if (avail == 0)
3501
0
            break;
3502
3503
0
        col += 1;
3504
3505
0
        cur = *in;
3506
0
        if (cur == '>') {
3507
0
            in += 1;
3508
0
            break;
3509
0
        } else if (cur == 0x0A) {
3510
0
            line += 1;
3511
0
            col = 1;
3512
0
        }
3513
3514
0
        in += 1;
3515
0
        avail -= 1;
3516
3517
0
        SHRINK;
3518
0
    }
3519
3520
0
    ctxt->input->cur = in;
3521
0
    ctxt->input->line = line;
3522
0
    ctxt->input->col = col;
3523
0
}
3524
3525
/**
3526
 * htmlParseDocTypeDecl:
3527
 * @ctxt:  an HTML parser context
3528
 *
3529
 * Parse a DOCTYPE declaration.
3530
 */
3531
3532
static void
3533
0
htmlParseDocTypeDecl(htmlParserCtxtPtr ctxt) {
3534
0
    xmlChar *name = NULL;
3535
0
    xmlChar *publicId = NULL;
3536
0
    xmlChar *URI = NULL;
3537
0
    int maxLength = (ctxt->options & HTML_PARSE_HUGE) ?
3538
0
                    XML_MAX_TEXT_LENGTH :
3539
0
                    XML_MAX_NAME_LENGTH;
3540
3541
    /*
3542
     * We know that '<!DOCTYPE' has been detected.
3543
     */
3544
0
    SKIP(9);
3545
3546
0
    SKIP_BLANKS;
3547
3548
0
    if ((ctxt->input->cur < ctxt->input->end) && (CUR != '>')) {
3549
0
        name = htmlParseData(ctxt, MASK_WS_GT, 0, 0, maxLength);
3550
3551
0
        if ((ctxt->options & HTML_PARSE_HTML5) && (name != NULL)) {
3552
0
            xmlChar *cur;
3553
3554
0
            for (cur = name; *cur; cur++) {
3555
0
                if (IS_UPPER(*cur))
3556
0
                    *cur += 0x20;
3557
0
            }
3558
0
        }
3559
3560
0
        SKIP_BLANKS;
3561
0
    }
3562
3563
    /*
3564
     * Check for SystemID and publicId
3565
     */
3566
0
    if ((UPPER == 'P') && (UPP(1) == 'U') &&
3567
0
  (UPP(2) == 'B') && (UPP(3) == 'L') &&
3568
0
  (UPP(4) == 'I') && (UPP(5) == 'C')) {
3569
0
        SKIP(6);
3570
0
        SKIP_BLANKS;
3571
0
  publicId = htmlParseDoctypeLiteral(ctxt);
3572
0
  if (publicId == NULL)
3573
0
            goto bogus;
3574
0
        SKIP_BLANKS;
3575
0
  URI = htmlParseDoctypeLiteral(ctxt);
3576
0
    } else if ((UPPER == 'S') && (UPP(1) == 'Y') &&
3577
0
               (UPP(2) == 'S') && (UPP(3) == 'T') &&
3578
0
         (UPP(4) == 'E') && (UPP(5) == 'M')) {
3579
0
        SKIP(6);
3580
0
        SKIP_BLANKS;
3581
0
  URI = htmlParseDoctypeLiteral(ctxt);
3582
0
    }
3583
3584
0
bogus:
3585
0
    htmlSkipBogusDoctype(ctxt);
3586
3587
    /*
3588
     * Create or update the document accordingly to the DOCTYPE
3589
     */
3590
0
    if ((ctxt->sax != NULL) && (ctxt->sax->internalSubset != NULL) &&
3591
0
  (!ctxt->disableSAX))
3592
0
  ctxt->sax->internalSubset(ctxt->userData, name, publicId, URI);
3593
3594
0
    xmlFree(name);
3595
0
    xmlFree(URI);
3596
0
    xmlFree(publicId);
3597
0
}
3598
3599
/**
3600
 * htmlParseAttribute:
3601
 * @ctxt:  an HTML parser context
3602
 * @value:  a xmlChar ** used to store the value of the attribute
3603
 *
3604
 * parse an attribute
3605
 *
3606
 * [41] Attribute ::= Name Eq AttValue
3607
 *
3608
 * [25] Eq ::= S? '=' S?
3609
 *
3610
 * With namespace:
3611
 *
3612
 * [NS 11] Attribute ::= QName Eq AttValue
3613
 *
3614
 * Also the case QName == xmlns:??? is handled independently as a namespace
3615
 * definition.
3616
 *
3617
 * Returns the attribute name, and the value in *value.
3618
 */
3619
3620
static xmlHashedString
3621
0
htmlParseAttribute(htmlParserCtxtPtr ctxt, xmlChar **value) {
3622
0
    xmlHashedString hname;
3623
0
    xmlChar *val = NULL;
3624
3625
0
    *value = NULL;
3626
0
    hname = htmlParseHTMLName(ctxt, 1);
3627
0
    if (hname.name == NULL)
3628
0
        return(hname);
3629
3630
    /*
3631
     * read the value
3632
     */
3633
0
    SKIP_BLANKS;
3634
0
    if (CUR == '=') {
3635
0
        SKIP(1);
3636
0
  SKIP_BLANKS;
3637
0
  val = htmlParseAttValue(ctxt);
3638
0
    }
3639
3640
0
    *value = val;
3641
0
    return(hname);
3642
0
}
3643
3644
/**
3645
 * htmlCheckEncoding:
3646
 * @ctxt:  an HTML parser context
3647
 * @attvalue: the attribute value
3648
 *
3649
 * Checks an http-equiv attribute from a Meta tag to detect
3650
 * the encoding
3651
 * If a new encoding is detected the parser is switched to decode
3652
 * it and pass UTF8
3653
 */
3654
static void
3655
0
htmlCheckEncoding(htmlParserCtxtPtr ctxt, const xmlChar *attvalue) {
3656
0
    const xmlChar *encoding;
3657
0
    xmlChar *copy;
3658
3659
0
    if (!attvalue)
3660
0
  return;
3661
3662
0
    encoding = xmlStrcasestr(attvalue, BAD_CAST"charset");
3663
0
    if (encoding != NULL) {
3664
0
  encoding += 7;
3665
0
    }
3666
    /*
3667
     * skip blank
3668
     */
3669
0
    if (encoding && IS_WS_HTML(*encoding))
3670
0
  encoding = xmlStrcasestr(attvalue, BAD_CAST"=");
3671
0
    if (encoding && *encoding == '=') {
3672
0
  encoding ++;
3673
0
        copy = xmlStrdup(encoding);
3674
0
        if (copy == NULL)
3675
0
            htmlErrMemory(ctxt);
3676
0
  xmlSetDeclaredEncoding(ctxt, copy);
3677
0
    }
3678
0
}
3679
3680
/**
3681
 * htmlCheckMeta:
3682
 * @ctxt:  an HTML parser context
3683
 * @atts:  the attributes values
3684
 *
3685
 * Checks an attributes from a Meta tag
3686
 */
3687
static void
3688
0
htmlCheckMeta(htmlParserCtxtPtr ctxt, const xmlChar **atts) {
3689
0
    int i;
3690
0
    const xmlChar *att, *value;
3691
0
    int http = 0;
3692
0
    const xmlChar *content = NULL;
3693
3694
0
    if ((ctxt == NULL) || (atts == NULL))
3695
0
  return;
3696
3697
0
    i = 0;
3698
0
    att = atts[i++];
3699
0
    while (att != NULL) {
3700
0
  value = atts[i++];
3701
0
        if (value != NULL) {
3702
0
            if ((!xmlStrcasecmp(att, BAD_CAST "http-equiv")) &&
3703
0
                (!xmlStrcasecmp(value, BAD_CAST "Content-Type"))) {
3704
0
                http = 1;
3705
0
            } else if (!xmlStrcasecmp(att, BAD_CAST "charset")) {
3706
0
                xmlChar *copy;
3707
3708
0
                copy = xmlStrdup(value);
3709
0
                if (copy == NULL)
3710
0
                    htmlErrMemory(ctxt);
3711
0
                xmlSetDeclaredEncoding(ctxt, copy);
3712
0
            } else if (!xmlStrcasecmp(att, BAD_CAST "content")) {
3713
0
                content = value;
3714
0
            }
3715
0
        }
3716
0
  att = atts[i++];
3717
0
    }
3718
0
    if ((http) && (content != NULL))
3719
0
  htmlCheckEncoding(ctxt, content);
3720
3721
0
}
3722
3723
/**
3724
 * htmlAttrHashInsert:
3725
 * @ctxt: parser context
3726
 * @size: size of the hash table
3727
 * @name: attribute name
3728
 * @hashValue: hash value of name
3729
 * @aindex: attribute index (this is a multiple of 5)
3730
 *
3731
 * Inserts a new attribute into the hash table.
3732
 *
3733
 * Returns INT_MAX if no existing attribute was found, the attribute
3734
 * index if an attribute was found, -1 if a memory allocation failed.
3735
 */
3736
static int
3737
htmlAttrHashInsert(xmlParserCtxtPtr ctxt, unsigned size, const xmlChar *name,
3738
0
                   unsigned hashValue, int aindex) {
3739
0
    xmlAttrHashBucket *table = ctxt->attrHash;
3740
0
    xmlAttrHashBucket *bucket;
3741
0
    unsigned hindex;
3742
3743
0
    hindex = hashValue & (size - 1);
3744
0
    bucket = &table[hindex];
3745
3746
0
    while (bucket->index >= 0) {
3747
0
        const xmlChar **atts = &ctxt->atts[bucket->index];
3748
3749
0
        if (name == atts[0])
3750
0
            return(bucket->index);
3751
3752
0
        hindex++;
3753
0
        bucket++;
3754
0
        if (hindex >= size) {
3755
0
            hindex = 0;
3756
0
            bucket = table;
3757
0
        }
3758
0
    }
3759
3760
0
    bucket->index = aindex;
3761
3762
0
    return(INT_MAX);
3763
0
}
3764
3765
/**
3766
 * htmlParseStartTag:
3767
 * @ctxt:  an HTML parser context
3768
 *
3769
 * parse a start of tag either for rule element or
3770
 * EmptyElement. In both case we don't parse the tag closing chars.
3771
 *
3772
 * [40] STag ::= '<' Name (S Attribute)* S? '>'
3773
 *
3774
 * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
3775
 *
3776
 * With namespace:
3777
 *
3778
 * [NS 8] STag ::= '<' QName (S Attribute)* S? '>'
3779
 *
3780
 * [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>'
3781
 *
3782
 * Returns 0 in case of success, -1 in case of error and 1 if discarded
3783
 */
3784
3785
static void
3786
0
htmlParseStartTag(htmlParserCtxtPtr ctxt) {
3787
0
    const xmlChar *name;
3788
0
    const xmlChar *attname;
3789
0
    xmlChar *attvalue;
3790
0
    const xmlChar **atts;
3791
0
    int nbatts = 0;
3792
0
    int maxatts;
3793
0
    int meta = 0;
3794
0
    int i;
3795
0
    int discardtag = 0;
3796
3797
0
    ctxt->endCheckState = 0;
3798
3799
0
    SKIP(1);
3800
3801
0
    atts = ctxt->atts;
3802
0
    maxatts = ctxt->maxatts;
3803
3804
0
    GROW;
3805
0
    name = htmlParseHTMLName(ctxt, 0).name;
3806
0
    if (name == NULL)
3807
0
        return;
3808
0
    if (xmlStrEqual(name, BAD_CAST"meta"))
3809
0
  meta = 1;
3810
3811
0
    if ((ctxt->options & HTML_PARSE_HTML5) == 0) {
3812
        /*
3813
         * Check for auto-closure of HTML elements.
3814
         */
3815
0
        htmlAutoClose(ctxt, name);
3816
3817
        /*
3818
         * Check for implied HTML elements.
3819
         */
3820
0
        htmlCheckImplied(ctxt, name);
3821
3822
        /*
3823
         * Avoid html at any level > 0, head at any level != 1
3824
         * or any attempt to recurse body
3825
         */
3826
0
        if ((ctxt->nameNr > 0) && (xmlStrEqual(name, BAD_CAST"html"))) {
3827
0
            htmlParseErr(ctxt, XML_HTML_STRUCURE_ERROR,
3828
0
                         "htmlParseStartTag: misplaced <html> tag\n",
3829
0
                         name, NULL);
3830
0
            discardtag = 1;
3831
0
            ctxt->depth++;
3832
0
        }
3833
0
        if ((ctxt->nameNr != 1) &&
3834
0
            (xmlStrEqual(name, BAD_CAST"head"))) {
3835
0
            htmlParseErr(ctxt, XML_HTML_STRUCURE_ERROR,
3836
0
                         "htmlParseStartTag: misplaced <head> tag\n",
3837
0
                         name, NULL);
3838
0
            discardtag = 1;
3839
0
            ctxt->depth++;
3840
0
        }
3841
0
        if (xmlStrEqual(name, BAD_CAST"body")) {
3842
0
            int indx;
3843
0
            for (indx = 0;indx < ctxt->nameNr;indx++) {
3844
0
                if (xmlStrEqual(ctxt->nameTab[indx], BAD_CAST"body")) {
3845
0
                    htmlParseErr(ctxt, XML_HTML_STRUCURE_ERROR,
3846
0
                                 "htmlParseStartTag: misplaced <body> tag\n",
3847
0
                                 name, NULL);
3848
0
                    discardtag = 1;
3849
0
                    ctxt->depth++;
3850
0
                }
3851
0
            }
3852
0
        }
3853
0
    }
3854
3855
    /*
3856
     * Now parse the attributes, it ends up with the ending
3857
     *
3858
     * (S Attribute)* S?
3859
     */
3860
0
    SKIP_BLANKS;
3861
0
    while ((ctxt->input->cur < ctxt->input->end) &&
3862
0
           (CUR != '>') &&
3863
0
     ((CUR != '/') || (NXT(1) != '>')) &&
3864
0
           (PARSER_STOPPED(ctxt) == 0)) {
3865
0
        xmlHashedString hattname;
3866
3867
        /*  unexpected-solidus-in-tag */
3868
0
        if (CUR == '/') {
3869
0
            SKIP(1);
3870
0
            SKIP_BLANKS;
3871
0
            continue;
3872
0
        }
3873
0
  GROW;
3874
0
  hattname = htmlParseAttribute(ctxt, &attvalue);
3875
0
        attname = hattname.name;
3876
3877
0
        if (attname != NULL) {
3878
      /*
3879
       * Add the pair to atts
3880
       */
3881
0
      if (nbatts + 4 > maxatts) {
3882
0
          const xmlChar **tmp;
3883
0
                unsigned *utmp;
3884
0
                int newSize;
3885
3886
0
                newSize = xmlGrowCapacity(maxatts,
3887
0
                                          sizeof(tmp[0]) * 2 + sizeof(utmp[0]),
3888
0
                                          11, HTML_MAX_ATTRS);
3889
0
    if (newSize < 0) {
3890
0
        htmlErrMemory(ctxt);
3891
0
        goto failed;
3892
0
    }
3893
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
3894
0
                if (newSize < 2)
3895
0
                    newSize = 2;
3896
0
#endif
3897
0
          tmp = xmlRealloc(atts, newSize * sizeof(tmp[0]) * 2);
3898
0
    if (tmp == NULL) {
3899
0
        htmlErrMemory(ctxt);
3900
0
        goto failed;
3901
0
    }
3902
0
                atts = tmp;
3903
0
    ctxt->atts = tmp;
3904
3905
0
          utmp = xmlRealloc(ctxt->attallocs, newSize * sizeof(utmp[0]));
3906
0
    if (utmp == NULL) {
3907
0
        htmlErrMemory(ctxt);
3908
0
        goto failed;
3909
0
    }
3910
0
                ctxt->attallocs = utmp;
3911
3912
0
                maxatts = newSize * 2;
3913
0
    ctxt->maxatts = maxatts;
3914
0
      }
3915
3916
0
            ctxt->attallocs[nbatts/2] = hattname.hashValue;
3917
0
      atts[nbatts++] = attname;
3918
0
      atts[nbatts++] = attvalue;
3919
3920
0
            attvalue = NULL;
3921
0
  }
3922
3923
0
failed:
3924
0
        if (attvalue != NULL)
3925
0
            xmlFree(attvalue);
3926
3927
0
  SKIP_BLANKS;
3928
0
    }
3929
3930
0
    if (ctxt->input->cur >= ctxt->input->end) {
3931
0
        discardtag = 1;
3932
0
        goto done;
3933
0
    }
3934
3935
    /*
3936
     * Verify that attribute names are unique.
3937
     */
3938
0
    if (nbatts > 2) {
3939
0
        unsigned attrHashSize;
3940
0
        int j, k;
3941
3942
0
        attrHashSize = 4;
3943
0
        while (attrHashSize / 2 < (unsigned) nbatts / 2)
3944
0
            attrHashSize *= 2;
3945
3946
0
        if (attrHashSize > ctxt->attrHashMax) {
3947
0
            xmlAttrHashBucket *tmp;
3948
3949
0
            tmp = xmlRealloc(ctxt->attrHash, attrHashSize * sizeof(tmp[0]));
3950
0
            if (tmp == NULL) {
3951
0
                htmlErrMemory(ctxt);
3952
0
                goto done;
3953
0
            }
3954
3955
0
            ctxt->attrHash = tmp;
3956
0
            ctxt->attrHashMax = attrHashSize;
3957
0
        }
3958
3959
0
        memset(ctxt->attrHash, -1, attrHashSize * sizeof(ctxt->attrHash[0]));
3960
3961
0
        for (i = 0, j = 0, k = 0; i < nbatts; i += 2, k++) {
3962
0
            unsigned hashValue;
3963
0
            int res;
3964
3965
0
            attname = atts[i];
3966
0
            hashValue = ctxt->attallocs[k] | 0x80000000;
3967
3968
0
            res = htmlAttrHashInsert(ctxt, attrHashSize, attname,
3969
0
                                    hashValue, j);
3970
0
            if (res < 0)
3971
0
                continue;
3972
3973
0
            if (res == INT_MAX) {
3974
0
                atts[j] = atts[i];
3975
0
                atts[j+1] = atts[i+1];
3976
0
                j += 2;
3977
0
            } else {
3978
0
                xmlFree((xmlChar *) atts[i+1]);
3979
0
            }
3980
0
        }
3981
3982
0
        nbatts = j;
3983
0
    }
3984
3985
0
    if (nbatts > 0) {
3986
0
        atts[nbatts] = NULL;
3987
0
        atts[nbatts + 1] = NULL;
3988
3989
    /*
3990
     * Apple's new libiconv is so broken that you routinely run into
3991
     * issues when fuzz testing (by accident with an uninstrumented
3992
     * libiconv). Here's a harmless (?) example:
3993
     *
3994
     * printf '>'             | iconv -f shift_jis -t utf-8 | hexdump -C
3995
     * printf '\xfc\x00\x00'  | iconv -f shift_jis -t utf-8 | hexdump -C
3996
     * printf '>\xfc\x00\x00' | iconv -f shift_jis -t utf-8 | hexdump -C
3997
     *
3998
     * The last command fails to detect the illegal sequence.
3999
     */
4000
0
#if !defined(__APPLE__) || \
4001
0
    !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
4002
        /*
4003
         * Handle specific association to the META tag
4004
         */
4005
0
        if (meta)
4006
0
            htmlCheckMeta(ctxt, atts);
4007
0
#endif
4008
0
    }
4009
4010
    /*
4011
     * SAX: Start of Element !
4012
     */
4013
0
    if (!discardtag) {
4014
0
        if (ctxt->options & HTML_PARSE_HTML5) {
4015
0
            if (ctxt->nameNr > 0)
4016
0
                htmlnamePop(ctxt);
4017
0
        }
4018
4019
0
  htmlnamePush(ctxt, name);
4020
0
  if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL)) {
4021
0
      if (nbatts != 0)
4022
0
    ctxt->sax->startElement(ctxt->userData, name, atts);
4023
0
      else
4024
0
    ctxt->sax->startElement(ctxt->userData, name, NULL);
4025
0
  }
4026
0
    }
4027
4028
0
done:
4029
0
    if (atts != NULL) {
4030
0
        for (i = 1;i < nbatts;i += 2) {
4031
0
      if (atts[i] != NULL)
4032
0
    xmlFree((xmlChar *) atts[i]);
4033
0
  }
4034
0
    }
4035
0
}
4036
4037
/**
4038
 * htmlParseEndTag:
4039
 * @ctxt:  an HTML parser context
4040
 *
4041
 * parse an end of tag
4042
 *
4043
 * [42] ETag ::= '</' Name S? '>'
4044
 *
4045
 * With namespace
4046
 *
4047
 * [NS 9] ETag ::= '</' QName S? '>'
4048
 *
4049
 * Returns 1 if the current level should be closed.
4050
 */
4051
4052
static void
4053
htmlParseEndTag(htmlParserCtxtPtr ctxt)
4054
0
{
4055
0
    const xmlChar *name;
4056
0
    const xmlChar *oldname;
4057
0
    int i;
4058
4059
0
    ctxt->endCheckState = 0;
4060
4061
0
    SKIP(2);
4062
4063
0
    if (ctxt->input->cur >= ctxt->input->end) {
4064
0
        htmlStartCharData(ctxt);
4065
0
        if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
4066
0
            (ctxt->sax->characters != NULL))
4067
0
            ctxt->sax->characters(ctxt->userData,
4068
0
                                  BAD_CAST "</", 2);
4069
0
        return;
4070
0
    }
4071
4072
0
    if (CUR == '>') {
4073
0
        SKIP(1);
4074
0
        return;
4075
0
    }
4076
4077
0
    if (!IS_ASCII_LETTER(CUR)) {
4078
0
        htmlParseComment(ctxt, /* bogus */ 1);
4079
0
        return;
4080
0
    }
4081
4082
0
    name = htmlParseHTMLName(ctxt, 0).name;
4083
0
    if (name == NULL)
4084
0
        return;
4085
4086
    /*
4087
     * Parse and ignore attributes.
4088
     */
4089
0
    SKIP_BLANKS;
4090
0
    while ((ctxt->input->cur < ctxt->input->end) &&
4091
0
           (CUR != '>') &&
4092
0
     ((CUR != '/') || (NXT(1) != '>')) &&
4093
0
           (ctxt->instate != XML_PARSER_EOF)) {
4094
0
        xmlChar *attvalue = NULL;
4095
4096
        /*  unexpected-solidus-in-tag */
4097
0
        if (CUR == '/') {
4098
0
            SKIP(1);
4099
0
            SKIP_BLANKS;
4100
0
            continue;
4101
0
        }
4102
0
  GROW;
4103
0
  htmlParseAttribute(ctxt, &attvalue);
4104
0
        if (attvalue != NULL)
4105
0
            xmlFree(attvalue);
4106
4107
0
  SKIP_BLANKS;
4108
0
    }
4109
4110
0
    if (CUR == '>') {
4111
0
        SKIP(1);
4112
0
    } else if ((CUR == '/') && (NXT(1) == '>')) {
4113
0
        SKIP(2);
4114
0
    } else {
4115
0
        return;
4116
0
    }
4117
4118
0
    if (ctxt->options & HTML_PARSE_HTML5) {
4119
0
        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4120
0
            ctxt->sax->endElement(ctxt->userData, name);
4121
0
        return;
4122
0
    }
4123
4124
    /*
4125
     * if we ignored misplaced tags in htmlParseStartTag don't pop them
4126
     * out now.
4127
     */
4128
0
    if ((ctxt->depth > 0) &&
4129
0
        (xmlStrEqual(name, BAD_CAST "html") ||
4130
0
         xmlStrEqual(name, BAD_CAST "body") ||
4131
0
   xmlStrEqual(name, BAD_CAST "head"))) {
4132
0
  ctxt->depth--;
4133
0
  return;
4134
0
    }
4135
4136
    /*
4137
     * If the name read is not one of the element in the parsing stack
4138
     * then return, it's just an error.
4139
     */
4140
0
    for (i = (ctxt->nameNr - 1); i >= 0; i--) {
4141
0
        if (xmlStrEqual(name, ctxt->nameTab[i]))
4142
0
            break;
4143
0
    }
4144
0
    if (i < 0) {
4145
0
        htmlParseErr(ctxt, XML_ERR_TAG_NAME_MISMATCH,
4146
0
               "Unexpected end tag : %s\n", name, NULL);
4147
0
        return;
4148
0
    }
4149
4150
4151
    /*
4152
     * Check for auto-closure of HTML elements.
4153
     */
4154
4155
0
    htmlAutoCloseOnClose(ctxt, name);
4156
4157
    /*
4158
     * Well formedness constraints, opening and closing must match.
4159
     * With the exception that the autoclose may have popped stuff out
4160
     * of the stack.
4161
     */
4162
0
    if ((ctxt->name != NULL) && (!xmlStrEqual(ctxt->name, name))) {
4163
0
        htmlParseErr(ctxt, XML_ERR_TAG_NAME_MISMATCH,
4164
0
                     "Opening and ending tag mismatch: %s and %s\n",
4165
0
                     name, ctxt->name);
4166
0
    }
4167
4168
    /*
4169
     * SAX: End of Tag
4170
     */
4171
0
    oldname = ctxt->name;
4172
0
    if ((oldname != NULL) && (xmlStrEqual(oldname, name))) {
4173
0
  htmlParserFinishElementParsing(ctxt);
4174
0
        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4175
0
            ctxt->sax->endElement(ctxt->userData, name);
4176
0
        htmlnamePop(ctxt);
4177
0
    }
4178
0
}
4179
4180
/**
4181
 * htmlParseContent:
4182
 * @ctxt:  an HTML parser context
4183
 *
4184
 * Parse a content: comment, sub-element, reference or text.
4185
 * New version for non recursive htmlParseElementInternal
4186
 */
4187
4188
static void
4189
0
htmlParseContent(htmlParserCtxtPtr ctxt) {
4190
0
    GROW;
4191
4192
0
    while ((PARSER_STOPPED(ctxt) == 0) &&
4193
0
           (ctxt->input->cur < ctxt->input->end)) {
4194
0
        int mode;
4195
4196
0
        mode = ctxt->endCheckState;
4197
4198
0
        if ((mode == 0) && (CUR == '<')) {
4199
0
            if (NXT(1) == '/') {
4200
0
          htmlParseEndTag(ctxt);
4201
0
            } else if (NXT(1) == '!') {
4202
                /*
4203
                 * Sometimes DOCTYPE arrives in the middle of the document
4204
                 */
4205
0
                if ((UPP(2) == 'D') && (UPP(3) == 'O') &&
4206
0
                    (UPP(4) == 'C') && (UPP(5) == 'T') &&
4207
0
                    (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4208
0
                    (UPP(8) == 'E')) {
4209
0
                    htmlParseDocTypeDecl(ctxt);
4210
0
                } else if ((NXT(2) == '-') && (NXT(3) == '-')) {
4211
0
                    SKIP(4);
4212
0
                    htmlParseComment(ctxt, /* bogus */ 0);
4213
0
                } else {
4214
0
                    SKIP(2);
4215
0
                    htmlParseComment(ctxt, /* bogus */ 1);
4216
0
                }
4217
0
            } else if (NXT(1) == '?') {
4218
0
                SKIP(1);
4219
0
                htmlParseComment(ctxt, /* bogus */ 1);
4220
0
            } else if (IS_ASCII_LETTER(NXT(1))) {
4221
0
                htmlParseElementInternal(ctxt);
4222
0
            } else {
4223
0
                htmlStartCharData(ctxt);
4224
0
                if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
4225
0
                    (ctxt->sax->characters != NULL))
4226
0
                    ctxt->sax->characters(ctxt->userData, BAD_CAST "<", 1);
4227
0
                SKIP(1);
4228
0
            }
4229
0
        } else {
4230
0
            htmlParseCharData(ctxt, /* partial */ 0);
4231
0
        }
4232
4233
0
        SHRINK;
4234
0
        GROW;
4235
0
    }
4236
4237
0
    if (ctxt->input->cur >= ctxt->input->end)
4238
0
        htmlAutoCloseOnEnd(ctxt);
4239
0
}
4240
4241
/**
4242
 * htmlParseElementInternal:
4243
 * @ctxt:  an HTML parser context
4244
 *
4245
 * parse an HTML element, new version, non recursive
4246
 *
4247
 * [39] element ::= EmptyElemTag | STag content ETag
4248
 *
4249
 * [41] Attribute ::= Name Eq AttValue
4250
 */
4251
4252
static int
4253
0
htmlParseElementInternal(htmlParserCtxtPtr ctxt) {
4254
0
    const xmlChar *name;
4255
0
    const htmlElemDesc * info;
4256
0
    htmlParserNodeInfo node_info = { NULL, 0, 0, 0, 0 };
4257
4258
0
    if ((ctxt == NULL) || (ctxt->input == NULL))
4259
0
  return(0);
4260
4261
    /* Capture start position */
4262
0
    if (ctxt->record_info) {
4263
0
        node_info.begin_pos = ctxt->input->consumed +
4264
0
                          (CUR_PTR - ctxt->input->base);
4265
0
  node_info.begin_line = ctxt->input->line;
4266
0
    }
4267
4268
0
    htmlParseStartTag(ctxt);
4269
0
    name = ctxt->name;
4270
0
    if (name == NULL)
4271
0
        return(0);
4272
4273
0
    if (ctxt->record_info)
4274
0
        htmlNodeInfoPush(ctxt, &node_info);
4275
4276
    /*
4277
     * Check for an Empty Element labeled the XML/SGML way
4278
     */
4279
0
    if ((CUR == '/') && (NXT(1) == '>')) {
4280
0
        SKIP(2);
4281
0
        htmlParserFinishElementParsing(ctxt);
4282
0
        if ((ctxt->options & HTML_PARSE_HTML5) == 0) {
4283
0
            if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4284
0
                ctxt->sax->endElement(ctxt->userData, name);
4285
0
        }
4286
0
  htmlnamePop(ctxt);
4287
0
  return(0);
4288
0
    }
4289
4290
0
    if (CUR != '>')
4291
0
        return(0);
4292
0
    SKIP(1);
4293
4294
    /*
4295
     * Lookup the info for that element.
4296
     */
4297
0
    info = htmlTagLookup(name);
4298
4299
    /*
4300
     * Check for an Empty Element from DTD definition
4301
     */
4302
0
    if ((info != NULL) && (info->empty)) {
4303
0
        htmlParserFinishElementParsing(ctxt);
4304
0
        if ((ctxt->options & HTML_PARSE_HTML5) == 0) {
4305
0
            if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
4306
0
                ctxt->sax->endElement(ctxt->userData, name);
4307
0
        }
4308
0
  htmlnamePop(ctxt);
4309
0
  return(0);
4310
0
    }
4311
4312
0
    if (info != NULL)
4313
0
        ctxt->endCheckState = info->dataMode;
4314
4315
0
    return(1);
4316
0
}
4317
4318
/**
4319
 * htmlParseElement:
4320
 * @ctxt:  an HTML parser context
4321
 *
4322
 * DEPRECATED: Internal function, don't use.
4323
 *
4324
 * parse an HTML element, this is highly recursive
4325
 * this is kept for compatibility with previous code versions
4326
 *
4327
 * [39] element ::= EmptyElemTag | STag content ETag
4328
 *
4329
 * [41] Attribute ::= Name Eq AttValue
4330
 */
4331
4332
void
4333
0
htmlParseElement(htmlParserCtxtPtr ctxt) {
4334
0
    const xmlChar *oldptr;
4335
0
    int depth;
4336
4337
0
    if ((ctxt == NULL) || (ctxt->input == NULL))
4338
0
  return;
4339
4340
0
    if (htmlParseElementInternal(ctxt) == 0)
4341
0
        return;
4342
4343
    /*
4344
     * Parse the content of the element:
4345
     */
4346
0
    depth = ctxt->nameNr;
4347
0
    while (CUR != 0) {
4348
0
  oldptr = ctxt->input->cur;
4349
0
  htmlParseContent(ctxt);
4350
0
  if (oldptr==ctxt->input->cur) break;
4351
0
  if (ctxt->nameNr < depth) break;
4352
0
    }
4353
4354
0
    if (CUR == 0) {
4355
0
  htmlAutoCloseOnEnd(ctxt);
4356
0
    }
4357
0
}
4358
4359
/**
4360
 * htmlCtxtParseContentInternal:
4361
 * @ctxt:  parser context
4362
 * @input:  parser input
4363
 *
4364
 * Returns a node list.
4365
 */
4366
xmlNodePtr
4367
0
htmlCtxtParseContentInternal(htmlParserCtxtPtr ctxt, xmlParserInputPtr input) {
4368
0
    xmlNodePtr root;
4369
0
    xmlNodePtr list = NULL;
4370
0
    xmlChar *rootName = BAD_CAST "#root";
4371
4372
0
    root = xmlNewDocNode(ctxt->myDoc, NULL, rootName, NULL);
4373
0
    if (root == NULL) {
4374
0
        htmlErrMemory(ctxt);
4375
0
        return(NULL);
4376
0
    }
4377
4378
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
4379
0
        xmlFreeNode(root);
4380
0
        return(NULL);
4381
0
    }
4382
4383
0
    htmlnamePush(ctxt, rootName);
4384
0
    nodePush(ctxt, root);
4385
4386
0
    htmlParseContent(ctxt);
4387
4388
    /*
4389
     * Only check for truncated multi-byte sequences
4390
     */
4391
0
    xmlParserCheckEOF(ctxt, XML_ERR_INTERNAL_ERROR);
4392
4393
    /* TODO: Use xmlCtxtIsCatastrophicError */
4394
0
    if (ctxt->errNo != XML_ERR_NO_MEMORY) {
4395
0
        xmlNodePtr cur;
4396
4397
        /*
4398
         * Unlink newly created node list.
4399
         */
4400
0
        list = root->children;
4401
0
        root->children = NULL;
4402
0
        root->last = NULL;
4403
0
        for (cur = list; cur != NULL; cur = cur->next)
4404
0
            cur->parent = NULL;
4405
0
    }
4406
4407
0
    nodePop(ctxt);
4408
0
    htmlnamePop(ctxt);
4409
4410
0
    xmlCtxtPopInput(ctxt);
4411
4412
0
    xmlFreeNode(root);
4413
0
    return(list);
4414
0
}
4415
4416
/**
4417
 * htmlParseDocument:
4418
 * @ctxt:  an HTML parser context
4419
 *
4420
 * Parse an HTML document and invoke the SAX handlers. This is useful
4421
 * if you're only interested in custom SAX callbacks. If you want a
4422
 * document tree, use htmlCtxtParseDocument.
4423
 *
4424
 * Returns 0, -1 in case of error.
4425
 */
4426
4427
int
4428
0
htmlParseDocument(htmlParserCtxtPtr ctxt) {
4429
0
    xmlDtdPtr dtd;
4430
4431
0
    if ((ctxt == NULL) || (ctxt->input == NULL))
4432
0
  return(-1);
4433
4434
0
    if ((ctxt->sax) && (ctxt->sax->setDocumentLocator)) {
4435
0
        ctxt->sax->setDocumentLocator(ctxt->userData,
4436
0
                (xmlSAXLocator *) &xmlDefaultSAXLocator);
4437
0
    }
4438
4439
0
    xmlDetectEncoding(ctxt);
4440
4441
    /*
4442
     * TODO: Implement HTML5 prescan algorithm
4443
     */
4444
4445
    /*
4446
     * This is wrong but matches long-standing behavior. In most
4447
     * cases, a document starting with an XML declaration will
4448
     * specify UTF-8. The HTML5 prescan algorithm handles
4449
     * XML declarations in a better way.
4450
     */
4451
0
    if (((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) &&
4452
0
        (xmlStrncmp(ctxt->input->cur, BAD_CAST "<?xm", 4) == 0))
4453
0
        xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_UTF8);
4454
4455
    /*
4456
     * Wipe out everything which is before the first '<'
4457
     */
4458
0
    SKIP_BLANKS;
4459
4460
0
    if ((ctxt->sax) && (ctxt->sax->startDocument) && (!ctxt->disableSAX))
4461
0
  ctxt->sax->startDocument(ctxt->userData);
4462
4463
    /*
4464
     * Parse possible comments and PIs before any content
4465
     */
4466
0
    while (CUR == '<') {
4467
0
        if ((NXT(1) == '!') && (NXT(2) == '-') && (NXT(3) == '-')) {
4468
0
            SKIP(4);
4469
0
            htmlParseComment(ctxt, /* bogus */ 0);
4470
0
        } else if (NXT(1) == '?') {
4471
0
            SKIP(1);
4472
0
            htmlParseComment(ctxt, /* bogus */ 1);
4473
0
        } else {
4474
0
            break;
4475
0
        }
4476
0
  SKIP_BLANKS;
4477
0
    }
4478
4479
    /*
4480
     * Then possibly doc type declaration(s) and more Misc
4481
     * (doctypedecl Misc*)?
4482
     */
4483
0
    if ((CUR == '<') && (NXT(1) == '!') &&
4484
0
  (UPP(2) == 'D') && (UPP(3) == 'O') &&
4485
0
  (UPP(4) == 'C') && (UPP(5) == 'T') &&
4486
0
  (UPP(6) == 'Y') && (UPP(7) == 'P') &&
4487
0
  (UPP(8) == 'E')) {
4488
0
        ctxt->instate = XML_PARSER_MISC;
4489
0
  htmlParseDocTypeDecl(ctxt);
4490
0
    }
4491
0
    SKIP_BLANKS;
4492
4493
    /*
4494
     * Parse possible comments and PIs before any content
4495
     */
4496
0
    ctxt->instate = XML_PARSER_PROLOG;
4497
0
    while (CUR == '<') {
4498
0
        if ((NXT(1) == '!') && (NXT(2) == '-') && (NXT(3) == '-')) {
4499
0
            SKIP(4);
4500
0
            htmlParseComment(ctxt, /* bogus */ 0);
4501
0
        } else if (NXT(1) == '?') {
4502
0
            SKIP(1);
4503
0
            htmlParseComment(ctxt, /* bogus */ 1);
4504
0
        } else {
4505
0
            break;
4506
0
        }
4507
0
  SKIP_BLANKS;
4508
0
    }
4509
4510
    /*
4511
     * Time to start parsing the tree itself
4512
     */
4513
0
    ctxt->instate = XML_PARSER_CONTENT;
4514
0
    htmlParseContent(ctxt);
4515
4516
    /*
4517
     * Only check for truncated multi-byte sequences
4518
     */
4519
0
    xmlParserCheckEOF(ctxt, XML_ERR_INTERNAL_ERROR);
4520
4521
    /*
4522
     * SAX: end of the document processing.
4523
     */
4524
0
    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
4525
0
        ctxt->sax->endDocument(ctxt->userData);
4526
4527
0
    if ((!(ctxt->options & HTML_PARSE_NODEFDTD)) && (ctxt->myDoc != NULL)) {
4528
0
  dtd = xmlGetIntSubset(ctxt->myDoc);
4529
0
  if (dtd == NULL) {
4530
0
      ctxt->myDoc->intSubset =
4531
0
    xmlCreateIntSubset(ctxt->myDoc, BAD_CAST "html",
4532
0
        BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN",
4533
0
        BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd");
4534
0
            if (ctxt->myDoc->intSubset == NULL)
4535
0
                htmlErrMemory(ctxt);
4536
0
        }
4537
0
    }
4538
0
    if (! ctxt->wellFormed) return(-1);
4539
0
    return(0);
4540
0
}
4541
4542
4543
/************************************************************************
4544
 *                  *
4545
 *      Parser contexts handling      *
4546
 *                  *
4547
 ************************************************************************/
4548
4549
/**
4550
 * htmlInitParserCtxt:
4551
 * @ctxt:  an HTML parser context
4552
 * @sax:  SAX handler
4553
 * @userData:  user data
4554
 *
4555
 * Initialize a parser context
4556
 *
4557
 * Returns 0 in case of success and -1 in case of error
4558
 */
4559
4560
static int
4561
htmlInitParserCtxt(htmlParserCtxtPtr ctxt, const htmlSAXHandler *sax,
4562
                   void *userData)
4563
0
{
4564
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
4565
0
    size_t initialNodeTabSize = 1;
4566
#else
4567
    size_t initialNodeTabSize = 10;
4568
#endif
4569
4570
0
    if (ctxt == NULL) return(-1);
4571
0
    memset(ctxt, 0, sizeof(htmlParserCtxt));
4572
4573
0
    ctxt->dict = xmlDictCreate();
4574
0
    if (ctxt->dict == NULL)
4575
0
  return(-1);
4576
4577
0
    if (ctxt->sax == NULL)
4578
0
        ctxt->sax = (htmlSAXHandler *) xmlMalloc(sizeof(htmlSAXHandler));
4579
0
    if (ctxt->sax == NULL)
4580
0
  return(-1);
4581
0
    if (sax == NULL) {
4582
0
        memset(ctxt->sax, 0, sizeof(htmlSAXHandler));
4583
0
        xmlSAX2InitHtmlDefaultSAXHandler(ctxt->sax);
4584
0
        ctxt->userData = ctxt;
4585
0
    } else {
4586
0
        memcpy(ctxt->sax, sax, sizeof(htmlSAXHandler));
4587
0
        ctxt->userData = userData ? userData : ctxt;
4588
0
    }
4589
4590
    /* Allocate the Input stack */
4591
0
    ctxt->inputTab = (htmlParserInputPtr *)
4592
0
                      xmlMalloc(sizeof(htmlParserInputPtr));
4593
0
    if (ctxt->inputTab == NULL)
4594
0
  return(-1);
4595
0
    ctxt->inputNr = 0;
4596
0
    ctxt->inputMax = 1;
4597
0
    ctxt->input = NULL;
4598
0
    ctxt->version = NULL;
4599
0
    ctxt->encoding = NULL;
4600
0
    ctxt->standalone = -1;
4601
0
    ctxt->instate = XML_PARSER_START;
4602
4603
    /* Allocate the Node stack */
4604
0
    ctxt->nodeTab = xmlMalloc(initialNodeTabSize * sizeof(htmlNodePtr));
4605
0
    if (ctxt->nodeTab == NULL)
4606
0
  return(-1);
4607
0
    ctxt->nodeNr = 0;
4608
0
    ctxt->nodeMax = initialNodeTabSize;
4609
0
    ctxt->node = NULL;
4610
4611
    /* Allocate the Name stack */
4612
0
    ctxt->nameTab = xmlMalloc(initialNodeTabSize * sizeof(xmlChar *));
4613
0
    if (ctxt->nameTab == NULL)
4614
0
  return(-1);
4615
0
    ctxt->nameNr = 0;
4616
0
    ctxt->nameMax = initialNodeTabSize;
4617
0
    ctxt->name = NULL;
4618
4619
0
    ctxt->nodeInfoTab = NULL;
4620
0
    ctxt->nodeInfoNr  = 0;
4621
0
    ctxt->nodeInfoMax = 0;
4622
4623
0
    ctxt->myDoc = NULL;
4624
0
    ctxt->wellFormed = 1;
4625
0
    ctxt->replaceEntities = 0;
4626
0
    ctxt->linenumbers = xmlLineNumbersDefaultValue;
4627
0
    ctxt->keepBlanks = xmlKeepBlanksDefaultValue;
4628
0
    ctxt->html = INSERT_INITIAL;
4629
0
    ctxt->vctxt.flags = XML_VCTXT_USE_PCTXT;
4630
0
    ctxt->vctxt.userData = ctxt;
4631
0
    ctxt->vctxt.error = xmlParserValidityError;
4632
0
    ctxt->vctxt.warning = xmlParserValidityWarning;
4633
0
    ctxt->record_info = 0;
4634
0
    ctxt->validate = 0;
4635
0
    ctxt->checkIndex = 0;
4636
0
    ctxt->catalogs = NULL;
4637
0
    xmlInitNodeInfoSeq(&ctxt->node_seq);
4638
0
    return(0);
4639
0
}
4640
4641
/**
4642
 * htmlFreeParserCtxt:
4643
 * @ctxt:  an HTML parser context
4644
 *
4645
 * Free all the memory used by a parser context. However the parsed
4646
 * document in ctxt->myDoc is not freed.
4647
 */
4648
4649
void
4650
htmlFreeParserCtxt(htmlParserCtxtPtr ctxt)
4651
0
{
4652
0
    xmlFreeParserCtxt(ctxt);
4653
0
}
4654
4655
/**
4656
 * htmlNewParserCtxt:
4657
 *
4658
 * Allocate and initialize a new HTML parser context.
4659
 *
4660
 * This can be used to parse HTML documents into DOM trees with
4661
 * functions like xmlCtxtReadFile or xmlCtxtReadMemory.
4662
 *
4663
 * See htmlCtxtUseOptions for parser options.
4664
 *
4665
 * See xmlCtxtSetErrorHandler for advanced error handling.
4666
 *
4667
 * See htmlNewSAXParserCtxt for custom SAX parsers.
4668
 *
4669
 * Returns the htmlParserCtxtPtr or NULL in case of allocation error
4670
 */
4671
4672
htmlParserCtxtPtr
4673
htmlNewParserCtxt(void)
4674
0
{
4675
0
    return(htmlNewSAXParserCtxt(NULL, NULL));
4676
0
}
4677
4678
/**
4679
 * htmlNewSAXParserCtxt:
4680
 * @sax:  SAX handler
4681
 * @userData:  user data
4682
 *
4683
 * Allocate and initialize a new HTML SAX parser context. If userData
4684
 * is NULL, the parser context will be passed as user data.
4685
 *
4686
 * Available since 2.11.0. If you want support older versions,
4687
 * it's best to invoke htmlNewParserCtxt and set ctxt->sax with
4688
 * struct assignment.
4689
 *
4690
 * Also see htmlNewParserCtxt.
4691
 *
4692
 * Returns the htmlParserCtxtPtr or NULL in case of allocation error
4693
 */
4694
4695
htmlParserCtxtPtr
4696
htmlNewSAXParserCtxt(const htmlSAXHandler *sax, void *userData)
4697
0
{
4698
0
    xmlParserCtxtPtr ctxt;
4699
4700
0
    xmlInitParser();
4701
4702
0
    ctxt = (xmlParserCtxtPtr) xmlMalloc(sizeof(xmlParserCtxt));
4703
0
    if (ctxt == NULL)
4704
0
  return(NULL);
4705
0
    memset(ctxt, 0, sizeof(xmlParserCtxt));
4706
0
    if (htmlInitParserCtxt(ctxt, sax, userData) < 0) {
4707
0
        htmlFreeParserCtxt(ctxt);
4708
0
  return(NULL);
4709
0
    }
4710
0
    return(ctxt);
4711
0
}
4712
4713
static htmlParserCtxtPtr
4714
htmlCreateMemoryParserCtxtInternal(const char *url,
4715
                                   const char *buffer, size_t size,
4716
0
                                   const char *encoding) {
4717
0
    xmlParserCtxtPtr ctxt;
4718
0
    xmlParserInputPtr input;
4719
4720
0
    if (buffer == NULL)
4721
0
  return(NULL);
4722
4723
0
    ctxt = htmlNewParserCtxt();
4724
0
    if (ctxt == NULL)
4725
0
  return(NULL);
4726
4727
0
    input = xmlCtxtNewInputFromMemory(ctxt, url, buffer, size, encoding, 0);
4728
0
    if (input == NULL) {
4729
0
  xmlFreeParserCtxt(ctxt);
4730
0
        return(NULL);
4731
0
    }
4732
4733
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
4734
0
        xmlFreeInputStream(input);
4735
0
        xmlFreeParserCtxt(ctxt);
4736
0
        return(NULL);
4737
0
    }
4738
4739
0
    return(ctxt);
4740
0
}
4741
4742
/**
4743
 * htmlCreateMemoryParserCtxt:
4744
 * @buffer:  a pointer to a char array
4745
 * @size:  the size of the array
4746
 *
4747
 * DEPRECATED: Use htmlNewParserCtxt and htmlCtxtReadMemory.
4748
 *
4749
 * Create a parser context for an HTML in-memory document. The input
4750
 * buffer must not contain any terminating null bytes.
4751
 *
4752
 * Returns the new parser context or NULL
4753
 */
4754
htmlParserCtxtPtr
4755
0
htmlCreateMemoryParserCtxt(const char *buffer, int size) {
4756
0
    if (size <= 0)
4757
0
  return(NULL);
4758
4759
0
    return(htmlCreateMemoryParserCtxtInternal(NULL, buffer, size, NULL));
4760
0
}
4761
4762
/**
4763
 * htmlCreateDocParserCtxt:
4764
 * @str:  a pointer to an array of xmlChar
4765
 * @encoding:  encoding (optional)
4766
 *
4767
 * Create a parser context for a null-terminated string.
4768
 *
4769
 * Returns the new parser context or NULL if a memory allocation failed.
4770
 */
4771
static htmlParserCtxtPtr
4772
htmlCreateDocParserCtxt(const xmlChar *str, const char *url,
4773
0
                        const char *encoding) {
4774
0
    xmlParserCtxtPtr ctxt;
4775
0
    xmlParserInputPtr input;
4776
4777
0
    if (str == NULL)
4778
0
  return(NULL);
4779
4780
0
    ctxt = htmlNewParserCtxt();
4781
0
    if (ctxt == NULL)
4782
0
  return(NULL);
4783
4784
0
    input = xmlCtxtNewInputFromString(ctxt, url, (const char *) str,
4785
0
                                      encoding, 0);
4786
0
    if (input == NULL) {
4787
0
  xmlFreeParserCtxt(ctxt);
4788
0
  return(NULL);
4789
0
    }
4790
4791
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
4792
0
        xmlFreeInputStream(input);
4793
0
        xmlFreeParserCtxt(ctxt);
4794
0
        return(NULL);
4795
0
    }
4796
4797
0
    return(ctxt);
4798
0
}
4799
4800
#ifdef LIBXML_PUSH_ENABLED
4801
/************************************************************************
4802
 *                  *
4803
 *  Progressive parsing interfaces        *
4804
 *                  *
4805
 ************************************************************************/
4806
4807
typedef enum {
4808
    LSTATE_TAG_NAME = 0,
4809
    LSTATE_BEFORE_ATTR_NAME,
4810
    LSTATE_ATTR_NAME,
4811
    LSTATE_AFTER_ATTR_NAME,
4812
    LSTATE_BEFORE_ATTR_VALUE,
4813
    LSTATE_ATTR_VALUE_DQUOTED,
4814
    LSTATE_ATTR_VALUE_SQUOTED,
4815
    LSTATE_ATTR_VALUE_UNQUOTED
4816
} xmlLookupStates;
4817
4818
/**
4819
 * htmlParseLookupGt:
4820
 * @ctxt:  an HTML parser context
4821
 *
4822
 * Check whether there's enough data in the input buffer to finish parsing
4823
 * a tag. This has to take quotes into account.
4824
 */
4825
static int
4826
0
htmlParseLookupGt(xmlParserCtxtPtr ctxt) {
4827
0
    const xmlChar *cur;
4828
0
    const xmlChar *end = ctxt->input->end;
4829
0
    int state = ctxt->endCheckState;
4830
0
    size_t index;
4831
4832
0
    if (ctxt->checkIndex == 0)
4833
0
        cur = ctxt->input->cur + 2; /* Skip '<a' or '</' */
4834
0
    else
4835
0
        cur = ctxt->input->cur + ctxt->checkIndex;
4836
4837
0
    while (cur < end) {
4838
0
        int c = *cur++;
4839
4840
0
        if (state != LSTATE_ATTR_VALUE_SQUOTED &&
4841
0
            state != LSTATE_ATTR_VALUE_DQUOTED) {
4842
0
            if (c == '/' &&
4843
0
                state != LSTATE_BEFORE_ATTR_VALUE &&
4844
0
                state != LSTATE_ATTR_VALUE_UNQUOTED) {
4845
0
                state = LSTATE_BEFORE_ATTR_NAME;
4846
0
                continue;
4847
0
            } else if (c == '>') {
4848
0
                ctxt->checkIndex = 0;
4849
0
                ctxt->endCheckState = 0;
4850
0
                return(0);
4851
0
            }
4852
0
        }
4853
4854
0
        switch (state) {
4855
0
            case LSTATE_TAG_NAME:
4856
0
                if (IS_WS_HTML(c))
4857
0
                    state = LSTATE_BEFORE_ATTR_NAME;
4858
0
                break;
4859
4860
0
            case LSTATE_BEFORE_ATTR_NAME:
4861
0
                if (!IS_WS_HTML(c))
4862
0
                    state = LSTATE_ATTR_NAME;
4863
0
                break;
4864
4865
0
            case LSTATE_ATTR_NAME:
4866
0
                if (c == '=')
4867
0
                    state = LSTATE_BEFORE_ATTR_VALUE;
4868
0
                else if (IS_WS_HTML(c))
4869
0
                    state = LSTATE_AFTER_ATTR_NAME;
4870
0
                break;
4871
4872
0
            case LSTATE_AFTER_ATTR_NAME:
4873
0
                if (c == '=')
4874
0
                    state = LSTATE_BEFORE_ATTR_VALUE;
4875
0
                else if (!IS_WS_HTML(c))
4876
0
                    state = LSTATE_ATTR_NAME;
4877
0
                break;
4878
4879
0
            case LSTATE_BEFORE_ATTR_VALUE:
4880
0
                if (c == '"')
4881
0
                    state = LSTATE_ATTR_VALUE_DQUOTED;
4882
0
                else if (c == '\'')
4883
0
                    state = LSTATE_ATTR_VALUE_SQUOTED;
4884
0
                else if (!IS_WS_HTML(c))
4885
0
                    state = LSTATE_ATTR_VALUE_UNQUOTED;
4886
0
                break;
4887
4888
0
            case LSTATE_ATTR_VALUE_DQUOTED:
4889
0
                if (c == '"')
4890
0
                    state = LSTATE_BEFORE_ATTR_NAME;
4891
0
                break;
4892
4893
0
            case LSTATE_ATTR_VALUE_SQUOTED:
4894
0
                if (c == '\'')
4895
0
                    state = LSTATE_BEFORE_ATTR_NAME;
4896
0
                break;
4897
4898
0
            case LSTATE_ATTR_VALUE_UNQUOTED:
4899
0
                if (IS_WS_HTML(c))
4900
0
                    state = LSTATE_BEFORE_ATTR_NAME;
4901
0
                break;
4902
0
        }
4903
0
    }
4904
4905
0
    index = cur - ctxt->input->cur;
4906
0
    if (index > LONG_MAX) {
4907
0
        ctxt->checkIndex = 0;
4908
0
        ctxt->endCheckState = 0;
4909
0
        return(0);
4910
0
    }
4911
0
    ctxt->checkIndex = index;
4912
0
    ctxt->endCheckState = state;
4913
0
    return(-1);
4914
0
}
4915
4916
/**
4917
 * htmlParseLookupString:
4918
 * @ctxt:  an XML parser context
4919
 * @startDelta: delta to apply at the start
4920
 * @str:  string
4921
 * @strLen:  length of string
4922
 *
4923
 * Check whether the input buffer contains a string.
4924
 */
4925
static int
4926
htmlParseLookupString(xmlParserCtxtPtr ctxt, size_t startDelta,
4927
0
                      const char *str, size_t strLen, size_t extraLen) {
4928
0
    const xmlChar *end = ctxt->input->end;
4929
0
    const xmlChar *cur, *term;
4930
0
    size_t index, rescan;
4931
0
    int ret;
4932
4933
0
    if (ctxt->checkIndex == 0) {
4934
0
        cur = ctxt->input->cur + startDelta;
4935
0
    } else {
4936
0
        cur = ctxt->input->cur + ctxt->checkIndex;
4937
0
    }
4938
4939
0
    term = BAD_CAST strstr((const char *) cur, str);
4940
0
    if ((term != NULL) &&
4941
0
        ((size_t) (ctxt->input->end - term) >= extraLen + 1)) {
4942
0
        ctxt->checkIndex = 0;
4943
4944
0
        if (term - ctxt->input->cur > INT_MAX / 2)
4945
0
            ret = INT_MAX / 2;
4946
0
        else
4947
0
            ret = term - ctxt->input->cur;
4948
4949
0
        return(ret);
4950
0
    }
4951
4952
    /* Rescan (strLen + extraLen - 1) characters. */
4953
0
    rescan = strLen + extraLen - 1;
4954
0
    if ((size_t) (end - cur) <= rescan)
4955
0
        end = cur;
4956
0
    else
4957
0
        end -= rescan;
4958
0
    index = end - ctxt->input->cur;
4959
0
    if (index > INT_MAX / 2) {
4960
0
        ctxt->checkIndex = 0;
4961
0
        ret = INT_MAX / 2;
4962
0
    } else {
4963
0
        ctxt->checkIndex = index;
4964
0
        ret = -1;
4965
0
    }
4966
4967
0
    return(ret);
4968
0
}
4969
4970
/**
4971
 * htmlParseLookupCommentEnd:
4972
 * @ctxt: an HTML parser context
4973
 *
4974
 * Try to find a comment end tag in the input stream
4975
 * The search includes "-->" as well as WHATWG-recommended incorrectly-closed tags.
4976
 * (See https://html.spec.whatwg.org/multipage/parsing.html#parse-error-incorrectly-closed-comment)
4977
 * This function has a side effect of (possibly) incrementing ctxt->checkIndex
4978
 * to avoid rescanning sequences of bytes, it DOES change the state of the
4979
 * parser, do not use liberally.
4980
 *
4981
 * Returns the index to the current parsing point if the full sequence is available, -1 otherwise.
4982
 */
4983
static int
4984
htmlParseLookupCommentEnd(htmlParserCtxtPtr ctxt)
4985
0
{
4986
0
    int mark = 0;
4987
0
    int offset;
4988
4989
0
    while (1) {
4990
0
  mark = htmlParseLookupString(ctxt, 2, "--", 2, 0);
4991
0
  if (mark < 0)
4992
0
            break;
4993
        /*
4994
         * <!-->    is a complete comment, but
4995
         * <!--!>   is not
4996
         * <!---!>  is not
4997
         * <!----!> is
4998
         */
4999
0
        if ((NXT(mark+2) == '>') ||
5000
0
      ((mark >= 4) && (NXT(mark+2) == '!') && (NXT(mark+3) == '>'))) {
5001
0
            ctxt->checkIndex = 0;
5002
0
      break;
5003
0
  }
5004
0
        offset = (NXT(mark+2) == '!') ? 3 : 2;
5005
0
        if (mark + offset >= ctxt->input->end - ctxt->input->cur) {
5006
0
      ctxt->checkIndex = mark;
5007
0
            return(-1);
5008
0
        }
5009
0
  ctxt->checkIndex = mark + 1;
5010
0
    }
5011
0
    return mark;
5012
0
}
5013
5014
5015
/**
5016
 * htmlParseTryOrFinish:
5017
 * @ctxt:  an HTML parser context
5018
 * @terminate:  last chunk indicator
5019
 *
5020
 * Try to progress on parsing
5021
 *
5022
 * Returns zero if no parsing was possible
5023
 */
5024
static void
5025
0
htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate) {
5026
0
    while (PARSER_STOPPED(ctxt) == 0) {
5027
0
        htmlParserInputPtr in;
5028
0
        size_t avail;
5029
5030
0
  in = ctxt->input;
5031
0
  if (in == NULL) break;
5032
0
  avail = in->end - in->cur;
5033
5034
0
        switch (ctxt->instate) {
5035
0
            case XML_PARSER_EOF:
5036
          /*
5037
     * Document parsing is done !
5038
     */
5039
0
          return;
5040
5041
0
            case XML_PARSER_START:
5042
                /*
5043
                 * Very first chars read from the document flow.
5044
                 */
5045
0
                if ((!terminate) && (avail < 4))
5046
0
                    return;
5047
5048
0
                xmlDetectEncoding(ctxt);
5049
5050
                /*
5051
                 * TODO: Implement HTML5 prescan algorithm
5052
                 */
5053
5054
                /*
5055
                 * This is wrong but matches long-standing behavior. In most
5056
                 * cases, a document starting with an XML declaration will
5057
                 * specify UTF-8. The HTML5 prescan algorithm handles
5058
                 * XML declarations in a better way.
5059
                 */
5060
0
                if (((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) &&
5061
0
                    (xmlStrncmp(ctxt->input->cur, BAD_CAST "<?xm", 4) == 0)) {
5062
0
                    xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_UTF8);
5063
0
                }
5064
5065
                /* fall through */
5066
5067
0
            case XML_PARSER_XML_DECL:
5068
0
                if ((ctxt->sax) && (ctxt->sax->setDocumentLocator)) {
5069
0
                    ctxt->sax->setDocumentLocator(ctxt->userData,
5070
0
                            (xmlSAXLocator *) &xmlDefaultSAXLocator);
5071
0
                }
5072
0
    if ((ctxt->sax) && (ctxt->sax->startDocument) &&
5073
0
              (!ctxt->disableSAX))
5074
0
        ctxt->sax->startDocument(ctxt->userData);
5075
5076
                /* Allow callback to modify state for tests */
5077
0
                if ((ctxt->instate == XML_PARSER_START) ||
5078
0
                    (ctxt->instate == XML_PARSER_XML_DECL))
5079
0
                    ctxt->instate = XML_PARSER_MISC;
5080
0
    break;
5081
5082
0
            case XML_PARSER_START_TAG:
5083
0
    if ((!terminate) &&
5084
0
        (htmlParseLookupGt(ctxt) < 0))
5085
0
        return;
5086
5087
0
                htmlParseElementInternal(ctxt);
5088
5089
0
    ctxt->instate = XML_PARSER_CONTENT;
5090
0
                break;
5091
5092
0
            case XML_PARSER_MISC: /* initial */
5093
0
            case XML_PARSER_PROLOG: /* before html */
5094
0
            case XML_PARSER_CONTENT: {
5095
0
                int mode;
5096
5097
0
                if ((ctxt->instate == XML_PARSER_MISC) ||
5098
0
                    (ctxt->instate == XML_PARSER_PROLOG)) {
5099
0
                    SKIP_BLANKS;
5100
0
                    avail = in->end - in->cur;
5101
0
                }
5102
5103
0
    if (avail < 1)
5104
0
        return;
5105
                /*
5106
                 * Note that endCheckState is also used by
5107
                 * xmlParseLookupGt.
5108
                 */
5109
0
                mode = ctxt->endCheckState;
5110
5111
0
                if (mode != 0) {
5112
0
                    if (htmlParseCharData(ctxt, !terminate) == 0)
5113
0
                        return;
5114
0
    } else if (in->cur[0] == '<') {
5115
0
                    int next;
5116
5117
0
                    if (avail < 2) {
5118
0
                        if (!terminate)
5119
0
                            return;
5120
0
                        next = ' ';
5121
0
                    } else {
5122
0
                        next = in->cur[1];
5123
0
                    }
5124
5125
0
                    if (next == '!') {
5126
0
                        if ((!terminate) && (avail < 4))
5127
0
                            return;
5128
0
                        if ((in->cur[2] == '-') && (in->cur[3] == '-')) {
5129
0
                            if ((!terminate) &&
5130
0
                                (htmlParseLookupCommentEnd(ctxt) < 0))
5131
0
                                return;
5132
0
                            SKIP(4);
5133
0
                            htmlParseComment(ctxt, /* bogus */ 0);
5134
                            /* don't change state */
5135
0
                            break;
5136
0
                        }
5137
5138
0
                        if ((!terminate) && (avail < 9))
5139
0
                            return;
5140
0
                        if ((UPP(2) == 'D') && (UPP(3) == 'O') &&
5141
0
                            (UPP(4) == 'C') && (UPP(5) == 'T') &&
5142
0
                            (UPP(6) == 'Y') && (UPP(7) == 'P') &&
5143
0
                            (UPP(8) == 'E')) {
5144
0
                            if ((!terminate) &&
5145
0
                                (htmlParseLookupString(ctxt, 9, ">", 1,
5146
0
                                                       0) < 0))
5147
0
                                return;
5148
0
                            htmlParseDocTypeDecl(ctxt);
5149
0
                            if (ctxt->instate == XML_PARSER_MISC)
5150
0
                                ctxt->instate = XML_PARSER_PROLOG;
5151
0
                            else
5152
0
                                ctxt->instate = XML_PARSER_CONTENT;
5153
0
                        } else {
5154
0
                            ctxt->instate = XML_PARSER_CONTENT;
5155
0
                            if ((!terminate) &&
5156
0
                                (htmlParseLookupString(ctxt, 2, ">", 1, 0) < 0))
5157
0
                                return;
5158
0
                            SKIP(2);
5159
0
                            htmlParseComment(ctxt, /* bogus */ 1);
5160
0
                        }
5161
0
                    } else if (next == '?') {
5162
0
                        if ((!terminate) &&
5163
0
                            (htmlParseLookupString(ctxt, 2, ">", 1, 0) < 0))
5164
0
                            return;
5165
0
                        SKIP(1);
5166
0
                        htmlParseComment(ctxt, /* bogus */ 1);
5167
                        /* don't change state */
5168
0
                    } else if (next == '/') {
5169
0
                        ctxt->instate = XML_PARSER_END_TAG;
5170
0
                        ctxt->checkIndex = 0;
5171
0
                    } else if (IS_ASCII_LETTER(next)) {
5172
0
                        ctxt->instate = XML_PARSER_START_TAG;
5173
0
                        ctxt->checkIndex = 0;
5174
0
                    } else {
5175
0
                        ctxt->instate = XML_PARSER_CONTENT;
5176
0
                        htmlStartCharData(ctxt);
5177
0
                        if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
5178
0
                            (ctxt->sax->characters != NULL))
5179
0
                            ctxt->sax->characters(ctxt->userData,
5180
0
                                                  BAD_CAST "<", 1);
5181
0
                        SKIP(1);
5182
0
                    }
5183
0
                } else {
5184
0
                    ctxt->instate = XML_PARSER_CONTENT;
5185
                    /*
5186
                     * We follow the logic of the XML push parser
5187
                     */
5188
0
        if (avail < HTML_PARSER_BIG_BUFFER_SIZE) {
5189
0
                        if ((!terminate) &&
5190
0
                            (htmlParseLookupString(ctxt, 0, "<", 1, 0) < 0))
5191
0
                            return;
5192
0
                    }
5193
0
                    ctxt->checkIndex = 0;
5194
0
                    if (htmlParseCharData(ctxt, !terminate) == 0)
5195
0
                        return;
5196
0
    }
5197
5198
0
    break;
5199
0
      }
5200
5201
0
            case XML_PARSER_END_TAG:
5202
0
    if ((!terminate) &&
5203
0
        (htmlParseLookupGt(ctxt) < 0))
5204
0
        return;
5205
0
    htmlParseEndTag(ctxt);
5206
0
    ctxt->instate = XML_PARSER_CONTENT;
5207
0
    ctxt->checkIndex = 0;
5208
0
          break;
5209
5210
0
      default:
5211
0
    htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
5212
0
           "HPP: internal error\n", NULL, NULL);
5213
0
    ctxt->instate = XML_PARSER_EOF;
5214
0
    break;
5215
0
  }
5216
0
    }
5217
0
}
5218
5219
/**
5220
 * htmlParseChunk:
5221
 * @ctxt:  an HTML parser context
5222
 * @chunk:  chunk of memory
5223
 * @size:  size of chunk in bytes
5224
 * @terminate:  last chunk indicator
5225
 *
5226
 * Parse a chunk of memory in push parser mode.
5227
 *
5228
 * Assumes that the parser context was initialized with
5229
 * htmlCreatePushParserCtxt.
5230
 *
5231
 * The last chunk, which will often be empty, must be marked with
5232
 * the @terminate flag. With the default SAX callbacks, the resulting
5233
 * document will be available in ctxt->myDoc. This pointer will not
5234
 * be freed by the library.
5235
 *
5236
 * If the document isn't well-formed, ctxt->myDoc is set to NULL.
5237
 *
5238
 * Returns an xmlParserErrors code (0 on success).
5239
 */
5240
int
5241
htmlParseChunk(htmlParserCtxtPtr ctxt, const char *chunk, int size,
5242
0
              int terminate) {
5243
0
    if ((ctxt == NULL) ||
5244
0
        (ctxt->input == NULL) || (ctxt->input->buf == NULL) ||
5245
0
        (size < 0) ||
5246
0
        ((size > 0) && (chunk == NULL)))
5247
0
  return(XML_ERR_ARGUMENT);
5248
0
    if (PARSER_STOPPED(ctxt) != 0)
5249
0
        return(ctxt->errNo);
5250
5251
0
    if (size > 0)  {
5252
0
  size_t pos = ctxt->input->cur - ctxt->input->base;
5253
0
  int res;
5254
5255
0
  res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
5256
0
        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
5257
0
  if (res < 0) {
5258
0
            htmlParseErr(ctxt, ctxt->input->buf->error,
5259
0
                         "xmlParserInputBufferPush failed", NULL, NULL);
5260
0
            xmlHaltParser(ctxt);
5261
0
      return (ctxt->errNo);
5262
0
  }
5263
0
    }
5264
5265
0
    htmlParseTryOrFinish(ctxt, terminate);
5266
5267
0
    if ((terminate) && (ctxt->instate != XML_PARSER_EOF)) {
5268
0
        htmlAutoCloseOnEnd(ctxt);
5269
5270
        /*
5271
         * Only check for truncated multi-byte sequences
5272
         */
5273
0
        xmlParserCheckEOF(ctxt, XML_ERR_INTERNAL_ERROR);
5274
5275
0
        if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
5276
0
            ctxt->sax->endDocument(ctxt->userData);
5277
5278
0
        if ((!(ctxt->options & HTML_PARSE_NODEFDTD)) &&
5279
0
            (ctxt->myDoc != NULL)) {
5280
0
            xmlDtdPtr dtd;
5281
0
            dtd = xmlGetIntSubset(ctxt->myDoc);
5282
0
            if (dtd == NULL) {
5283
0
                ctxt->myDoc->intSubset =
5284
0
                    xmlCreateIntSubset(ctxt->myDoc, BAD_CAST "html",
5285
0
                        BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN",
5286
0
                        BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd");
5287
0
                if (ctxt->myDoc->intSubset == NULL)
5288
0
                    htmlErrMemory(ctxt);
5289
0
            }
5290
0
        }
5291
5292
0
  ctxt->instate = XML_PARSER_EOF;
5293
0
    }
5294
5295
0
    return((xmlParserErrors) ctxt->errNo);
5296
0
}
5297
5298
/************************************************************************
5299
 *                  *
5300
 *      User entry points       *
5301
 *                  *
5302
 ************************************************************************/
5303
5304
/**
5305
 * htmlCreatePushParserCtxt:
5306
 * @sax:  a SAX handler (optional)
5307
 * @user_data:  The user data returned on SAX callbacks (optional)
5308
 * @chunk:  a pointer to an array of chars (optional)
5309
 * @size:  number of chars in the array
5310
 * @filename:  only used for error reporting (optional)
5311
 * @enc:  encoding (deprecated, pass XML_CHAR_ENCODING_NONE)
5312
 *
5313
 * Create a parser context for using the HTML parser in push mode.
5314
 *
5315
 * Returns the new parser context or NULL if a memory allocation
5316
 * failed.
5317
 */
5318
htmlParserCtxtPtr
5319
htmlCreatePushParserCtxt(htmlSAXHandlerPtr sax, void *user_data,
5320
                         const char *chunk, int size, const char *filename,
5321
0
       xmlCharEncoding enc) {
5322
0
    htmlParserCtxtPtr ctxt;
5323
0
    htmlParserInputPtr input;
5324
0
    const char *encoding;
5325
5326
0
    ctxt = htmlNewSAXParserCtxt(sax, user_data);
5327
0
    if (ctxt == NULL)
5328
0
  return(NULL);
5329
5330
0
    encoding = xmlGetCharEncodingName(enc);
5331
0
    input = xmlNewPushInput(filename, chunk, size);
5332
0
    if (input == NULL) {
5333
0
  htmlFreeParserCtxt(ctxt);
5334
0
  return(NULL);
5335
0
    }
5336
5337
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
5338
0
        xmlFreeInputStream(input);
5339
0
        xmlFreeParserCtxt(ctxt);
5340
0
        return(NULL);
5341
0
    }
5342
5343
0
    if (encoding != NULL)
5344
0
        xmlSwitchEncodingName(ctxt, encoding);
5345
5346
0
    return(ctxt);
5347
0
}
5348
#endif /* LIBXML_PUSH_ENABLED */
5349
5350
/**
5351
 * htmlSAXParseDoc:
5352
 * @cur:  a pointer to an array of xmlChar
5353
 * @encoding:  a free form C string describing the HTML document encoding, or NULL
5354
 * @sax:  the SAX handler block
5355
 * @userData: if using SAX, this pointer will be provided on callbacks.
5356
 *
5357
 * DEPRECATED: Use htmlNewSAXParserCtxt and htmlCtxtReadDoc.
5358
 *
5359
 * Parse an HTML in-memory document. If sax is not NULL, use the SAX callbacks
5360
 * to handle parse events. If sax is NULL, fallback to the default DOM
5361
 * behavior and return a tree.
5362
 *
5363
 * Returns the resulting document tree unless SAX is NULL or the document is
5364
 *     not well formed.
5365
 */
5366
5367
htmlDocPtr
5368
htmlSAXParseDoc(const xmlChar *cur, const char *encoding,
5369
0
                htmlSAXHandlerPtr sax, void *userData) {
5370
0
    htmlDocPtr ret;
5371
0
    htmlParserCtxtPtr ctxt;
5372
5373
0
    if (cur == NULL)
5374
0
        return(NULL);
5375
5376
0
    ctxt = htmlCreateDocParserCtxt(cur, NULL, encoding);
5377
0
    if (ctxt == NULL)
5378
0
        return(NULL);
5379
5380
0
    if (sax != NULL) {
5381
0
        *ctxt->sax = *sax;
5382
0
        ctxt->userData = userData;
5383
0
    }
5384
5385
0
    htmlParseDocument(ctxt);
5386
0
    ret = ctxt->myDoc;
5387
0
    htmlFreeParserCtxt(ctxt);
5388
5389
0
    return(ret);
5390
0
}
5391
5392
/**
5393
 * htmlParseDoc:
5394
 * @cur:  a pointer to an array of xmlChar
5395
 * @encoding:  the encoding (optional)
5396
 *
5397
 * DEPRECATED: Use htmlReadDoc.
5398
 *
5399
 * Parse an HTML in-memory document and build a tree.
5400
 *
5401
 * This function uses deprecated global parser options.
5402
 *
5403
 * Returns the resulting document tree
5404
 */
5405
5406
htmlDocPtr
5407
0
htmlParseDoc(const xmlChar *cur, const char *encoding) {
5408
0
    return(htmlSAXParseDoc(cur, encoding, NULL, NULL));
5409
0
}
5410
5411
5412
/**
5413
 * htmlCreateFileParserCtxt:
5414
 * @filename:  the filename
5415
 * @encoding:  optional encoding
5416
 *
5417
 * DEPRECATED: Use htmlNewParserCtxt and htmlCtxtReadFile.
5418
 *
5419
 * Create a parser context to read from a file.
5420
 *
5421
 * A non-NULL encoding overrides encoding declarations in the document.
5422
 *
5423
 * Automatic support for ZLIB/Compress compressed document is provided
5424
 * by default if found at compile-time.
5425
 *
5426
 * Returns the new parser context or NULL if a memory allocation failed.
5427
 */
5428
htmlParserCtxtPtr
5429
htmlCreateFileParserCtxt(const char *filename, const char *encoding)
5430
0
{
5431
0
    htmlParserCtxtPtr ctxt;
5432
0
    htmlParserInputPtr input;
5433
5434
0
    if (filename == NULL)
5435
0
        return(NULL);
5436
5437
0
    ctxt = htmlNewParserCtxt();
5438
0
    if (ctxt == NULL) {
5439
0
  return(NULL);
5440
0
    }
5441
5442
0
    input = xmlCtxtNewInputFromUrl(ctxt, filename, NULL, encoding, 0);
5443
0
    if (input == NULL) {
5444
0
  xmlFreeParserCtxt(ctxt);
5445
0
  return(NULL);
5446
0
    }
5447
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
5448
0
        xmlFreeInputStream(input);
5449
0
        xmlFreeParserCtxt(ctxt);
5450
0
        return(NULL);
5451
0
    }
5452
5453
0
    return(ctxt);
5454
0
}
5455
5456
/**
5457
 * htmlSAXParseFile:
5458
 * @filename:  the filename
5459
 * @encoding:  encoding (optional)
5460
 * @sax:  the SAX handler block
5461
 * @userData: if using SAX, this pointer will be provided on callbacks.
5462
 *
5463
 * DEPRECATED: Use htmlNewSAXParserCtxt and htmlCtxtReadFile.
5464
 *
5465
 * parse an HTML file and build a tree. Automatic support for ZLIB/Compress
5466
 * compressed document is provided by default if found at compile-time.
5467
 * It use the given SAX function block to handle the parsing callback.
5468
 * If sax is NULL, fallback to the default DOM tree building routines.
5469
 *
5470
 * Returns the resulting document tree unless SAX is NULL or the document is
5471
 *     not well formed.
5472
 */
5473
5474
htmlDocPtr
5475
htmlSAXParseFile(const char *filename, const char *encoding, htmlSAXHandlerPtr sax,
5476
0
                 void *userData) {
5477
0
    htmlDocPtr ret;
5478
0
    htmlParserCtxtPtr ctxt;
5479
0
    htmlSAXHandlerPtr oldsax = NULL;
5480
5481
0
    ctxt = htmlCreateFileParserCtxt(filename, encoding);
5482
0
    if (ctxt == NULL) return(NULL);
5483
0
    if (sax != NULL) {
5484
0
  oldsax = ctxt->sax;
5485
0
        ctxt->sax = sax;
5486
0
        ctxt->userData = userData;
5487
0
    }
5488
5489
0
    htmlParseDocument(ctxt);
5490
5491
0
    ret = ctxt->myDoc;
5492
0
    if (sax != NULL) {
5493
0
        ctxt->sax = oldsax;
5494
0
        ctxt->userData = NULL;
5495
0
    }
5496
0
    htmlFreeParserCtxt(ctxt);
5497
5498
0
    return(ret);
5499
0
}
5500
5501
/**
5502
 * htmlParseFile:
5503
 * @filename:  the filename
5504
 * @encoding:  encoding (optional)
5505
 *
5506
 * Parse an HTML file and build a tree.
5507
 *
5508
 * Returns the resulting document tree
5509
 */
5510
5511
htmlDocPtr
5512
0
htmlParseFile(const char *filename, const char *encoding) {
5513
0
    return(htmlSAXParseFile(filename, encoding, NULL, NULL));
5514
0
}
5515
5516
/**
5517
 * htmlHandleOmittedElem:
5518
 * @val:  int 0 or 1
5519
 *
5520
 * DEPRECATED: Use HTML_PARSE_NOIMPLIED
5521
 *
5522
 * Set and return the previous value for handling HTML omitted tags.
5523
 *
5524
 * Returns the last value for 0 for no handling, 1 for auto insertion.
5525
 */
5526
5527
int
5528
0
htmlHandleOmittedElem(int val) {
5529
0
    int old = htmlOmittedDefaultValue;
5530
5531
0
    htmlOmittedDefaultValue = val;
5532
0
    return(old);
5533
0
}
5534
5535
/**
5536
 * htmlElementAllowedHere:
5537
 * @parent: HTML parent element
5538
 * @elt: HTML element
5539
 *
5540
 * DEPRECATED: Don't use.
5541
 *
5542
 * Returns 1
5543
 */
5544
int
5545
htmlElementAllowedHere(const htmlElemDesc* parent ATTRIBUTE_UNUSED,
5546
0
                       const xmlChar* elt ATTRIBUTE_UNUSED) {
5547
0
    return(1);
5548
0
}
5549
5550
/**
5551
 * htmlElementStatusHere:
5552
 * @parent: HTML parent element
5553
 * @elt: HTML element
5554
 *
5555
 * DEPRECATED: Don't use.
5556
 *
5557
 * Returns HTML_VALID
5558
 */
5559
htmlStatus
5560
htmlElementStatusHere(const htmlElemDesc* parent ATTRIBUTE_UNUSED,
5561
0
                      const htmlElemDesc* elt ATTRIBUTE_UNUSED) {
5562
0
    return(HTML_VALID);
5563
0
}
5564
5565
/**
5566
 * htmlAttrAllowed:
5567
 * @elt: HTML element
5568
 * @attr: HTML attribute
5569
 * @legacy: whether to allow deprecated attributes
5570
 *
5571
 * DEPRECATED: Don't use.
5572
 *
5573
 * Returns HTML_VALID
5574
 */
5575
htmlStatus
5576
htmlAttrAllowed(const htmlElemDesc* elt ATTRIBUTE_UNUSED,
5577
                const xmlChar* attr ATTRIBUTE_UNUSED,
5578
0
                int legacy ATTRIBUTE_UNUSED) {
5579
0
    return(HTML_VALID);
5580
0
}
5581
5582
/**
5583
 * htmlNodeStatus:
5584
 * @node: an htmlNodePtr in a tree
5585
 * @legacy: whether to allow deprecated elements (YES is faster here
5586
 *  for Element nodes)
5587
 *
5588
 * DEPRECATED: Don't use.
5589
 *
5590
 * Returns HTML_VALID
5591
 */
5592
htmlStatus
5593
htmlNodeStatus(htmlNodePtr node ATTRIBUTE_UNUSED,
5594
0
               int legacy ATTRIBUTE_UNUSED) {
5595
0
    return(HTML_VALID);
5596
0
}
5597
5598
/************************************************************************
5599
 *                  *
5600
 *  New set (2.6.0) of simpler and more flexible APIs   *
5601
 *                  *
5602
 ************************************************************************/
5603
/**
5604
 * DICT_FREE:
5605
 * @str:  a string
5606
 *
5607
 * Free a string if it is not owned by the "dict" dictionary in the
5608
 * current scope
5609
 */
5610
#define DICT_FREE(str)            \
5611
0
  if ((str) && ((!dict) ||       \
5612
0
      (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))  \
5613
0
      xmlFree((char *)(str));
5614
5615
/**
5616
 * htmlCtxtReset:
5617
 * @ctxt: an HTML parser context
5618
 *
5619
 * Reset a parser context
5620
 */
5621
void
5622
htmlCtxtReset(htmlParserCtxtPtr ctxt)
5623
0
{
5624
0
    xmlParserInputPtr input;
5625
0
    xmlDictPtr dict;
5626
5627
0
    if (ctxt == NULL)
5628
0
        return;
5629
5630
0
    dict = ctxt->dict;
5631
5632
0
    while ((input = xmlCtxtPopInput(ctxt)) != NULL) { /* Non consuming */
5633
0
        xmlFreeInputStream(input);
5634
0
    }
5635
0
    ctxt->inputNr = 0;
5636
0
    ctxt->input = NULL;
5637
5638
0
    ctxt->spaceNr = 0;
5639
0
    if (ctxt->spaceTab != NULL) {
5640
0
  ctxt->spaceTab[0] = -1;
5641
0
  ctxt->space = &ctxt->spaceTab[0];
5642
0
    } else {
5643
0
  ctxt->space = NULL;
5644
0
    }
5645
5646
5647
0
    ctxt->nodeNr = 0;
5648
0
    ctxt->node = NULL;
5649
5650
0
    ctxt->nameNr = 0;
5651
0
    ctxt->name = NULL;
5652
5653
0
    ctxt->nsNr = 0;
5654
5655
0
    DICT_FREE(ctxt->version);
5656
0
    ctxt->version = NULL;
5657
0
    DICT_FREE(ctxt->encoding);
5658
0
    ctxt->encoding = NULL;
5659
0
    DICT_FREE(ctxt->extSubURI);
5660
0
    ctxt->extSubURI = NULL;
5661
0
    DICT_FREE(ctxt->extSubSystem);
5662
0
    ctxt->extSubSystem = NULL;
5663
5664
0
    if (ctxt->directory != NULL) {
5665
0
        xmlFree(ctxt->directory);
5666
0
        ctxt->directory = NULL;
5667
0
    }
5668
5669
0
    if (ctxt->myDoc != NULL)
5670
0
        xmlFreeDoc(ctxt->myDoc);
5671
0
    ctxt->myDoc = NULL;
5672
5673
0
    ctxt->standalone = -1;
5674
0
    ctxt->hasExternalSubset = 0;
5675
0
    ctxt->hasPErefs = 0;
5676
0
    ctxt->html = INSERT_INITIAL;
5677
0
    ctxt->instate = XML_PARSER_START;
5678
5679
0
    ctxt->wellFormed = 1;
5680
0
    ctxt->nsWellFormed = 1;
5681
0
    ctxt->disableSAX = 0;
5682
0
    ctxt->valid = 1;
5683
0
    ctxt->vctxt.userData = ctxt;
5684
0
    ctxt->vctxt.flags = XML_VCTXT_USE_PCTXT;
5685
0
    ctxt->vctxt.error = xmlParserValidityError;
5686
0
    ctxt->vctxt.warning = xmlParserValidityWarning;
5687
0
    ctxt->record_info = 0;
5688
0
    ctxt->checkIndex = 0;
5689
0
    ctxt->endCheckState = 0;
5690
0
    ctxt->inSubset = 0;
5691
0
    ctxt->errNo = XML_ERR_OK;
5692
0
    ctxt->depth = 0;
5693
0
    ctxt->catalogs = NULL;
5694
0
    xmlInitNodeInfoSeq(&ctxt->node_seq);
5695
5696
0
    if (ctxt->attsDefault != NULL) {
5697
0
        xmlHashFree(ctxt->attsDefault, xmlHashDefaultDeallocator);
5698
0
        ctxt->attsDefault = NULL;
5699
0
    }
5700
0
    if (ctxt->attsSpecial != NULL) {
5701
0
        xmlHashFree(ctxt->attsSpecial, NULL);
5702
0
        ctxt->attsSpecial = NULL;
5703
0
    }
5704
5705
0
    ctxt->nbErrors = 0;
5706
0
    ctxt->nbWarnings = 0;
5707
0
    if (ctxt->lastError.code != XML_ERR_OK)
5708
0
        xmlResetError(&ctxt->lastError);
5709
0
}
5710
5711
static int
5712
htmlCtxtSetOptionsInternal(xmlParserCtxtPtr ctxt, int options, int keepMask)
5713
0
{
5714
0
    int allMask;
5715
5716
0
    if (ctxt == NULL)
5717
0
        return(-1);
5718
5719
0
    allMask = HTML_PARSE_RECOVER |
5720
0
              HTML_PARSE_HTML5 |
5721
0
              HTML_PARSE_NODEFDTD |
5722
0
              HTML_PARSE_NOERROR |
5723
0
              HTML_PARSE_NOWARNING |
5724
0
              HTML_PARSE_PEDANTIC |
5725
0
              HTML_PARSE_NOBLANKS |
5726
0
              HTML_PARSE_NONET |
5727
0
              HTML_PARSE_NOIMPLIED |
5728
0
              HTML_PARSE_COMPACT |
5729
0
              HTML_PARSE_HUGE |
5730
0
              HTML_PARSE_IGNORE_ENC |
5731
0
              HTML_PARSE_BIG_LINES;
5732
5733
0
    ctxt->options = (ctxt->options & keepMask) | (options & allMask);
5734
5735
    /*
5736
     * For some options, struct members are historically the source
5737
     * of truth. See xmlCtxtSetOptionsInternal.
5738
     */
5739
0
    ctxt->keepBlanks = (options & HTML_PARSE_NOBLANKS) ? 0 : 1;
5740
5741
    /*
5742
     * Changing SAX callbacks is a bad idea. This should be fixed.
5743
     */
5744
0
    if (options & HTML_PARSE_NOBLANKS) {
5745
0
        ctxt->sax->ignorableWhitespace = xmlSAX2IgnorableWhitespace;
5746
0
    }
5747
0
    if (options & HTML_PARSE_HUGE) {
5748
0
        if (ctxt->dict != NULL)
5749
0
            xmlDictSetLimit(ctxt->dict, 0);
5750
0
    }
5751
5752
    /*
5753
     * It would be useful to allow this feature.
5754
     */
5755
0
    ctxt->dictNames = 0;
5756
5757
0
    ctxt->linenumbers = 1;
5758
5759
    /*
5760
     * Allow XML_PARSE_NOENT which many users set on the HTML parser.
5761
     */
5762
0
    return(options & ~allMask & ~XML_PARSE_NOENT);
5763
0
}
5764
5765
/**
5766
 * htmlCtxtSetOptions:
5767
 * @ctxt: an HTML parser context
5768
 * @options:  a bitmask of xmlParserOption values
5769
 *
5770
 * Applies the options to the parser context. Unset options are
5771
 * cleared.
5772
 *
5773
 * Available since 2.14.0. With older versions, you can use
5774
 * htmlCtxtUseOptions.
5775
 *
5776
 * HTML_PARSE_RECOVER
5777
 *
5778
 * No effect as of 2.14.0.
5779
 *
5780
 * HTML_PARSE_HTML5
5781
 *
5782
 * Make the tokenizer emit a SAX callback for each token. This results
5783
 * in unbalanced invocations of startElement and endElement.
5784
 *
5785
 * For now, this is only usable with custom SAX callbacks.
5786
 *
5787
 * HTML_PARSE_NODEFDTD
5788
 *
5789
 * Do not default to a doctype if none was found.
5790
 *
5791
 * HTML_PARSE_NOERROR
5792
 *
5793
 * Disable error and warning reports to the error handlers.
5794
 * Errors are still accessible with xmlCtxtGetLastError.
5795
 *
5796
 * HTML_PARSE_NOWARNING
5797
 *
5798
 * Disable warning reports.
5799
 *
5800
 * HTML_PARSE_PEDANTIC
5801
 *
5802
 * No effect.
5803
 *
5804
 * HTML_PARSE_NOBLANKS
5805
 *
5806
 * Remove some text nodes containing only whitespace from the
5807
 * result document. Which nodes are removed depends on a conservative
5808
 * heuristic. The reindenting feature of the serialization code relies
5809
 * on this option to be set when parsing. Use of this option is
5810
 * DISCOURAGED.
5811
 *
5812
 * HTML_PARSE_NONET
5813
 *
5814
 * No effect.
5815
 *
5816
 * HTML_PARSE_NOIMPLIED
5817
 *
5818
 * Do not add implied html, head or body elements.
5819
 *
5820
 * HTML_PARSE_COMPACT
5821
 *
5822
 * Store small strings directly in the node struct to save
5823
 * memory.
5824
 *
5825
 * HTML_PARSE_HUGE
5826
 *
5827
 * Relax some internal limits.
5828
 *
5829
 * Available since 2.14.0. Use XML_PARSE_HUGE works with older
5830
 * versions.
5831
 *
5832
 * Maximum size of text nodes, tags, comments, CDATA sections
5833
 *
5834
 * normal: 10M
5835
 * huge:    1B
5836
 *
5837
 * Maximum size of names, system literals, pubid literals
5838
 *
5839
 * normal: 50K
5840
 * huge:   10M
5841
 *
5842
 * Maximum nesting depth of elements
5843
 *
5844
 * normal:  256
5845
 * huge:   2048
5846
 *
5847
 * HTML_PARSE_IGNORE_ENC
5848
 *
5849
 * Ignore the encoding in the HTML declaration. This option is
5850
 * mostly unneeded these days. The only effect is to enforce
5851
 * UTF-8 decoding of ASCII-like data.
5852
 *
5853
 * HTML_PARSE_BIG_LINES
5854
 *
5855
 * Enable reporting of line numbers larger than 65535.
5856
 *
5857
 * Available since 2.14.0.
5858
 *
5859
 * Returns 0 in case of success, the set of unknown or unimplemented options
5860
 *         in case of error.
5861
 */
5862
int
5863
htmlCtxtSetOptions(xmlParserCtxtPtr ctxt, int options)
5864
0
{
5865
0
    return(htmlCtxtSetOptionsInternal(ctxt, options, 0));
5866
0
}
5867
5868
/**
5869
 * htmlCtxtUseOptions:
5870
 * @ctxt: an HTML parser context
5871
 * @options:  a combination of htmlParserOption(s)
5872
 *
5873
 * DEPRECATED: Use htmlCtxtSetOptions.
5874
 *
5875
 * Applies the options to the parser context. The following options
5876
 * are never cleared and can only be enabled:
5877
 *
5878
 * HTML_PARSE_NODEFDTD
5879
 * HTML_PARSE_NOERROR
5880
 * HTML_PARSE_NOWARNING
5881
 * HTML_PARSE_NOIMPLIED
5882
 * HTML_PARSE_COMPACT
5883
 * HTML_PARSE_HUGE
5884
 * HTML_PARSE_IGNORE_ENC
5885
 * HTML_PARSE_BIG_LINES
5886
 *
5887
 * Returns 0 in case of success, the set of unknown or unimplemented options
5888
 *         in case of error.
5889
 */
5890
int
5891
htmlCtxtUseOptions(htmlParserCtxtPtr ctxt, int options)
5892
0
{
5893
0
    int keepMask;
5894
5895
    /*
5896
     * For historic reasons, some options can only be enabled.
5897
     */
5898
0
    keepMask = HTML_PARSE_NODEFDTD |
5899
0
               HTML_PARSE_NOERROR |
5900
0
               HTML_PARSE_NOWARNING |
5901
0
               HTML_PARSE_NOIMPLIED |
5902
0
               HTML_PARSE_COMPACT |
5903
0
               HTML_PARSE_HUGE |
5904
0
               HTML_PARSE_IGNORE_ENC |
5905
0
               HTML_PARSE_BIG_LINES;
5906
5907
0
    return(htmlCtxtSetOptionsInternal(ctxt, options, keepMask));
5908
0
}
5909
5910
/**
5911
 * htmlCtxtParseDocument:
5912
 * @ctxt:  an HTML parser context
5913
 * @input:  parser input
5914
 *
5915
 * Parse an HTML document and return the resulting document tree.
5916
 *
5917
 * Available since 2.13.0.
5918
 *
5919
 * Returns the resulting document tree or NULL
5920
 */
5921
htmlDocPtr
5922
htmlCtxtParseDocument(htmlParserCtxtPtr ctxt, xmlParserInputPtr input)
5923
0
{
5924
0
    htmlDocPtr ret;
5925
5926
0
    if ((ctxt == NULL) || (input == NULL)) {
5927
0
        xmlFatalErr(ctxt, XML_ERR_ARGUMENT, NULL);
5928
0
        xmlFreeInputStream(input);
5929
0
        return(NULL);
5930
0
    }
5931
5932
    /* assert(ctxt->inputNr == 0); */
5933
0
    while (ctxt->inputNr > 0)
5934
0
        xmlFreeInputStream(xmlCtxtPopInput(ctxt));
5935
5936
0
    if (xmlCtxtPushInput(ctxt, input) < 0) {
5937
0
        xmlFreeInputStream(input);
5938
0
        return(NULL);
5939
0
    }
5940
5941
0
    ctxt->html = INSERT_INITIAL;
5942
0
    htmlParseDocument(ctxt);
5943
5944
0
    ret = xmlCtxtGetDocument(ctxt);
5945
5946
    /* assert(ctxt->inputNr == 1); */
5947
0
    while (ctxt->inputNr > 0)
5948
0
        xmlFreeInputStream(xmlCtxtPopInput(ctxt));
5949
5950
0
    return(ret);
5951
0
}
5952
5953
/**
5954
 * htmlReadDoc:
5955
 * @str:  a pointer to a zero terminated string
5956
 * @url:  only used for error reporting (optoinal)
5957
 * @encoding:  the document encoding (optional)
5958
 * @options:  a combination of htmlParserOptions
5959
 *
5960
 * Convenience function to parse an HTML document from a zero-terminated
5961
 * string.
5962
 *
5963
 * See htmlCtxtReadDoc for details.
5964
 *
5965
 * Returns the resulting document tree.
5966
 */
5967
htmlDocPtr
5968
htmlReadDoc(const xmlChar *str, const char *url, const char *encoding,
5969
            int options)
5970
0
{
5971
0
    htmlParserCtxtPtr ctxt;
5972
0
    xmlParserInputPtr input;
5973
0
    htmlDocPtr doc = NULL;
5974
5975
0
    ctxt = htmlNewParserCtxt();
5976
0
    if (ctxt == NULL)
5977
0
        return(NULL);
5978
5979
0
    htmlCtxtUseOptions(ctxt, options);
5980
5981
0
    input = xmlCtxtNewInputFromString(ctxt, url, (const char *) str, encoding,
5982
0
                                      XML_INPUT_BUF_STATIC);
5983
5984
0
    if (input != NULL)
5985
0
        doc = htmlCtxtParseDocument(ctxt, input);
5986
5987
0
    htmlFreeParserCtxt(ctxt);
5988
0
    return(doc);
5989
0
}
5990
5991
/**
5992
 * htmlReadFile:
5993
 * @filename:  a file or URL
5994
 * @encoding:  the document encoding (optional)
5995
 * @options:  a combination of htmlParserOptions
5996
 *
5997
 * Convenience function to parse an HTML file from the filesystem,
5998
 * the network or a global user-defined resource loader.
5999
 *
6000
 * See htmlCtxtReadFile for details.
6001
 *
6002
 * Returns the resulting document tree.
6003
 */
6004
htmlDocPtr
6005
htmlReadFile(const char *filename, const char *encoding, int options)
6006
0
{
6007
0
    htmlParserCtxtPtr ctxt;
6008
0
    xmlParserInputPtr input;
6009
0
    htmlDocPtr doc = NULL;
6010
6011
0
    ctxt = htmlNewParserCtxt();
6012
0
    if (ctxt == NULL)
6013
0
        return(NULL);
6014
6015
0
    htmlCtxtUseOptions(ctxt, options);
6016
6017
0
    input = xmlCtxtNewInputFromUrl(ctxt, filename, NULL, encoding, 0);
6018
6019
0
    if (input != NULL)
6020
0
        doc = htmlCtxtParseDocument(ctxt, input);
6021
6022
0
    htmlFreeParserCtxt(ctxt);
6023
0
    return(doc);
6024
0
}
6025
6026
/**
6027
 * htmlReadMemory:
6028
 * @buffer:  a pointer to a char array
6029
 * @size:  the size of the array
6030
 * @url:  only used for error reporting (optional)
6031
 * @encoding:  the document encoding, or NULL
6032
 * @options:  a combination of htmlParserOption(s)
6033
 *
6034
 * Convenience function to parse an HTML document from memory.
6035
 * The input buffer must not contain any terminating null bytes.
6036
 *
6037
 * See htmlCtxtReadMemory for details.
6038
 *
6039
 * Returns the resulting document tree
6040
 */
6041
htmlDocPtr
6042
htmlReadMemory(const char *buffer, int size, const char *url,
6043
               const char *encoding, int options)
6044
0
{
6045
0
    htmlParserCtxtPtr ctxt;
6046
0
    xmlParserInputPtr input;
6047
0
    htmlDocPtr doc = NULL;
6048
6049
0
    if (size < 0)
6050
0
  return(NULL);
6051
6052
0
    ctxt = htmlNewParserCtxt();
6053
0
    if (ctxt == NULL)
6054
0
        return(NULL);
6055
6056
0
    htmlCtxtUseOptions(ctxt, options);
6057
6058
0
    input = xmlCtxtNewInputFromMemory(ctxt, url, buffer, size, encoding,
6059
0
                                      XML_INPUT_BUF_STATIC);
6060
6061
0
    if (input != NULL)
6062
0
        doc = htmlCtxtParseDocument(ctxt, input);
6063
6064
0
    htmlFreeParserCtxt(ctxt);
6065
0
    return(doc);
6066
0
}
6067
6068
/**
6069
 * htmlReadFd:
6070
 * @fd:  an open file descriptor
6071
 * @url:  only used for error reporting (optional)
6072
 * @encoding:  the document encoding, or NULL
6073
 * @options:  a combination of htmlParserOptions
6074
 *
6075
 * Convenience function to parse an HTML document from a
6076
 * file descriptor.
6077
 *
6078
 * NOTE that the file descriptor will not be closed when the
6079
 * context is freed or reset.
6080
 *
6081
 * See htmlCtxtReadFd for details.
6082
 *
6083
 * Returns the resulting document tree
6084
 */
6085
htmlDocPtr
6086
htmlReadFd(int fd, const char *url, const char *encoding, int options)
6087
0
{
6088
0
    htmlParserCtxtPtr ctxt;
6089
0
    xmlParserInputPtr input;
6090
0
    htmlDocPtr doc = NULL;
6091
6092
0
    ctxt = htmlNewParserCtxt();
6093
0
    if (ctxt == NULL)
6094
0
        return(NULL);
6095
6096
0
    htmlCtxtUseOptions(ctxt, options);
6097
6098
0
    input = xmlCtxtNewInputFromFd(ctxt, url, fd, encoding, 0);
6099
6100
0
    if (input != NULL)
6101
0
        doc = htmlCtxtParseDocument(ctxt, input);
6102
6103
0
    htmlFreeParserCtxt(ctxt);
6104
0
    return(doc);
6105
0
}
6106
6107
/**
6108
 * htmlReadIO:
6109
 * @ioread:  an I/O read function
6110
 * @ioclose:  an I/O close function (optional)
6111
 * @ioctx:  an I/O handler
6112
 * @url:  only used for error reporting (optional)
6113
 * @encoding:  the document encoding (optional)
6114
 * @options:  a combination of htmlParserOption(s)
6115
 *
6116
 * Convenience function to parse an HTML document from I/O functions
6117
 * and context.
6118
 *
6119
 * See htmlCtxtReadIO for details.
6120
 *
6121
 * Returns the resulting document tree
6122
 */
6123
htmlDocPtr
6124
htmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
6125
          void *ioctx, const char *url, const char *encoding, int options)
6126
0
{
6127
0
    htmlParserCtxtPtr ctxt;
6128
0
    xmlParserInputPtr input;
6129
0
    htmlDocPtr doc = NULL;
6130
6131
0
    ctxt = htmlNewParserCtxt();
6132
0
    if (ctxt == NULL)
6133
0
        return (NULL);
6134
6135
0
    htmlCtxtUseOptions(ctxt, options);
6136
6137
0
    input = xmlCtxtNewInputFromIO(ctxt, url, ioread, ioclose, ioctx,
6138
0
                                  encoding, 0);
6139
6140
0
    if (input != NULL)
6141
0
        doc = htmlCtxtParseDocument(ctxt, input);
6142
6143
0
    htmlFreeParserCtxt(ctxt);
6144
0
    return(doc);
6145
0
}
6146
6147
/**
6148
 * htmlCtxtReadDoc:
6149
 * @ctxt:  an HTML parser context
6150
 * @str:  a pointer to a zero terminated string
6151
 * @URL:  only used for error reporting (optional)
6152
 * @encoding:  the document encoding (optional)
6153
 * @options:  a combination of htmlParserOptions
6154
 *
6155
 * Parse an HTML in-memory document and build a tree.
6156
 *
6157
 * See htmlCtxtUseOptions for details.
6158
 *
6159
 * Returns the resulting document tree
6160
 */
6161
htmlDocPtr
6162
htmlCtxtReadDoc(htmlParserCtxtPtr ctxt, const xmlChar *str,
6163
                const char *URL, const char *encoding, int options)
6164
0
{
6165
0
    xmlParserInputPtr input;
6166
6167
0
    if (ctxt == NULL)
6168
0
        return (NULL);
6169
6170
0
    htmlCtxtReset(ctxt);
6171
0
    htmlCtxtUseOptions(ctxt, options);
6172
6173
0
    input = xmlCtxtNewInputFromString(ctxt, URL, (const char *) str,
6174
0
                                      encoding, 0);
6175
0
    if (input == NULL)
6176
0
        return(NULL);
6177
6178
0
    return(htmlCtxtParseDocument(ctxt, input));
6179
0
}
6180
6181
/**
6182
 * htmlCtxtReadFile:
6183
 * @ctxt:  an HTML parser context
6184
 * @filename:  a file or URL
6185
 * @encoding:  the document encoding (optional)
6186
 * @options:  a combination of htmlParserOptions
6187
 *
6188
 * Parse an HTML file from the filesystem, the network or a
6189
 * user-defined resource loader.
6190
 *
6191
 * See htmlCtxtUseOptions for details.
6192
 *
6193
 * Returns the resulting document tree
6194
 */
6195
htmlDocPtr
6196
htmlCtxtReadFile(htmlParserCtxtPtr ctxt, const char *filename,
6197
                const char *encoding, int options)
6198
0
{
6199
0
    xmlParserInputPtr input;
6200
6201
0
    if (ctxt == NULL)
6202
0
        return (NULL);
6203
6204
0
    htmlCtxtReset(ctxt);
6205
0
    htmlCtxtUseOptions(ctxt, options);
6206
6207
0
    input = xmlCtxtNewInputFromUrl(ctxt, filename, NULL, encoding, 0);
6208
0
    if (input == NULL)
6209
0
        return(NULL);
6210
6211
0
    return(htmlCtxtParseDocument(ctxt, input));
6212
0
}
6213
6214
/**
6215
 * htmlCtxtReadMemory:
6216
 * @ctxt:  an HTML parser context
6217
 * @buffer:  a pointer to a char array
6218
 * @size:  the size of the array
6219
 * @URL:  only used for error reporting (optional)
6220
 * @encoding:  the document encoding (optinal)
6221
 * @options:  a combination of htmlParserOptions
6222
 *
6223
 * Parse an HTML in-memory document and build a tree. The input buffer must
6224
 * not contain any terminating null bytes.
6225
 *
6226
 * See htmlCtxtUseOptions for details.
6227
 *
6228
 * Returns the resulting document tree
6229
 */
6230
htmlDocPtr
6231
htmlCtxtReadMemory(htmlParserCtxtPtr ctxt, const char *buffer, int size,
6232
                  const char *URL, const char *encoding, int options)
6233
0
{
6234
0
    xmlParserInputPtr input;
6235
6236
0
    if ((ctxt == NULL) || (size < 0))
6237
0
        return (NULL);
6238
6239
0
    htmlCtxtReset(ctxt);
6240
0
    htmlCtxtUseOptions(ctxt, options);
6241
6242
0
    input = xmlCtxtNewInputFromMemory(ctxt, URL, buffer, size, encoding,
6243
0
                                      XML_INPUT_BUF_STATIC);
6244
0
    if (input == NULL)
6245
0
        return(NULL);
6246
6247
0
    return(htmlCtxtParseDocument(ctxt, input));
6248
0
}
6249
6250
/**
6251
 * htmlCtxtReadFd:
6252
 * @ctxt:  an HTML parser context
6253
 * @fd:  an open file descriptor
6254
 * @URL:  only used for error reporting (optional)
6255
 * @encoding:  the document encoding (optinal)
6256
 * @options:  a combination of htmlParserOptions
6257
 *
6258
 * Parse an HTML from a file descriptor and build a tree.
6259
 *
6260
 * See htmlCtxtUseOptions for details.
6261
 *
6262
 * NOTE that the file descriptor will not be closed when the
6263
 * context is freed or reset.
6264
 *
6265
 * Returns the resulting document tree
6266
 */
6267
htmlDocPtr
6268
htmlCtxtReadFd(htmlParserCtxtPtr ctxt, int fd,
6269
              const char *URL, const char *encoding, int options)
6270
0
{
6271
0
    xmlParserInputPtr input;
6272
6273
0
    if (ctxt == NULL)
6274
0
        return(NULL);
6275
6276
0
    htmlCtxtReset(ctxt);
6277
0
    htmlCtxtUseOptions(ctxt, options);
6278
6279
0
    input = xmlCtxtNewInputFromFd(ctxt, URL, fd, encoding, 0);
6280
0
    if (input == NULL)
6281
0
        return(NULL);
6282
6283
0
    return(htmlCtxtParseDocument(ctxt, input));
6284
0
}
6285
6286
/**
6287
 * htmlCtxtReadIO:
6288
 * @ctxt:  an HTML parser context
6289
 * @ioread:  an I/O read function
6290
 * @ioclose:  an I/O close function
6291
 * @ioctx:  an I/O handler
6292
 * @URL:  the base URL to use for the document
6293
 * @encoding:  the document encoding, or NULL
6294
 * @options:  a combination of htmlParserOption(s)
6295
 *
6296
 * Parse an HTML document from I/O functions and source and build a tree.
6297
 *
6298
 * See htmlCtxtUseOptions for details.
6299
 *
6300
 * Returns the resulting document tree
6301
 */
6302
htmlDocPtr
6303
htmlCtxtReadIO(htmlParserCtxtPtr ctxt, xmlInputReadCallback ioread,
6304
              xmlInputCloseCallback ioclose, void *ioctx,
6305
        const char *URL,
6306
              const char *encoding, int options)
6307
0
{
6308
0
    xmlParserInputPtr input;
6309
6310
0
    if (ctxt == NULL)
6311
0
        return (NULL);
6312
6313
0
    htmlCtxtReset(ctxt);
6314
0
    htmlCtxtUseOptions(ctxt, options);
6315
6316
0
    input = xmlCtxtNewInputFromIO(ctxt, URL, ioread, ioclose, ioctx,
6317
0
                                  encoding, 0);
6318
0
    if (input == NULL)
6319
0
        return(NULL);
6320
6321
0
    return(htmlCtxtParseDocument(ctxt, input));
6322
0
}
6323
6324
#endif /* LIBXML_HTML_ENABLED */