File Coverage

hparser.c
Criterion Covered Total %
statement 907 989 91.7
branch 852 1092 78.0
condition n/a
subroutine n/a
pod n/a
total 1759 2081 84.5


line stmt bran cond sub pod time code
1             /*
2             * Copyright 1999-2016, Gisle Aas
3             * Copyright 1999-2000, Michael A. Chase
4             *
5             * This library is free software; you can redistribute it and/or
6             * modify it under the same terms as Perl itself.
7             */
8              
9             #ifndef EXTERN
10             #define EXTERN extern
11             #endif
12              
13             #include "hctype.h" /* isH...() macros */
14             #include "tokenpos.h" /* dTOKEN; PUSH_TOKEN() */
15              
16              
17             const static
18             struct literal_tag {
19             int len;
20             char* str;
21             int is_cdata;
22             }
23             literal_mode_elem[] =
24             {
25             {6, "script", 1},
26             {5, "style", 1},
27             {3, "xmp", 1},
28             {6, "iframe", 1},
29             {9, "plaintext", 1},
30             {5, "title", 0},
31             {8, "textarea", 0},
32             {0, 0, 0}
33             };
34              
35             enum argcode {
36             ARG_SELF = 1, /* need to avoid '\0' in argspec string */
37             ARG_TOKENS,
38             ARG_TOKENPOS,
39             ARG_TOKEN0,
40             ARG_TAGNAME,
41             ARG_TAG,
42             ARG_ATTR,
43             ARG_ATTRARR,
44             ARG_ATTRSEQ,
45             ARG_TEXT,
46             ARG_DTEXT,
47             ARG_IS_CDATA,
48             ARG_SKIPPED_TEXT,
49             ARG_OFFSET,
50             ARG_OFFSET_END,
51             ARG_LENGTH,
52             ARG_LINE,
53             ARG_COLUMN,
54             ARG_EVENT,
55             ARG_UNDEF,
56             ARG_LITERAL, /* Always keep last */
57              
58             /* extra flags always encoded first */
59             ARG_FLAG_FLAT_ARRAY
60             };
61              
62             static const char * const argname[] = {
63             /* Must be in the same order as enum argcode */
64             "self", /* ARG_SELF */
65             "tokens", /* ARG_TOKENS */
66             "tokenpos", /* ARG_TOKENPOS */
67             "token0", /* ARG_TOKEN0 */
68             "tagname", /* ARG_TAGNAME */
69             "tag", /* ARG_TAG */
70             "attr", /* ARG_ATTR */
71             "@attr", /* ARG_ATTRARR */
72             "attrseq", /* ARG_ATTRSEQ */
73             "text", /* ARG_TEXT */
74             "dtext", /* ARG_DTEXT */
75             "is_cdata", /* ARG_IS_CDATA */
76             "skipped_text", /* ARG_SKIPPED_TEXT */
77             "offset", /* ARG_OFFSET */
78             "offset_end", /* ARG_OFFSET_END */
79             "length", /* ARG_LENGTH */
80             "line", /* ARG_LINE */
81             "column", /* ARG_COLUMN */
82             "event", /* ARG_EVENT */
83             "undef", /* ARG_UNDEF */
84             /* ARG_LITERAL (not compared) */
85             /* ARG_FLAG_FLAT_ARRAY */
86             };
87              
88             #define CASE_SENSITIVE(p_state) \
89             ((p_state)->xml_mode || (p_state)->case_sensitive)
90             #define STRICT_NAMES(p_state) \
91             ((p_state)->xml_mode || (p_state)->strict_names)
92             #define ALLOW_EMPTY_TAG(p_state) \
93             ((p_state)->xml_mode || (p_state)->empty_element_tags)
94              
95             static void flush_pending_text(PSTATE* p_state, SV* self);
96              
97             /*
98             * Parser functions.
99             *
100             * parse() - top level entry point.
101             * deals with text and calls one of its
102             * subordinate parse_*() routines after
103             * looking at the first char after "<"
104             * parse_decl() - deals with declarations
105             * parse_comment() - deals with
106             * parse_marked_section - deals with
107             * parse_end() - deals with end tags
108             * parse_start() - deals with start tags
109             * parse_process() - deals with process instructions
110             * parse_null() - deals with anything else <....>
111             *
112             * report_event() - called whenever any of the parse*() routines
113             * has recongnized something.
114             */
115              
116             static void
117 29266           report_event(PSTATE* p_state,
118             event_id_t event,
119             char *beg, char *end, U32 utf8,
120             token_pos_t *tokens, int num_tokens,
121             SV* self
122             )
123             {
124             struct p_handler *h;
125             dTHX;
126 29266           dSP;
127             AV *array;
128             STRLEN my_na;
129             char *argspec;
130             char *s;
131             STRLEN offset;
132             STRLEN line;
133             STRLEN column;
134              
135             #define CHR_DIST(a,b) (utf8 ? utf8_distance((U8*)(a),(U8*)(b)) : (a) - (b))
136              
137             /* some events might still fire after a handler has signaled eof
138             * so suppress them here.
139             */
140 29266 100         if (p_state->eof)
141 4           return;
142              
143             /* capture offsets */
144 29262           offset = p_state->offset;
145 29262           line = p_state->line;
146 29262           column = p_state->column;
147              
148             #if 0
149             { /* used for debugging at some point */
150             char *s = beg;
151             int i;
152              
153             /* print debug output */
154             switch(event) {
155             case E_DECLARATION: printf("DECLARATION"); break;
156             case E_COMMENT: printf("COMMENT"); break;
157             case E_START: printf("START"); break;
158             case E_END: printf("END"); break;
159             case E_TEXT: printf("TEXT"); break;
160             case E_PROCESS: printf("PROCESS"); break;
161             case E_NONE: printf("NONE"); break;
162             default: printf("EVENT #%d", event); break;
163             }
164              
165             printf(" [");
166             while (s < end) {
167             if (*s == '\n') {
168             putchar('\\'); putchar('n');
169             }
170             else
171             putchar(*s);
172             s++;
173             }
174             printf("] %d\n", end - beg);
175             for (i = 0; i < num_tokens; i++) {
176             printf(" token %d: %d %d\n",
177             i,
178             tokens[i].beg - beg,
179             tokens[i].end - tokens[i].beg);
180             }
181             }
182             #endif
183              
184 29262 50         if (p_state->pending_end_tag && event != E_TEXT && event != E_COMMENT) {
    0          
    0          
185             token_pos_t t;
186             char dummy;
187 0           t.beg = p_state->pending_end_tag;
188 0           t.end = p_state->pending_end_tag + strlen(p_state->pending_end_tag);
189 0           p_state->pending_end_tag = 0;
190 0           report_event(p_state, E_END, &dummy, &dummy, 0, &t, 1, self);
191 0           SPAGAIN;
192             }
193              
194             /* update offsets */
195 29262 100         p_state->offset += CHR_DIST(end, beg);
196 29262 100         if (line) {
197 122           char *s = beg;
198 122           char *nl = NULL;
199 840 100         while (s < end) {
200 718 100         if (*s == '\n') {
201 31           p_state->line++;
202 31           nl = s;
203             }
204 718           s++;
205             }
206 122 100         if (nl)
207 27 100         p_state->column = CHR_DIST(end, nl) - 1;
208             else
209 95 100         p_state->column += CHR_DIST(end, beg);
210             }
211              
212 29262 100         if (event == E_NONE)
213 32           goto IGNORE_EVENT;
214              
215             #ifdef MARKED_SECTION
216 29230 100         if (p_state->ms == MS_IGNORE)
217 4           goto IGNORE_EVENT;
218             #endif
219              
220             /* tag filters */
221 29226 100         if (p_state->ignore_tags || p_state->report_tags || p_state->ignore_elements) {
    100          
    100          
222              
223 533 100         if (event == E_START || event == E_END) {
    100          
224 167           SV* tagname = p_state->tmp;
225              
226             assert(num_tokens >= 1);
227 167           sv_setpvn(tagname, tokens[0].beg, tokens[0].end - tokens[0].beg);
228 167 100         if (utf8)
229 38           SvUTF8_on(tagname);
230             else
231 129           SvUTF8_off(tagname);
232 167 100         if (!CASE_SENSITIVE(p_state))
    100          
233 155           sv_lower(aTHX_ tagname);
234              
235 167 100         if (p_state->ignoring_element) {
236 7 50         if (sv_eq(p_state->ignoring_element, tagname)) {
237 7 50         if (event == E_START)
238 0           p_state->ignore_depth++;
239 7 50         else if (--p_state->ignore_depth == 0) {
240 7           SvREFCNT_dec(p_state->ignoring_element);
241 7           p_state->ignoring_element = 0;
242             }
243             }
244 7           goto IGNORE_EVENT;
245             }
246              
247 244           if (p_state->ignore_elements &&
248 84           hv_fetch_ent(p_state->ignore_elements, tagname, 0, 0))
249             {
250 8 100         if (event == E_START) {
251 7           p_state->ignoring_element = newSVsv(tagname);
252 7           p_state->ignore_depth = 1;
253             }
254 8           goto IGNORE_EVENT;
255             }
256              
257 205           if (p_state->ignore_tags &&
258 53           hv_fetch_ent(p_state->ignore_tags, tagname, 0, 0))
259             {
260 20           goto IGNORE_EVENT;
261             }
262 174           if (p_state->report_tags &&
263 42           !hv_fetch_ent(p_state->report_tags, tagname, 0, 0))
264             {
265 12           goto IGNORE_EVENT;
266             }
267             }
268 246 100         else if (p_state->ignoring_element) {
269 7           goto IGNORE_EVENT;
270             }
271             }
272              
273 29172           h = &p_state->handlers[event];
274 29172 100         if (!h->cb) {
275             /* event = E_DEFAULT; */
276 3286           h = &p_state->handlers[E_DEFAULT];
277 3286 100         if (!h->cb)
278 2178           goto IGNORE_EVENT;
279             }
280              
281 26994 100         if (SvTYPE(h->cb) != SVt_PVAV && !SvTRUE(h->cb)) {
    50          
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    100          
    100          
    50          
    50          
    50          
    0          
    50          
    50          
282             /* FALSE scalar ('' or 0) means IGNORE this event */
283 7           return;
284             }
285              
286 26987 100         if (p_state->unbroken_text && event == E_TEXT) {
    100          
287             /* should buffer text */
288 4510 100         if (!p_state->pend_text)
289 16           p_state->pend_text = newSV(256);
290 4510 100         if (SvOK(p_state->pend_text)) {
    50          
    50          
291 154 100         if (p_state->is_cdata != p_state->pend_text_is_cdata) {
292 2           flush_pending_text(p_state, self);
293 2           SPAGAIN;
294 2           goto INIT_PEND_TEXT;
295             }
296             }
297             else {
298             INIT_PEND_TEXT:
299 4358           p_state->pend_text_offset = offset;
300 4358           p_state->pend_text_line = line;
301 4358           p_state->pend_text_column = column;
302 4358           p_state->pend_text_is_cdata = p_state->is_cdata;
303 4358           sv_setpvs(p_state->pend_text, "");
304 4358 50         if (!utf8)
305 4358           SvUTF8_off(p_state->pend_text);
306             }
307 4510 50         if (utf8 && !SvUTF8(p_state->pend_text))
    0          
308 0           sv_utf8_upgrade(p_state->pend_text);
309 4510 50         if (utf8 || !SvUTF8(p_state->pend_text)) {
    50          
310 4510           sv_catpvn(p_state->pend_text, beg, end - beg);
311             }
312             else {
313 0           SV *tmp = newSVpvn(beg, end - beg);
314 0           sv_utf8_upgrade(tmp);
315 0           sv_catsv(p_state->pend_text, tmp);
316 0           SvREFCNT_dec(tmp);
317             }
318 4510           return;
319             }
320 22477 100         else if (p_state->pend_text && SvOK(p_state->pend_text)) {
    100          
    50          
    50          
321 4294           flush_pending_text(p_state, self);
322 4294           SPAGAIN;
323             }
324              
325             /* At this point we have decided to generate an event callback */
326              
327 22477 100         argspec = h->argspec ? SvPV(h->argspec, my_na) : "";
    50          
328              
329 22477 100         if (SvTYPE(h->cb) == SVt_PVAV) {
330              
331 8945 100         if (*argspec == ARG_FLAG_FLAT_ARRAY) {
332 502           argspec++;
333 502           array = (AV*)h->cb;
334             }
335             else {
336             /* start sub-array for accumulator array */
337 8945           array = newAV();
338             }
339             }
340             else {
341 13532           array = 0;
342 13532 50         if (*argspec == ARG_FLAG_FLAT_ARRAY)
343 0           argspec++;
344              
345             /* start argument stack for callback */
346 13532           ENTER;
347 13532           SAVETMPS;
348 13532 50         PUSHMARK(SP);
349             }
350              
351 97353 100         for (s = argspec; *s; s++) {
352 74876           SV* arg = 0;
353 74876           int push_arg = 1;
354 74876           enum argcode argcode = (enum argcode)*s;
355              
356 74876           switch( argcode ) {
357              
358             case ARG_SELF:
359 13165           arg = sv_mortalcopy(self);
360 13165           break;
361              
362             case ARG_TOKENS:
363 85 100         if (num_tokens >= 1) {
364 68           AV* av = newAV();
365 68           SV* prev_token = &PL_sv_undef;
366             int i;
367 68           av_extend(av, num_tokens);
368 473 100         for (i = 0; i < num_tokens; i++) {
369 405 50         if (tokens[i].beg) {
370 405           prev_token = newSVpvn(tokens[i].beg, tokens[i].end-tokens[i].beg);
371 405 50         if (utf8)
372 0           SvUTF8_on(prev_token);
373 405           av_push(av, prev_token);
374             }
375             else { /* boolean */
376 0 0         av_push(av, p_state->bool_attr_val
377             ? newSVsv(p_state->bool_attr_val)
378             : newSVsv(prev_token));
379             }
380             }
381 68           arg = sv_2mortal(newRV_noinc((SV*)av));
382             }
383 85           break;
384              
385             case ARG_TOKENPOS:
386 84 100         if (num_tokens >= 1 && tokens[0].beg >= beg) {
    100          
387 32           AV* av = newAV();
388             int i;
389 32           av_extend(av, num_tokens*2);
390 401 100         for (i = 0; i < num_tokens; i++) {
391 369 100         if (tokens[i].beg) {
392 367 100         av_push(av, newSViv(CHR_DIST(tokens[i].beg, beg)));
393 367 100         av_push(av, newSViv(CHR_DIST(tokens[i].end, tokens[i].beg)));
394             }
395             else { /* boolean tag value */
396 2           av_push(av, newSViv(0));
397 2           av_push(av, newSViv(0));
398             }
399             }
400 32           arg = sv_2mortal(newRV_noinc((SV*)av));
401             }
402 84           break;
403              
404             case ARG_TOKEN0:
405             case ARG_TAGNAME:
406             /* fall through */
407              
408             case ARG_TAG:
409 6401 100         if (num_tokens >= 1) {
410 6333           arg = sv_2mortal(newSVpvn(tokens[0].beg,
411             tokens[0].end - tokens[0].beg));
412 6333 100         if (utf8)
413 29           SvUTF8_on(arg);
414 6333 100         if (!CASE_SENSITIVE(p_state) && argcode != ARG_TOKEN0)
    100          
    100          
415 6215           sv_lower(aTHX_ arg);
416 6333 100         if (argcode == ARG_TAG && event != E_START) {
    100          
417 10           char *e_type = "!##/#?#";
418 10           sv_insert(arg, 0, 0, &e_type[event], 1);
419             }
420             }
421 6401           break;
422              
423             case ARG_ATTR:
424             case ARG_ATTRARR:
425 4146 100         if (event == E_START) {
426             HV* hv;
427             int i;
428 4054 100         if (argcode == ARG_ATTR) {
429 4045           hv = newHV();
430 4045           arg = sv_2mortal(newRV_noinc((SV*)hv));
431             }
432             else {
433             #ifdef __GNUC__
434             /* gcc -Wall reports this variable as possibly used uninitialized */
435 9           hv = 0;
436             #endif
437 9           push_arg = 0; /* deal with argument pushing here */
438             }
439              
440 14259 100         for (i = 1; i < num_tokens; i += 2) {
441 10205           SV* attrname = newSVpvn(tokens[i].beg,
442             tokens[i].end-tokens[i].beg);
443             SV* attrval;
444              
445 10205 100         if (utf8)
446 37           SvUTF8_on(attrname);
447 10205 100         if (tokens[i+1].beg) {
448 3196           char *beg = tokens[i+1].beg;
449 3196           STRLEN len = tokens[i+1].end - beg;
450 3196 100         if (*beg == '"' || *beg == '\'' || (*beg == '`' && p_state->backquote)) {
    100          
    100          
    100          
451             assert(len >= 2 && *beg == beg[len-1]);
452 2203           beg++; len -= 2;
453             }
454 3196           attrval = newSVpvn(beg, len);
455 3196 100         if (utf8)
456 34           SvUTF8_on(attrval);
457 3196 100         if (!p_state->attr_encoded) {
458 3194 100         if (p_state->utf8_mode) {
459 20           sv_utf8_decode(attrval);
460 20           sv_utf8_upgrade(attrval);
461             }
462 3194           decode_entities(aTHX_ attrval, p_state->entity2char, 0);
463 3194 100         if (p_state->utf8_mode)
464 3196           SvUTF8_off(attrval);
465             }
466             }
467             else { /* boolean */
468 7009 50         if (p_state->bool_attr_val)
469 0           attrval = newSVsv(p_state->bool_attr_val);
470             else
471 7009           attrval = newSVsv(attrname);
472             }
473              
474 10205 100         if (!CASE_SENSITIVE(p_state))
    100          
475 10188           sv_lower(aTHX_ attrname);
476              
477 10205 100         if (argcode == ARG_ATTR) {
478 20392           if (hv_exists_ent(hv, attrname, 0) ||
479 10195           !hv_store_ent(hv, attrname, attrval, 0)) {
480 2           SvREFCNT_dec(attrval);
481             }
482 10197           SvREFCNT_dec(attrname);
483             }
484             else { /* ARG_ATTRARR */
485 8 100         if (array) {
486 4           av_push(array, attrname);
487 4           av_push(array, attrval);
488             }
489             else {
490 4 50         mXPUSHs(attrname);
491 4 50         mXPUSHs(attrval);
492             }
493             }
494             }
495             }
496 92 100         else if (argcode == ARG_ATTRARR) {
497 18           push_arg = 0;
498             }
499 4146           break;
500              
501             case ARG_ATTRSEQ: /* (v2 compatibility stuff) */
502 3969 100         if (event == E_START) {
503 3944           AV* av = newAV();
504             int i;
505 14033 100         for (i = 1; i < num_tokens; i += 2) {
506 10089           SV* attrname = newSVpvn(tokens[i].beg,
507             tokens[i].end-tokens[i].beg);
508 10089 50         if (utf8)
509 0           SvUTF8_on(attrname);
510 10089 100         if (!CASE_SENSITIVE(p_state))
    100          
511 10075           sv_lower(aTHX_ attrname);
512 10089           av_push(av, attrname);
513             }
514 3944           arg = sv_2mortal(newRV_noinc((SV*)av));
515             }
516 3969           break;
517              
518             case ARG_TEXT:
519 22184           arg = sv_2mortal(newSVpvn(beg, end - beg));
520 22184 100         if (utf8)
521 115           SvUTF8_on(arg);
522 22184           break;
523              
524             case ARG_DTEXT:
525 156 100         if (event == E_TEXT) {
526 79           arg = sv_2mortal(newSVpvn(beg, end - beg));
527 79 100         if (utf8)
528 23           SvUTF8_on(arg);
529 79 100         if (!p_state->is_cdata) {
530 71 100         if (p_state->utf8_mode) {
531 8           sv_utf8_decode(arg);
532 8           sv_utf8_upgrade(arg);
533             }
534 71           decode_entities(aTHX_ arg, p_state->entity2char, 1);
535 71 100         if (p_state->utf8_mode)
536 8           SvUTF8_off(arg);
537             }
538             }
539 156           break;
540              
541             case ARG_IS_CDATA:
542 15214 100         if (event == E_TEXT) {
543 15161 100         arg = boolSV(p_state->is_cdata);
544             }
545 15214           break;
546              
547             case ARG_SKIPPED_TEXT:
548 14           arg = sv_2mortal(p_state->skipped_text);
549 14           p_state->skipped_text = newSVpvs("");
550 14           break;
551              
552             case ARG_OFFSET:
553 122           arg = sv_2mortal(newSViv(offset));
554 122           break;
555              
556             case ARG_OFFSET_END:
557 74 100         arg = sv_2mortal(newSViv(offset + CHR_DIST(end, beg)));
558 74           break;
559              
560             case ARG_LENGTH:
561 101 100         arg = sv_2mortal(newSViv(CHR_DIST(end, beg)));
562 101           break;
563              
564             case ARG_LINE:
565 39           arg = sv_2mortal(newSViv(line));
566 39           break;
567              
568             case ARG_COLUMN:
569 95           arg = sv_2mortal(newSViv(column));
570 95           break;
571              
572             case ARG_EVENT:
573             assert(event >= 0 && event < EVENT_COUNT);
574 708           arg = sv_2mortal(newSVpv(event_id_str[event], 0));
575 708           break;
576              
577             case ARG_LITERAL:
578             {
579 8319           int len = (unsigned char)s[1];
580 8319           arg = sv_2mortal(newSVpvn(s+2, len));
581 8319 50         if (SvUTF8(h->argspec))
582 0           SvUTF8_on(arg);
583 8319           s += len + 1;
584             }
585 8319           break;
586              
587             case ARG_UNDEF:
588 0           arg = sv_mortalcopy(&PL_sv_undef);
589 0           break;
590              
591             default:
592 0           arg = sv_2mortal(newSVpvf("Bad argspec %d", *s));
593 0           break;
594             }
595              
596 74876 100         if (push_arg) {
597 74849 100         if (!arg)
598 366           arg = sv_mortalcopy(&PL_sv_undef);
599              
600 74849 100         if (array) {
601             /* have to fix mortality here or add mortality to
602             * XPUSHs after removing it from the switch cases.
603             */
604 31113           av_push(array, SvREFCNT_inc(arg));
605             }
606             else {
607 43736 50         XPUSHs(arg);
608             }
609             }
610             }
611              
612 22477 100         if (array) {
613 8945 100         if (array != (AV*)h->cb)
614 8945           av_push((AV*)h->cb, newRV_noinc((SV*)array));
615             }
616             else {
617 13532           PUTBACK;
618              
619 26597 100         if ((enum argcode)*argspec == ARG_SELF && !SvROK(h->cb)) {
    100          
620 13065 50         char *method = SvPV(h->cb, my_na);
621 13065           call_method(method, G_DISCARD | G_EVAL | G_VOID);
622             }
623             else {
624 467           call_sv(h->cb, G_DISCARD | G_EVAL | G_VOID);
625             }
626              
627 13532 50         if (SvTRUE(ERRSV)) {
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
628 1           RETHROW;
629             }
630              
631 13531 100         FREETMPS;
632 13531           LEAVE;
633             }
634 22476 100         if (p_state->skipped_text)
635 14           SvCUR_set(p_state->skipped_text, 0);
636 22476           return;
637              
638             IGNORE_EVENT:
639 2268 100         if (p_state->skipped_text) {
640 38 100         if (event != E_TEXT && p_state->pend_text && SvOK(p_state->pend_text))
    100          
    50          
    0          
    0          
641 2           flush_pending_text(p_state, self);
642 38 100         if (utf8 && !SvUTF8(p_state->skipped_text))
    100          
643 1           sv_utf8_upgrade(p_state->skipped_text);
644 38 100         if (utf8 || !SvUTF8(p_state->skipped_text)) {
    100          
645 37           sv_catpvn(p_state->skipped_text, beg, end - beg);
646             }
647             else {
648 1           SV *tmp = newSVpvn(beg, end - beg);
649 1           sv_utf8_upgrade(tmp);
650 1           sv_catsv(p_state->skipped_text, tmp);
651 1           SvREFCNT_dec(tmp);
652             }
653             }
654             #undef CHR_DIST
655 29265           return;
656             }
657              
658              
659             EXTERN SV*
660 463           argspec_compile(SV* src, PSTATE* p_state)
661             {
662             dTHX;
663 463           SV* argspec = newSVpvs("");
664             STRLEN len;
665 463 50         char *s = SvPV(src, len);
666 463           char *end = s + len;
667              
668 463 50         if (SvUTF8(src))
669 0           SvUTF8_on(argspec);
670              
671 463 50         while (isHSPACE(*s))
672 0           s++;
673              
674 463 100         if (*s == '@') {
675             /* try to deal with '@{ ... }' wrapping */
676 7           char *tmp = s + 1;
677 7 50         while (isHSPACE(*tmp))
678 0           tmp++;
679 7 50         if (*tmp == '{') {
680 7           char c = ARG_FLAG_FLAT_ARRAY;
681 7           sv_catpvn(argspec, &c, 1);
682 7           tmp++;
683 7 50         while (isHSPACE(*tmp))
684 0           tmp++;
685 7           s = tmp;
686             }
687             }
688 1271 100         while (s < end) {
689 2465 100         if (isHNAME_FIRST(*s) || *s == '@') {
    100          
690 1200           char *name = s;
691 1200           int a = ARG_SELF;
692             const char * const *arg_name;
693              
694 1200           s++;
695 6076 100         while (isHNAME_CHAR(*s))
696 4876           s++;
697              
698             /* check identifier */
699 7622 100         for ( arg_name = argname; a < ARG_LITERAL ; ++a, ++arg_name ) {
700 7620 100         if (strnEQ(*arg_name, name, s - name) &&
    100          
701 1202           (*arg_name)[s - name] == '\0')
702 1198           break;
703             }
704 1200 100         if (a < ARG_LITERAL) {
705 1198           char c = (unsigned char) a;
706 1198           sv_catpvn(argspec, &c, 1);
707              
708 1198 100         if (a == ARG_LINE || a == ARG_COLUMN) {
    100          
709 9 100         if (!p_state->line)
710 5           p_state->line = 1; /* enable tracing of line/column */
711             }
712 1198 100         if (a == ARG_SKIPPED_TEXT) {
713 7 100         if (!p_state->skipped_text) {
714 5           p_state->skipped_text = newSVpvs("");
715             }
716             }
717 1198 100         if (a == ARG_ATTR || a == ARG_ATTRARR) {
    100          
718 161 100         if (p_state->argspec_entity_decode != ARG_DTEXT)
719 79           p_state->argspec_entity_decode = ARG_ATTR;
720             }
721 1116 100         else if (a == ARG_DTEXT) {
722 1198           p_state->argspec_entity_decode = ARG_DTEXT;
723             }
724             }
725             else {
726 2           croak("Unrecognized identifier %.*s in argspec", (int) (s - name), name);
727             }
728             }
729 131 100         else if (*s == '"' || *s == '\'') {
    50          
730 67           char *string_beg = s;
731 67           s++;
732 656 100         while (s < end && *s != *string_beg && *s != '\\')
    100          
    100          
733 589           s++;
734 67 100         if (*s == *string_beg) {
735             /* literal */
736 65           int len = s - string_beg - 1;
737             unsigned char buf[2];
738 65 100         if (len > 255)
739 1           croak("Literal string is longer than 255 chars in argspec");
740 64           buf[0] = ARG_LITERAL;
741 64           buf[1] = len;
742 64           sv_catpvn(argspec, (char*)buf, 2);
743 64           sv_catpvn(argspec, string_beg+1, len);
744 64           s++;
745             }
746 2 100         else if (*s == '\\') {
747 1           croak("Backslash reserved for literal string in argspec");
748             }
749             else {
750 1           croak("Unterminated literal string in argspec");
751             }
752             }
753             else {
754 0           croak("Bad argspec (%s)", s);
755             }
756              
757 1263 100         while (isHSPACE(*s))
758 1           s++;
759              
760 1262 100         if (*s == '}' && SvPVX(argspec)[0] == ARG_FLAG_FLAT_ARRAY) {
    50          
761             /* end of '@{ ... }' */
762 7           s++;
763 7 50         while (isHSPACE(*s))
764 0           s++;
765 7 50         if (s < end)
766 0           croak("Bad argspec: stuff after @{...} (%s)", s);
767             }
768              
769 1262 100         if (s == end)
770 453           break;
771 809 100         if (*s != ',') {
772 1           croak("Missing comma separator in argspec");
773             }
774 808           s++;
775 840 100         while (isHSPACE(*s))
776 32           s++;
777             }
778 457           return argspec;
779             }
780              
781              
782             static void
783 4358           flush_pending_text(PSTATE* p_state, SV* self)
784             {
785             dTHX;
786 4358           bool old_unbroken_text = p_state->unbroken_text;
787 4358           SV* old_pend_text = p_state->pend_text;
788 4358           bool old_is_cdata = p_state->is_cdata;
789 4358           STRLEN old_offset = p_state->offset;
790 4358           STRLEN old_line = p_state->line;
791 4358           STRLEN old_column = p_state->column;
792              
793             assert(p_state->pend_text && SvOK(p_state->pend_text));
794              
795 4358           p_state->unbroken_text = 0;
796 4358           p_state->pend_text = 0;
797 4358           p_state->is_cdata = p_state->pend_text_is_cdata;
798 4358           p_state->offset = p_state->pend_text_offset;
799 4358           p_state->line = p_state->pend_text_line;
800 4358           p_state->column = p_state->pend_text_column;
801              
802 4358           report_event(p_state, E_TEXT,
803 4358           SvPVX(old_pend_text), SvEND(old_pend_text),
804 4358           SvUTF8(old_pend_text), 0, 0, self);
805 4358 50         SvOK_off(old_pend_text);
806              
807 4358           p_state->unbroken_text = old_unbroken_text;
808 4358           p_state->pend_text = old_pend_text;
809 4358           p_state->is_cdata = old_is_cdata;
810 4358           p_state->offset = old_offset;
811 4358           p_state->line = old_line;
812 4358           p_state->column = old_column;
813 4358           }
814              
815             static char*
816 2934           skip_until_gt(char *beg, char *end)
817             {
818             /* tries to emulate quote skipping behaviour observed in MSIE */
819 2934           char *s = beg;
820 2934           char quote = '\0';
821 2934           char prev = ' ';
822 11243 100         while (s < end) {
823 11184 100         if (!quote && *s == '>')
    100          
824 2875           return s;
825 8309 100         if (*s == '"' || *s == '\'') {
    100          
826 69 100         if (*s == quote) {
827 6           quote = '\0'; /* end of quoted string */
828             }
829 63 50         else if (!quote && (prev == ' ' || prev == '=')) {
    100          
    50          
830 6           quote = *s;
831             }
832             }
833 8309           prev = *s++;
834             }
835 59           return end;
836             }
837              
838             static char*
839 177           parse_comment(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
840             {
841 177           char *s = beg;
842              
843 177 100         if (p_state->strict_comment) {
844 6           dTOKENS(4);
845 6           char *start_com = s; /* also used to signal inside/outside */
846              
847             while (1) {
848             /* try to locate "--" */
849             FIND_DASH_DASH:
850             /* printf("find_dash_dash: [%s]\n", s); */
851 125 50         while (s < end && *s != '-' && *s != '>')
    100          
    100          
852 104           s++;
853              
854 21 50         if (s == end) {
855 0 0         FREE_TOKENS;
856 0           return beg;
857             }
858              
859 21 100         if (*s == '>') {
860 8           s++;
861 8 100         if (start_com)
862 2           goto FIND_DASH_DASH;
863              
864             /* we are done recognizing all comments, make callbacks */
865 6           report_event(p_state, E_COMMENT,
866             beg - 4, s, utf8,
867             tokens, num_tokens,
868             self);
869 6 50         FREE_TOKENS;
870              
871 6           return s;
872             }
873              
874 13           s++;
875 13 50         if (s == end) {
876 0 0         FREE_TOKENS;
877 0           return beg;
878             }
879              
880 13 100         if (*s == '-') {
881             /* two dashes in a row seen */
882 12           s++;
883             /* do something */
884 12 100         if (start_com) {
885 9 50         PUSH_TOKEN(start_com, s-2);
886 9           start_com = 0;
887             }
888             else {
889 3           start_com = s;
890             }
891             }
892 19           }
893             }
894 171 100         else if (p_state->no_dash_dash_comment_end) {
895             token_pos_t token;
896 2           token.beg = beg;
897             /* a lone '>' signals end-of-comment */
898 16 50         while (s < end && *s != '>')
    100          
899 14           s++;
900 2           token.end = s;
901 2 50         if (s < end) {
902 2           s++;
903 2           report_event(p_state, E_COMMENT, beg-4, s, utf8, &token, 1, self);
904 2           return s;
905             }
906             else {
907 2           return beg;
908             }
909             }
910             else { /* non-strict comment */
911             token_pos_t token;
912 169           token.beg = beg;
913             /* try to locate /--\s*>/ which signals end-of-comment */
914             LOCATE_END:
915 2330 100         while (s < end && *s != '-')
    100          
916 2131           s++;
917 199           token.end = s;
918 199 100         if (s < end) {
919 95           s++;
920 95 100         if (*s == '-') {
921 78           s++;
922 93 100         while (isHSPACE(*s))
923 15           s++;
924 78 100         if (*s == '>') {
925 48           s++;
926             /* yup */
927 48           report_event(p_state, E_COMMENT, beg-4, s, utf8, &token, 1, self);
928 169           return s;
929             }
930             }
931 47 100         if (s < end) {
932 30           s = token.end + 1;
933 30           goto LOCATE_END;
934             }
935             }
936              
937 121 50         if (s == end)
938 121           return beg;
939             }
940              
941 0           return 0;
942             }
943              
944              
945             #ifdef MARKED_SECTION
946              
947             static void
948 32           marked_section_update(PSTATE* p_state)
949             {
950             dTHX;
951             /* we look at p_state->ms_stack to determine p_state->ms */
952 32           AV* ms_stack = p_state->ms_stack;
953 32           p_state->ms = MS_NONE;
954              
955 32 50         if (ms_stack) {
956 32           int stack_len = av_len(ms_stack);
957             int stack_idx;
958 50 100         for (stack_idx = 0; stack_idx <= stack_len; stack_idx++) {
959 18           SV** svp = av_fetch(ms_stack, stack_idx, 0);
960 18 50         if (svp) {
961 18           AV* tokens = (AV*)SvRV(*svp);
962 18           int tokens_len = av_len(tokens);
963             int i;
964             assert(SvTYPE(tokens) == SVt_PVAV);
965 43 100         for (i = 0; i <= tokens_len; i++) {
966 25           SV** svp = av_fetch(tokens, i, 0);
967 25 50         if (svp) {
968             STRLEN len;
969 25 50         char *token_str = SvPV(*svp, len);
970             enum marked_section_t token;
971 25 100         if (strEQ(token_str, "include"))
972 11           token = MS_INCLUDE;
973 14 100         else if (strEQ(token_str, "rcdata"))
974 4           token = MS_RCDATA;
975 10 100         else if (strEQ(token_str, "cdata"))
976 7           token = MS_CDATA;
977 3 100         else if (strEQ(token_str, "ignore"))
978 2           token = MS_IGNORE;
979             else
980 1           token = MS_NONE;
981 25 100         if (p_state->ms < token)
982 25           p_state->ms = token;
983             }
984             }
985             }
986             }
987             }
988             /* printf("MS %d\n", p_state->ms); */
989 32           p_state->is_cdata = (p_state->ms == MS_CDATA);
990 32           return;
991             }
992              
993              
994             static char*
995 16           parse_marked_section(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
996             {
997             dTHX;
998             char *s;
999 16           AV* tokens = 0;
1000              
1001 16 50         if (!p_state->marked_sections)
1002 0           return 0;
1003              
1004             assert(beg[0] == '<');
1005             assert(beg[1] == '!');
1006             assert(beg[2] == '[');
1007 16           s = beg + 3;
1008              
1009             FIND_NAMES:
1010 22 100         while (isHSPACE(*s))
1011 5           s++;
1012 37 100         while (isHNAME_FIRST(*s)) {
1013 20           char *name_start = s;
1014             char *name_end;
1015             SV *name;
1016 20           s++;
1017 117 100         while (isHNAME_CHAR(*s))
1018 97           s++;
1019 20           name_end = s;
1020 39 100         while (isHSPACE(*s))
1021 19           s++;
1022 20 50         if (s == end)
1023 0           goto PREMATURE;
1024              
1025 20 100         if (!tokens)
1026 13           tokens = newAV();
1027 20           name = newSVpvn(name_start, name_end - name_start);
1028 20 100         if (utf8)
1029 1           SvUTF8_on(name);
1030 20           av_push(tokens, sv_lower(aTHX_ name));
1031             }
1032 17 100         if (*s == '-') {
1033 1           s++;
1034 1 50         if (*s == '-') {
1035             /* comment */
1036 1           s++;
1037             while (1) {
1038 9 50         while (s < end && *s != '-')
    100          
1039 8           s++;
1040 1 50         if (s == end)
1041 0           goto PREMATURE;
1042              
1043 1           s++; /* skip first '-' */
1044 1 50         if (*s == '-') {
1045 1           s++;
1046             /* comment finished */
1047 1           goto FIND_NAMES;
1048             }
1049 0           }
1050             }
1051             else
1052 0           goto FAIL;
1053              
1054             }
1055 16 50         if (*s == '[') {
1056 16           s++;
1057             /* yup */
1058              
1059 16 100         if (!tokens) {
1060 3           tokens = newAV();
1061 3           av_push(tokens, newSVpvs("include"));
1062             }
1063              
1064 16 100         if (!p_state->ms_stack)
1065 4           p_state->ms_stack = newAV();
1066 16           av_push(p_state->ms_stack, newRV_noinc((SV*)tokens));
1067 16           marked_section_update(p_state);
1068 16           report_event(p_state, E_NONE, beg, s, utf8, 0, 0, self);
1069 16           return s;
1070             }
1071              
1072             FAIL:
1073 0           SvREFCNT_dec(tokens);
1074 0           return 0; /* not yet implemented */
1075              
1076             PREMATURE:
1077 0           SvREFCNT_dec(tokens);
1078 0           return beg;
1079             }
1080             #endif
1081              
1082              
1083             static char*
1084 528           parse_decl(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1085             {
1086 528           char *s = beg + 2;
1087              
1088 528 100         if (*s == '-') {
1089             /* comment? */
1090              
1091             char *tmp;
1092 186           s++;
1093 186 100         if (s == end)
1094 6           return beg;
1095              
1096 180 100         if (*s != '-')
1097 3           goto DECL_FAIL; /* nope, illegal */
1098              
1099             /* yes, two dashes seen */
1100 177           s++;
1101              
1102 177           tmp = parse_comment(p_state, s, end, utf8, self);
1103 177 100         return (tmp == s) ? beg : tmp;
1104             }
1105              
1106             #ifdef MARKED_SECTION
1107 342 100         if (*s == '[') {
1108             /* marked section */
1109             char *tmp;
1110 16           tmp = parse_marked_section(p_state, beg, end, utf8, self);
1111 16 50         if (!tmp)
1112 0           goto DECL_FAIL;
1113 16           return tmp;
1114             }
1115             #endif
1116              
1117 326 100         if (*s == '>') {
1118             /* make into empty comment */
1119             token_pos_t token;
1120 1           token.beg = s;
1121 1           token.end = s;
1122 1           s++;
1123 1           report_event(p_state, E_COMMENT, beg, s, utf8, &token, 1, self);
1124 1           return s;
1125             }
1126              
1127 325 100         if (isALPHA(*s)) {
1128 295           dTOKENS(8);
1129 295           char *decl_id = s;
1130             STRLEN decl_id_len;
1131              
1132 295           s++;
1133             /* declaration */
1134 1965 100         while (s < end && isHNAME_CHAR(*s))
    100          
1135 1670           s++;
1136 295           decl_id_len = s - decl_id;
1137 295 100         if (s == end)
1138 16           goto PREMATURE;
1139              
1140             /* just hardcode a few names as the recognized declarations */
1141 545 100         if (!((decl_id_len == 7 &&
1142 266 50         strnEQx(decl_id, "DOCTYPE", 7, !CASE_SENSITIVE(p_state))) ||
    50          
1143 4 50         (decl_id_len == 6 &&
1144 4 100         strnEQx(decl_id, "ENTITY", 6, !CASE_SENSITIVE(p_state)))
    50          
1145             )
1146             )
1147             {
1148             goto FAIL;
1149             }
1150              
1151             /* first word available */
1152 269 50         PUSH_TOKEN(decl_id, s);
1153              
1154             while (1) {
1155 50732 100         while (s < end && isHSPACE(*s))
    100          
1156 25413           s++;
1157              
1158 25319 100         if (s == end)
1159 165           goto PREMATURE;
1160              
1161 25218 100         if (*s == '"' || *s == '\'' || (*s == '`' && p_state->backquote)) {
    50          
    50          
    0          
1162 129           char *str_beg = s;
1163 129           s++;
1164 2914 100         while (s < end && *s != *str_beg)
    100          
1165 2785           s++;
1166 129 100         if (s == end)
1167 65           goto PREMATURE;
1168 64           s++;
1169 64 50         PUSH_TOKEN(str_beg, s);
1170             }
1171 25025 100         else if (*s == '-') {
1172             /* comment */
1173 12405           char *com_beg = s;
1174 12405           s++;
1175 12405 50         if (s == end)
1176 0           goto PREMATURE;
1177 12405 50         if (*s != '-')
1178 0           goto FAIL;
1179 12405           s++;
1180              
1181             while (1) {
1182 62054 50         while (s < end && *s != '-')
    100          
1183 49647           s++;
1184 12407 50         if (s == end)
1185 0           goto PREMATURE;
1186 12407           s++;
1187 12407 50         if (s == end)
1188 0           goto PREMATURE;
1189 12407 100         if (*s == '-') {
1190 12405           s++;
1191 12405 50         PUSH_TOKEN(com_beg, s);
1192 12405           break;
1193             }
1194 12407           }
1195             }
1196 12620 100         else if (*s != '>') {
1197             /* plain word */
1198 12598           char *word_beg = s;
1199 12598           s++;
1200 25722 100         while (s < end && isHNOT_SPACE_GT(*s))
    100          
1201 13124           s++;
1202 12598 100         if (s == end)
1203 17           goto PREMATURE;
1204 12581 100         PUSH_TOKEN(word_beg, s);
1205             }
1206             else {
1207 22           break;
1208             }
1209 25050           }
1210              
1211 22 50         if (s == end)
1212 0           goto PREMATURE;
1213 22 50         if (*s == '>') {
1214 22           s++;
1215 22           report_event(p_state, E_DECLARATION, beg, s, utf8, tokens, num_tokens, self);
1216 22 100         FREE_TOKENS;
1217 285           return s;
1218             }
1219              
1220             FAIL:
1221 10 50         FREE_TOKENS;
1222 10           goto DECL_FAIL;
1223              
1224             PREMATURE:
1225 263 100         FREE_TOKENS;
1226 263           return beg;
1227              
1228             }
1229              
1230             DECL_FAIL:
1231 43 50         if (p_state->strict_comment)
1232 0           return 0;
1233              
1234             /* consider everything up to the first '>' a comment */
1235 5747 100         while (s < end && *s != '>')
    100          
1236 5704           s++;
1237 43 100         if (s < end) {
1238             token_pos_t token;
1239 35           token.beg = beg + 2;
1240 35           token.end = s;
1241 35           s++;
1242 35           report_event(p_state, E_COMMENT, beg, s, utf8, &token, 1, self);
1243 35           return s;
1244             }
1245             else {
1246 8           return beg;
1247             }
1248             }
1249              
1250              
1251             static char*
1252 6348           parse_start(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1253             {
1254 6348           char *s = beg;
1255 6348           int empty_tag = 0;
1256 6348           dTOKENS(16);
1257              
1258             hctype_t tag_name_char;
1259             hctype_t attr_name_first, attr_name_char;
1260              
1261 6348 100         if (STRICT_NAMES(p_state)) {
    50          
1262 20           attr_name_first = HCTYPE_NAME_FIRST;
1263 20           tag_name_char = attr_name_char = HCTYPE_NAME_CHAR;
1264             }
1265             else {
1266 6328           tag_name_char = HCTYPE_NOT_SPACE_GT;
1267 6328           attr_name_first = HCTYPE_NOT_SPACE_GT;
1268 6328           attr_name_char = HCTYPE_NOT_SPACE_EQ_GT;
1269             }
1270              
1271 6348           s += 2;
1272              
1273 98992 100         while (s < end && isHCTYPE(*s, tag_name_char)) {
    100          
1274 92644 100         if (*s == '/' && ALLOW_EMPTY_TAG(p_state)) {
    50          
    50          
1275 0 0         if ((s + 1) == end)
1276 0           goto PREMATURE;
1277 0 0         if (*(s + 1) == '>')
1278 0           break;
1279             }
1280 92644           s++;
1281             }
1282 6348 50         PUSH_TOKEN(beg+1, s); /* tagname */
1283              
1284 11638 100         while (isHSPACE(*s))
1285 5290           s++;
1286 6348 100         if (s == end)
1287 223           goto PREMATURE;
1288              
1289 31474 100         while (isHCTYPE(*s, attr_name_first)) {
1290             /* attribute */
1291 26617           char *attr_name_beg = s;
1292             char *attr_name_end;
1293 26617 100         if (*s == '/' && ALLOW_EMPTY_TAG(p_state)) {
    50          
    100          
1294 4 50         if ((s + 1) == end)
1295 0           goto PREMATURE;
1296 4 50         if (*(s + 1) == '>')
1297 4           break;
1298             }
1299 26613           s++;
1300 376403 100         while (s < end && isHCTYPE(*s, attr_name_char)) {
    100          
1301 349790 100         if (*s == '/' && ALLOW_EMPTY_TAG(p_state)) {
    50          
    50          
1302 0 0         if ((s + 1) == end)
1303 0           goto PREMATURE;
1304 0 0         if (*(s + 1) == '>')
1305 0           break;
1306             }
1307 349790           s++;
1308             }
1309 26613 100         if (s == end)
1310 665           goto PREMATURE;
1311              
1312 25948           attr_name_end = s;
1313 25948 100         PUSH_TOKEN(attr_name_beg, attr_name_end); /* attr name */
1314              
1315 34041 100         while (isHSPACE(*s))
1316 8093           s++;
1317 25948 100         if (s == end)
1318 16           goto PREMATURE;
1319              
1320 25932 100         if (*s == '=') {
1321             /* with a value */
1322 16784           s++;
1323 16814 100         while (isHSPACE(*s))
1324 30           s++;
1325 16784 100         if (s == end)
1326 32           goto PREMATURE;
1327 16752 100         if (*s == '>') {
1328             /* parse it similar to ="" */
1329 6 50         PUSH_TOKEN(s, s);
1330 6           break;
1331             }
1332 19449 100         if (*s == '"' || *s == '\'' || (*s == '`' && p_state->backquote)) {
    100          
    100          
    100          
1333 2979           char *str_beg = s;
1334 2979           s++;
1335 32118 100         while (s < end && *s != *str_beg)
    100          
1336 29139           s++;
1337 2979 100         if (s == end)
1338 276           goto PREMATURE;
1339 2703           s++;
1340 2703 50         PUSH_TOKEN(str_beg, s);
1341             }
1342             else {
1343 13767           char *word_start = s;
1344 72922 100         while (s < end && isHNOT_SPACE_GT(*s)) {
    100          
1345 59157 100         if (*s == '/' && ALLOW_EMPTY_TAG(p_state)) {
    100          
    100          
1346 2 50         if ((s + 1) == end)
1347 0           goto PREMATURE;
1348 2 50         if (*(s + 1) == '>')
1349 2           break;
1350             }
1351 59155           s++;
1352             }
1353 13767 100         if (s == end)
1354 74           goto PREMATURE;
1355 13693 50         PUSH_TOKEN(word_start, s);
1356             }
1357 30165 100         while (isHSPACE(*s))
1358 13769           s++;
1359 16396 100         if (s == end)
1360 195           goto PREMATURE;
1361             }
1362             else {
1363 9148 50         PUSH_TOKEN(0, 0); /* boolean attr value */
1364             }
1365             }
1366              
1367 4867 100         if (ALLOW_EMPTY_TAG(p_state) && *s == '/') {
    100          
    100          
1368 7           s++;
1369 7 50         if (s == end)
1370 0           goto PREMATURE;
1371 7           empty_tag = 1;
1372             }
1373              
1374 4867 50         if (*s == '>') {
1375 4867           s++;
1376             /* done */
1377 4867           report_event(p_state, E_START, beg, s, utf8, tokens, num_tokens, self);
1378 4867 100         if (empty_tag) {
1379 7           report_event(p_state, E_END, s, s, utf8, tokens, 1, self);
1380             }
1381 4860 100         else if (!p_state->xml_mode) {
1382             /* find out if this start tag should put us into literal_mode
1383             */
1384             int i;
1385 4843           int tag_len = tokens[0].end - tokens[0].beg;
1386              
1387 38456 100         for (i = 0; literal_mode_elem[i].len; i++) {
1388 33718 100         if (tag_len == literal_mode_elem[i].len) {
1389             /* try to match it */
1390 549           char *s = beg + 1;
1391 549           char *t = literal_mode_elem[i].str;
1392 549           int len = tag_len;
1393 997 50         while (len) {
1394 997 100         if (toLOWER(*s) != *t)
    100          
1395 444           break;
1396 553           s++;
1397 553           t++;
1398 553 100         if (!--len) {
1399             /* found it */
1400 105           p_state->literal_mode = literal_mode_elem[i].str;
1401 105           p_state->is_cdata = literal_mode_elem[i].is_cdata;
1402             /* printf("Found %s\n", p_state->literal_mode); */
1403 105           goto END_OF_LITERAL_SEARCH;
1404             }
1405             }
1406             }
1407             }
1408             END_OF_LITERAL_SEARCH:
1409             ;
1410             }
1411              
1412 4867 100         FREE_TOKENS;
1413 4867           return s;
1414             }
1415              
1416 0 0         FREE_TOKENS;
1417 0           return 0;
1418              
1419             PREMATURE:
1420 1481 100         FREE_TOKENS;
1421 6348           return beg;
1422             }
1423              
1424              
1425             static char*
1426 2934           parse_end(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1427             {
1428 2934           char *s = beg+2;
1429             hctype_t name_first, name_char;
1430              
1431 2934 100         if (STRICT_NAMES(p_state)) {
    50          
1432 17           name_first = HCTYPE_NAME_FIRST;
1433 17           name_char = HCTYPE_NAME_CHAR;
1434             }
1435             else {
1436 2917           name_first = name_char = HCTYPE_NOT_SPACE_GT;
1437             }
1438              
1439 2934 100         if (isHCTYPE(*s, name_first)) {
1440             token_pos_t tagname;
1441 2927           tagname.beg = s;
1442 2927           s++;
1443 5278 100         while (s < end && isHCTYPE(*s, name_char))
    100          
1444 2351           s++;
1445 2927           tagname.end = s;
1446              
1447 2927 50         if (p_state->strict_end) {
1448 0 0         while (isHSPACE(*s))
1449 0           s++;
1450             }
1451             else {
1452 2927           s = skip_until_gt(s, end);
1453             }
1454 2927 100         if (s < end) {
1455 2869 50         if (*s == '>') {
1456 2869           s++;
1457             /* a complete end tag has been recognized */
1458 2869           report_event(p_state, E_END, beg, s, utf8, &tagname, 1, self);
1459 2926           return s;
1460             }
1461             }
1462             else {
1463 58           return beg;
1464             }
1465             }
1466 7 50         else if (!p_state->strict_comment) {
1467 7           s = skip_until_gt(s, end);
1468 7 100         if (s < end) {
1469             token_pos_t token;
1470 6           token.beg = beg + 2;
1471 6           token.end = s;
1472 6           s++;
1473 6           report_event(p_state, E_COMMENT, beg, s, utf8, &token, 1, self);
1474 6           return s;
1475             }
1476             else {
1477 1           return beg;
1478             }
1479             }
1480 0           return 0;
1481             }
1482              
1483              
1484             static char*
1485 67           parse_process(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1486             {
1487 67           char *s = beg + 2; /* skip '
1488             /* processing instruction */
1489             token_pos_t token_pos;
1490 67           token_pos.beg = s;
1491              
1492 8023 100         while (s < end) {
1493 8004 100         if (*s == '>') {
1494 50           token_pos.end = s;
1495 50           s++;
1496              
1497 50 100         if (p_state->xml_mode || p_state->xml_pic) {
    100          
1498             /* XML processing instructions are ended by "?>" */
1499 8 50         if (s - beg < 4 || s[-2] != '?')
    100          
1500 2           continue;
1501 6           token_pos.end = s - 2;
1502             }
1503              
1504             /* a complete processing instruction seen */
1505 48           report_event(p_state, E_PROCESS, beg, s, utf8,
1506             &token_pos, 1, self);
1507 48           return s;
1508             }
1509 7954           s++;
1510             }
1511 67           return beg; /* could not find end */
1512             }
1513              
1514              
1515             #ifdef USE_PFUNC
1516             static char*
1517             parse_null(PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1518             {
1519             return 0;
1520             }
1521              
1522              
1523              
1524             #include "pfunc.h" /* declares the parsefunc[] */
1525             #endif /* USE_PFUNC */
1526              
1527             static char*
1528 6346           parse_buf(pTHX_ PSTATE* p_state, char *beg, char *end, U32 utf8, SV* self)
1529             {
1530 6346           char *s = beg;
1531 6346           char *t = beg;
1532             char *new_pos;
1533              
1534 20999 100         while (!p_state->eof) {
1535             /*
1536             * At the start of this loop we will always be ready for eating text
1537             * or a new tag. We will never be inside some tag. The 't' points
1538             * to where we started and the 's' is advanced as we go.
1539             */
1540              
1541 21161 100         while (p_state->literal_mode) {
1542 287           char *l = p_state->literal_mode;
1543             char *end_text;
1544              
1545 2704 100         while (s < end && *s != '<') {
    100          
1546 2417           s++;
1547             }
1548              
1549 287 100         if (s == end) {
1550 119           s = t;
1551 119           goto DONE;
1552             }
1553              
1554 168           end_text = s;
1555 168           s++;
1556              
1557             /* here we rely on '\0' termination of perl svpv buffers */
1558 168 100         if (*s == '/') {
1559 147           s++;
1560 777 100         while (*l && toLOWER(*s) == *l) {
    100          
    100          
1561 630           s++;
1562 630           l++;
1563             }
1564              
1565 147 100         if (!*l && (strNE(p_state->literal_mode, "plaintext") || p_state->closing_plaintext)) {
    100          
    100          
1566             /* matched it all */
1567             token_pos_t end_token;
1568 110           end_token.beg = end_text + 2;
1569 110           end_token.end = s;
1570              
1571 110 50         while (isHSPACE(*s))
1572 0           s++;
1573 110 100         if (*s == '>') {
1574 101           s++;
1575 101 50         if (t != end_text)
1576 101           report_event(p_state, E_TEXT, t, end_text, utf8,
1577             0, 0, self);
1578 101           report_event(p_state, E_END, end_text, s, utf8,
1579             &end_token, 1, self);
1580 101           p_state->literal_mode = 0;
1581 101           p_state->is_cdata = 0;
1582 110           t = s;
1583             }
1584             }
1585             }
1586             }
1587              
1588             #ifdef MARKED_SECTION
1589 20883 100         while (p_state->ms == MS_CDATA || p_state->ms == MS_RCDATA) {
    100          
1590 116 100         while (s < end && *s != ']')
    100          
1591 106           s++;
1592 10 100         if (*s == ']') {
1593 9           char *end_text = s;
1594 9           s++;
1595 9 50         if (*s == ']' && *(s + 1) == '>') {
    100          
1596 8           s += 2;
1597             /* marked section end */
1598 8 50         if (t != end_text)
1599 8           report_event(p_state, E_TEXT, t, end_text, utf8,
1600             0, 0, self);
1601 8           report_event(p_state, E_NONE, end_text, s, utf8, 0, 0, self);
1602 8           t = s;
1603 8           SvREFCNT_dec(av_pop(p_state->ms_stack));
1604 8           marked_section_update(p_state);
1605 8           continue;
1606             }
1607             }
1608 2 100         if (s == end) {
1609 1           s = t;
1610 1           goto DONE;
1611             }
1612             }
1613             #endif
1614              
1615             /* first we try to match as much text as possible */
1616 1801933 100         while (s < end && *s != '<') {
    100          
1617             #ifdef MARKED_SECTION
1618 1781060 100         if (p_state->ms && *s == ']') {
    100          
1619 8           char *end_text = s;
1620 8           s++;
1621 8 50         if (*s == ']') {
1622 8           s++;
1623 8 50         if (*s == '>') {
1624 8           s++;
1625 8           report_event(p_state, E_TEXT, t, end_text, utf8,
1626             0, 0, self);
1627 8           report_event(p_state, E_NONE, end_text, s, utf8,
1628             0, 0, self);
1629 8           t = s;
1630 8           SvREFCNT_dec(av_pop(p_state->ms_stack));
1631 8           marked_section_update(p_state);
1632 8           continue;
1633             }
1634             }
1635             }
1636             #endif
1637 1781052           s++;
1638             }
1639 20873 100         if (s != t) {
1640 17212 100         if (*s == '<') {
1641 13296           report_event(p_state, E_TEXT, t, s, utf8, 0, 0, self);
1642 13296           t = s;
1643             }
1644             else {
1645 3916           s--;
1646 3916 100         if (isHSPACE(*s)) {
1647             /* wait with white space at end */
1648 780 100         while (s >= t && isHSPACE(*s))
    100          
1649 421           s--;
1650             }
1651             else {
1652             /* might be a chopped up entities/words */
1653 112158 100         while (s >= t && !isHSPACE(*s))
    100          
1654 108601           s--;
1655 6564 100         while (s >= t && isHSPACE(*s))
    100          
1656 3007           s--;
1657             }
1658 3916           s++;
1659 3916 100         if (s != t)
1660 2894           report_event(p_state, E_TEXT, t, s, utf8, 0, 0, self);
1661 3916           break;
1662             }
1663             }
1664              
1665 16957 100         if (end - s < 3)
1666 346           break;
1667              
1668             /* next char is known to be '<' and pointed to by 't' as well as 's' */
1669 16611           s++;
1670              
1671             #ifdef USE_PFUNC
1672             new_pos = parsefunc[(unsigned char)*s](p_state, t, end, utf8, self);
1673             #else
1674 16611 100         if (isHNAME_FIRST(*s))
1675 6348           new_pos = parse_start(p_state, t, end, utf8, self);
1676 10263 100         else if (*s == '/')
1677 2934           new_pos = parse_end(p_state, t, end, utf8, self);
1678 7329 100         else if (*s == '!')
1679 528           new_pos = parse_decl(p_state, t, end, utf8, self);
1680 6801 100         else if (*s == '?')
1681 67           new_pos = parse_process(p_state, t, end, utf8, self);
1682             else
1683 6734           new_pos = 0;
1684             #endif /* USE_PFUNC */
1685              
1686 16610 100         if (new_pos) {
1687 9876 100         if (new_pos == t) {
1688             /* no progress, need more data to know what it is */
1689 1957           s = t;
1690 1957           break;
1691             }
1692 7919           t = s = new_pos;
1693             }
1694              
1695             /* if we get out here then this was not a conforming tag, so
1696             * treat it is plain text at the top of the loop again (we
1697             * have already skipped past the "<").
1698             */
1699             }
1700              
1701             DONE:
1702 6345           return s;
1703              
1704             }
1705              
1706             EXTERN void
1707 6544           parse(pTHX_
1708             PSTATE* p_state,
1709             SV* chunk,
1710             SV* self)
1711             {
1712             char *s, *beg, *end;
1713 6544           U32 utf8 = 0;
1714             STRLEN len;
1715              
1716 6544 100         if (!p_state->start_document) {
1717             char dummy[1];
1718 211           report_event(p_state, E_START_DOCUMENT, dummy, dummy, 0, 0, 0, self);
1719 211           p_state->start_document = 1;
1720             }
1721              
1722 6544 100         if (!chunk) {
1723             /* eof */
1724             char empty[1];
1725 197 100         if (p_state->buf && SvOK(p_state->buf)) {
    100          
    50          
    50          
1726             /* flush it */
1727 149 50         s = SvPV(p_state->buf, len);
1728 149           end = s + len;
1729 149           utf8 = SvUTF8(p_state->buf);
1730             assert(len);
1731              
1732 152 50         while (s < end) {
1733 152 100         if (p_state->literal_mode) {
1734 2 50         if (strEQ(p_state->literal_mode, "plaintext") ||
    0          
1735 0 0         strEQ(p_state->literal_mode, "xmp") ||
1736 0 0         strEQ(p_state->literal_mode, "iframe") ||
1737 0           strEQ(p_state->literal_mode, "textarea"))
1738             {
1739             /* rest is considered text */
1740             break;
1741             }
1742 0 0         if (strEQ(p_state->literal_mode, "script") ||
    0          
1743 0           strEQ(p_state->literal_mode, "style"))
1744 0           {
1745             /* effectively make it an empty element */
1746             token_pos_t t;
1747             char dummy;
1748 0           t.beg = p_state->literal_mode;
1749 0           t.end = p_state->literal_mode + strlen(p_state->literal_mode);
1750 0           report_event(p_state, E_END, &dummy, &dummy, 0, &t, 1, self);
1751             }
1752             else {
1753 0           p_state->pending_end_tag = p_state->literal_mode;
1754             }
1755 0           p_state->literal_mode = 0;
1756 0           s = parse_buf(aTHX_ p_state, s, end, utf8, self);
1757 0           continue;
1758             }
1759              
1760 150 100         if (!p_state->strict_comment && !p_state->no_dash_dash_comment_end && *s == '<') {
    100          
    100          
1761 3           p_state->no_dash_dash_comment_end = 1;
1762 3           s = parse_buf(aTHX_ p_state, s, end, utf8, self);
1763 3           continue;
1764             }
1765              
1766 147 100         if (!p_state->strict_comment && *s == '<') {
    100          
1767 3           char *s1 = s + 1;
1768 3 50         if (s1 == end || isHNAME_FIRST(*s1) || *s1 == '/' || *s1 == '!' || *s1 == '?') {
    100          
    100          
    50          
    50          
1769             /* some kind of unterminated markup. Report rest as as comment */
1770             token_pos_t token;
1771 2           token.beg = s + 1;
1772 2           token.end = end;
1773 2           report_event(p_state, E_COMMENT, s, end, utf8, &token, 1, self);
1774 2           s = end;
1775             }
1776             }
1777              
1778 147           break;
1779             }
1780              
1781 149 100         if (s < end) {
1782             /* report rest as text */
1783 147           report_event(p_state, E_TEXT, s, end, utf8, 0, 0, self);
1784             }
1785              
1786 149           SvREFCNT_dec(p_state->buf);
1787 149           p_state->buf = 0;
1788             }
1789 197 100         if (p_state->pend_text && SvOK(p_state->pend_text))
    50          
    0          
    0          
1790 60           flush_pending_text(p_state, self);
1791              
1792 197 50         if (p_state->ignoring_element) {
1793             /* document not balanced */
1794 0           SvREFCNT_dec(p_state->ignoring_element);
1795 0           p_state->ignoring_element = 0;
1796             }
1797 197           report_event(p_state, E_END_DOCUMENT, empty, empty, 0, 0, 0, self);
1798              
1799             /* reset state */
1800 197           p_state->offset = 0;
1801 197 100         if (p_state->line)
1802 10           p_state->line = 1;
1803 197           p_state->column = 0;
1804 197           p_state->start_document = 0;
1805 197           p_state->literal_mode = 0;
1806 197           p_state->is_cdata = 0;
1807 197           return;
1808             }
1809              
1810 6347 100         if (p_state->utf8_mode)
1811 5           sv_utf8_downgrade(chunk, 0);
1812              
1813 6347 100         if (p_state->buf && SvOK(p_state->buf)) {
    100          
    50          
    50          
1814 6016           sv_catsv(p_state->buf, chunk);
1815 6016 50         beg = SvPV(p_state->buf, len);
1816 6016           utf8 = SvUTF8(p_state->buf);
1817             }
1818             else {
1819 331 100         beg = SvPV(chunk, len);
1820 331           utf8 = SvUTF8(chunk);
1821 331 100         if (p_state->offset == 0 && DOWARN) {
    100          
1822             /* Print warnings if we find unexpected Unicode BOM forms */
1823 15 100         if (p_state->argspec_entity_decode &&
    100          
1824 14 50         !(p_state->attr_encoded && p_state->argspec_entity_decode == ARG_ATTR) &&
    100          
1825 10 100         !p_state->utf8_mode && (
1826 10 50         (!utf8 && len >= 3 && strnEQ(beg, "\xEF\xBB\xBF", 3)) ||
    100          
    100          
1827 8 50         (utf8 && len >= 6 && strnEQ(beg, "\xC3\xAF\xC2\xBB\xC2\xBF", 6)) ||
    100          
    100          
1828 5 100         (!utf8 && probably_utf8_chunk(aTHX_ beg, len))
1829             )
1830             )
1831             {
1832 4           warn("Parsing of undecoded UTF-8 will give garbage when decoding entities");
1833             }
1834 15 100         if (utf8 && len >= 2 && strnEQ(beg, "\xFF\xFE", 2)) {
    50          
    50          
1835 0           warn("Parsing string decoded with wrong endianness");
1836             }
1837 15 100         if (!utf8 && len >= 4 &&
    50          
    100          
1838 11 100         (strnEQ(beg, "\x00\x00\xFE\xFF", 4) ||
1839 11           strnEQ(beg, "\xFE\xFF\x00\x00", 4))
1840             )
1841             {
1842 2           warn("Parsing of undecoded UTF-32");
1843             }
1844 13 100         else if (!utf8 && len >= 2 &&
    50          
    100          
1845 9 100         (strnEQ(beg, "\xFE\xFF", 2) || strnEQ(beg, "\xFF\xFE", 2))
1846             )
1847             {
1848 2           warn("Parsing of undecoded UTF-16");
1849             }
1850             }
1851             }
1852              
1853 6347 100         if (!len)
1854 4           return; /* nothing to do */
1855              
1856 6343           end = beg + len;
1857 6343           s = parse_buf(aTHX_ p_state, beg, end, utf8, self);
1858              
1859 6342 100         if (s == end || p_state->eof) {
    100          
1860 280 100         if (p_state->buf) {
1861 105 100         SvOK_off(p_state->buf);
1862             }
1863             }
1864             else {
1865             /* need to keep rest in buffer */
1866 6167 100         if (p_state->buf) {
1867             /* chop off some chars at the beginning */
1868 6011 100         if (SvOK(p_state->buf)) {
    50          
    50          
1869 5925           sv_chop(p_state->buf, s);
1870             }
1871             else {
1872 86           sv_setpvn(p_state->buf, s, end - s);
1873 86 100         if (utf8)
1874 16           SvUTF8_on(p_state->buf);
1875             else
1876 6011           SvUTF8_off(p_state->buf);
1877             }
1878             }
1879             else {
1880 156           p_state->buf = newSVpv(s, end - s);
1881 156 100         if (utf8)
1882 8           SvUTF8_on(p_state->buf);
1883             }
1884             }
1885 6543           return;
1886             }