File Coverage

Parser.xs
Criterion Covered Total %
statement 184 198 92.9
branch 153 288 53.1
condition n/a
subroutine n/a
pod n/a
total 337 486 69.3


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             #define PERL_NO_GET_CONTEXT /* we want efficiency */
10             #include "EXTERN.h"
11             #include "perl.h"
12             #include "XSUB.h"
13             #include "ppport.h"
14              
15             #define DOWARN (PL_dowarn & G_WARN_ON)
16             #define RETHROW croak(Nullch)
17              
18             /*
19             * Include stuff. We include .c files instead of linking them,
20             * so that they don't have to pollute the external dll name space.
21             */
22              
23             #ifdef EXTERN
24             #undef EXTERN
25             #endif
26              
27             #define EXTERN static /* Don't pollute */
28              
29             #include "hparser.h"
30             #include "util.c"
31             #include "hparser.c"
32              
33              
34             /*
35             * Support functions for the XS glue
36             */
37              
38             static SV*
39 514           check_handler(pTHX_ SV* h)
40             {
41 514 100         SvGETMAGIC(h);
    50          
42 514 100         if (SvROK(h)) {
43 288           SV* myref = SvRV(h);
44 288 100         if (SvTYPE(myref) == SVt_PVCV)
45 162           return newSVsv(h);
46 126 100         if (SvTYPE(myref) == SVt_PVAV)
47 125           return SvREFCNT_inc(myref);
48 1           croak("Only code or array references allowed as handler");
49             }
50 226 100         return SvOK(h) ? newSVsv(h) : 0;
    50          
    50          
51             }
52              
53              
54             static PSTATE*
55 7004           get_pstate_iv(pTHX_ SV* sv)
56             {
57             PSTATE *p;
58 7004 100         MAGIC *mg = SvMAGICAL(sv) ? mg_find(sv, '~') : NULL;
59              
60 7004 100         if (!mg)
61 1           croak("Lost parser state magic");
62 7003           p = (PSTATE *)mg->mg_ptr;
63 7003 50         if (!p)
64 0           croak("Lost parser state magic");
65 7003 50         if (p->signature != P_SIGNATURE)
66 0           croak("Bad signature in parser state object at %p", p);
67 7003           return p;
68             }
69              
70              
71             static PSTATE*
72 7006           get_pstate_hv(pTHX_ SV* sv) /* used by XS typemap */
73             {
74             HV* hv;
75             SV** svp;
76              
77 7006           sv = SvRV(sv);
78 7006 50         if (!sv || SvTYPE(sv) != SVt_PVHV)
    50          
79 0           croak("Not a reference to a hash");
80 7006           hv = (HV*)sv;
81 7006           svp = hv_fetchs(hv, "_hparser_xs_state", 0);
82 7006 100         if (svp) {
83 7005 100         if (SvROK(*svp))
84 7004           return get_pstate_iv(aTHX_ SvRV(*svp));
85             else
86 1           croak("_hparser_xs_state element is not a reference");
87             }
88 1           croak("Can't find '_hparser_xs_state' element in HTML::Parser hash");
89             return 0;
90             }
91              
92              
93             static void
94 129           free_pstate(pTHX_ PSTATE* pstate)
95             {
96             int i;
97 129           SvREFCNT_dec(pstate->buf);
98 129           SvREFCNT_dec(pstate->pend_text);
99 129           SvREFCNT_dec(pstate->skipped_text);
100             #ifdef MARKED_SECTION
101 129           SvREFCNT_dec(pstate->ms_stack);
102             #endif
103 129           SvREFCNT_dec(pstate->bool_attr_val);
104 1290 100         for (i = 0; i < EVENT_COUNT; i++) {
105 1161           SvREFCNT_dec(pstate->handlers[i].cb);
106 1161           SvREFCNT_dec(pstate->handlers[i].argspec);
107             }
108              
109 129           SvREFCNT_dec(pstate->report_tags);
110 129           SvREFCNT_dec(pstate->ignore_tags);
111 129           SvREFCNT_dec(pstate->ignore_elements);
112 129           SvREFCNT_dec(pstate->ignoring_element);
113              
114 129           SvREFCNT_dec(pstate->tmp);
115              
116 129           pstate->signature = 0;
117 129           Safefree(pstate);
118 129           }
119              
120             static int
121 129           magic_free_pstate(pTHX_ SV *sv, MAGIC *mg)
122             {
123 129           free_pstate(aTHX_ (PSTATE *)mg->mg_ptr);
124 129           return 0;
125             }
126              
127             #if defined(USE_ITHREADS)
128              
129             static PSTATE *
130             dup_pstate(pTHX_ PSTATE *pstate, CLONE_PARAMS *params)
131             {
132             PSTATE *pstate2;
133             int i;
134              
135             Newz(56, pstate2, 1, PSTATE);
136             pstate2->signature = pstate->signature;
137              
138             pstate2->buf = SvREFCNT_inc(sv_dup(pstate->buf, params));
139             pstate2->offset = pstate->offset;
140             pstate2->line = pstate->line;
141             pstate2->column = pstate->column;
142             pstate2->start_document = pstate->start_document;
143             pstate2->parsing = pstate->parsing;
144             pstate2->eof = pstate->eof;
145              
146             pstate2->literal_mode = pstate->literal_mode;
147             pstate2->is_cdata = pstate->is_cdata;
148             pstate2->no_dash_dash_comment_end = pstate->no_dash_dash_comment_end;
149             pstate2->pending_end_tag = pstate->pending_end_tag;
150              
151             pstate2->pend_text = SvREFCNT_inc(sv_dup(pstate->pend_text, params));
152             pstate2->pend_text_is_cdata = pstate->pend_text_is_cdata;
153             pstate2->pend_text_offset = pstate->pend_text_offset;
154             pstate2->pend_text_line = pstate->pend_text_offset;
155             pstate2->pend_text_column = pstate->pend_text_column;
156              
157             pstate2->skipped_text = SvREFCNT_inc(sv_dup(pstate->skipped_text, params));
158              
159             #ifdef MARKED_SECTION
160             pstate2->ms = pstate->ms;
161             pstate2->ms_stack =
162             (AV *)SvREFCNT_inc(sv_dup((SV *)pstate->ms_stack, params));
163             pstate2->marked_sections = pstate->marked_sections;
164             #endif
165              
166             pstate2->strict_comment = pstate->strict_comment;
167             pstate2->strict_names = pstate->strict_names;
168             pstate2->strict_end = pstate->strict_end;
169             pstate2->xml_mode = pstate->xml_mode;
170             pstate2->unbroken_text = pstate->unbroken_text;
171             pstate2->attr_encoded = pstate->attr_encoded;
172             pstate2->case_sensitive = pstate->case_sensitive;
173             pstate2->closing_plaintext = pstate->closing_plaintext;
174             pstate2->utf8_mode = pstate->utf8_mode;
175             pstate2->empty_element_tags = pstate->empty_element_tags;
176             pstate2->xml_pic = pstate->xml_pic;
177             pstate2->backquote = pstate->backquote;
178              
179             pstate2->bool_attr_val =
180             SvREFCNT_inc(sv_dup(pstate->bool_attr_val, params));
181             for (i = 0; i < EVENT_COUNT; i++) {
182             pstate2->handlers[i].cb =
183             SvREFCNT_inc(sv_dup(pstate->handlers[i].cb, params));
184             pstate2->handlers[i].argspec =
185             SvREFCNT_inc(sv_dup(pstate->handlers[i].argspec, params));
186             }
187             pstate2->argspec_entity_decode = pstate->argspec_entity_decode;
188              
189             pstate2->report_tags =
190             (HV *)SvREFCNT_inc(sv_dup((SV *)pstate->report_tags, params));
191             pstate2->ignore_tags =
192             (HV *)SvREFCNT_inc(sv_dup((SV *)pstate->ignore_tags, params));
193             pstate2->ignore_elements =
194             (HV *)SvREFCNT_inc(sv_dup((SV *)pstate->ignore_elements, params));
195              
196             pstate2->ignoring_element =
197             SvREFCNT_inc(sv_dup(pstate->ignoring_element, params));
198             pstate2->ignore_depth = pstate->ignore_depth;
199              
200             if (params->flags & CLONEf_JOIN_IN) {
201             pstate2->entity2char =
202             get_hv("HTML::Entities::entity2char", GV_ADD);
203             } else {
204             pstate2->entity2char = (HV *)sv_dup((SV *)pstate->entity2char, params);
205             }
206             pstate2->tmp = SvREFCNT_inc(sv_dup(pstate->tmp, params));
207              
208             return pstate2;
209             }
210              
211             static int
212             magic_dup_pstate(pTHX_ MAGIC *mg, CLONE_PARAMS *params)
213             {
214             mg->mg_ptr = (char *)dup_pstate(aTHX_ (PSTATE *)mg->mg_ptr, params);
215             return 0;
216             }
217              
218             #endif
219              
220             const MGVTBL vtbl_pstate =
221             {
222             0,
223             0,
224             0,
225             0,
226             MEMBER_TO_FPTR(magic_free_pstate),
227             #if defined(USE_ITHREADS)
228             0,
229             MEMBER_TO_FPTR(magic_dup_pstate),
230             #endif
231             };
232              
233              
234             /*
235             * XS interface definition.
236             */
237              
238             MODULE = HTML::Parser PACKAGE = HTML::Parser
239              
240             PROTOTYPES: DISABLE
241              
242             void
243             _alloc_pstate(self)
244             SV* self;
245             PREINIT:
246             PSTATE* pstate;
247             SV* sv;
248             HV* hv;
249             MAGIC* mg;
250              
251             CODE:
252 129           sv = SvRV(self);
253 129 50         if (!sv || SvTYPE(sv) != SVt_PVHV)
    50          
254 0           croak("Not a reference to a hash");
255 129           hv = (HV*)sv;
256              
257 129           Newz(56, pstate, 1, PSTATE);
258 129           pstate->signature = P_SIGNATURE;
259 129           pstate->entity2char = get_hv("HTML::Entities::entity2char", GV_ADD);
260 129           pstate->tmp = NEWSV(0, 20);
261              
262 129           sv = newSViv(PTR2IV(pstate));
263 129           sv_magic(sv, 0, '~', (char *)pstate, 0);
264 129           mg = mg_find(sv, '~');
265             assert(mg);
266 129           mg->mg_virtual = (MGVTBL*)&vtbl_pstate;
267             #if defined(USE_ITHREADS)
268             mg->mg_flags |= MGf_DUP;
269             #endif
270 129           SvREADONLY_on(sv);
271              
272 129           hv_stores(hv, "_hparser_xs_state", newRV_noinc(sv));
273              
274             void
275             parse(self, chunk)
276             SV* self;
277             SV* chunk
278             PREINIT:
279 6167           PSTATE* p_state = get_pstate_hv(aTHX_ self);
280             PPCODE:
281 6167           (void)sv_2mortal(SvREFCNT_inc(SvRV(self)));
282 6167 100         if (p_state->parsing)
283 1           croak("Parse loop not allowed");
284 6166           p_state->parsing = 1;
285 6212 100         if (SvROK(chunk) && SvTYPE(SvRV(chunk)) == SVt_PVCV) {
    50          
286 47           SV* generator = chunk;
287             STRLEN len;
288             do {
289             int count;
290 275 50         PUSHMARK(SP);
291 275           count = call_sv(generator, G_SCALAR|G_EVAL);
292 275           SPAGAIN;
293 275 50         chunk = count ? POPs : 0;
294 275           PUTBACK;
295              
296 275 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          
297 1           p_state->parsing = 0;
298 1           p_state->eof = 0;
299 1           RETHROW;
300             }
301              
302 274 50         if (chunk && SvOK(chunk)) {
    50          
    0          
    0          
303 274 50         (void)SvPV(chunk, len); /* get length */
304             }
305             else {
306 0           len = 0;
307             }
308 274 100         parse(aTHX_ p_state, len ? chunk : 0, self);
309 274           SPAGAIN;
310              
311 274 100         } while (len && !p_state->eof);
    50          
312             }
313             else {
314 6119           parse(aTHX_ p_state, chunk, self);
315 6118           SPAGAIN;
316             }
317 6164           p_state->parsing = 0;
318 6164 100         if (p_state->eof) {
319 9           p_state->eof = 0;
320 9           PUSHs(sv_newmortal());
321             }
322             else {
323 6155           PUSHs(self);
324             }
325              
326             void
327             eof(self)
328             SV* self;
329             PREINIT:
330 162           PSTATE* p_state = get_pstate_hv(aTHX_ self);
331             PPCODE:
332 162 100         if (p_state->parsing)
333 11           p_state->eof = 1;
334             else {
335 151           p_state->parsing = 1;
336 151           parse(aTHX_ p_state, 0, self); /* flush */
337 151           SPAGAIN;
338 151           p_state->parsing = 0;
339             }
340 162           PUSHs(self);
341              
342             SV*
343             strict_comment(pstate,...)
344             PSTATE* pstate
345             ALIAS:
346             HTML::Parser::strict_comment = 1
347             HTML::Parser::strict_names = 2
348             HTML::Parser::xml_mode = 3
349             HTML::Parser::unbroken_text = 4
350             HTML::Parser::marked_sections = 5
351             HTML::Parser::attr_encoded = 6
352             HTML::Parser::case_sensitive = 7
353             HTML::Parser::strict_end = 8
354             HTML::Parser::closing_plaintext = 9
355             HTML::Parser::utf8_mode = 10
356             HTML::Parser::empty_element_tags = 11
357             HTML::Parser::xml_pic = 12
358             HTML::Parser::backquote = 13
359             PREINIT:
360             bool *attr;
361             CODE:
362 106           switch (ix) {
363 31           case 1: attr = &pstate->strict_comment; break;
364 0           case 2: attr = &pstate->strict_names; break;
365 15           case 3: attr = &pstate->xml_mode; break;
366 18           case 4: attr = &pstate->unbroken_text; break;
367             case 5:
368             #ifdef MARKED_SECTION
369 4           attr = &pstate->marked_sections; break;
370             #else
371             croak("marked sections not supported"); break;
372             #endif
373 3           case 6: attr = &pstate->attr_encoded; break;
374 8           case 7: attr = &pstate->case_sensitive; break;
375 0           case 8: attr = &pstate->strict_end; break;
376 2           case 9: attr = &pstate->closing_plaintext; break;
377 19           case 10: attr = &pstate->utf8_mode; break;
378 4           case 11: attr = &pstate->empty_element_tags; break;
379 1           case 12: attr = &pstate->xml_pic; break;
380 1           case 13: attr = &pstate->backquote; break;
381             default:
382 0           croak("Unknown boolean attribute (%d)", (int)ix);
383             }
384 106 100         RETVAL = boolSV(*attr);
385 106 100         if (items > 1)
386 87 50         *attr = SvTRUE(ST(1));
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    100          
    50          
    0          
    100          
    0          
387             OUTPUT:
388             RETVAL
389              
390             SV*
391             boolean_attribute_value(pstate,...)
392             PSTATE* pstate
393             CODE:
394 13           RETVAL = pstate->bool_attr_val ? newSVsv(pstate->bool_attr_val)
395 5 100         : &PL_sv_undef;
396 5 100         if (items > 1) {
397 3           SvREFCNT_dec(pstate->bool_attr_val);
398 3           pstate->bool_attr_val = newSVsv(ST(1));
399             }
400             OUTPUT:
401             RETVAL
402              
403             void
404             ignore_tags(pstate,...)
405             PSTATE* pstate
406             ALIAS:
407             HTML::Parser::report_tags = 1
408             HTML::Parser::ignore_tags = 2
409             HTML::Parser::ignore_elements = 3
410             PREINIT:
411             HV** attr;
412             int i;
413             CODE:
414 37           switch (ix) {
415 15           case 1: attr = &pstate->report_tags; break;
416 11           case 2: attr = &pstate->ignore_tags; break;
417 11           case 3: attr = &pstate->ignore_elements; break;
418             default:
419 0           croak("Unknown tag-list attribute (%d)", (int)ix);
420             }
421 37 50         if (GIMME_V != G_VOID)
    50          
422 0           croak("Can't report tag lists yet");
423              
424 37           items--; /* pstate */
425 37 100         if (items) {
426 34 100         if (*attr)
427 3           hv_clear(*attr);
428             else
429 31           *attr = newHV();
430              
431 75 100         for (i = 0; i < items; i++) {
432 41           SV* sv = ST(i+1);
433 41 100         if (SvROK(sv)) {
434 14           sv = SvRV(sv);
435 14 50         if (SvTYPE(sv) == SVt_PVAV) {
436 14           AV* av = (AV*)sv;
437             STRLEN j;
438 14           STRLEN top = av_top_index(av);
439 121 100         for (j = 0; j <= top; j++) {
440 107           SV**svp = av_fetch(av, j, 0);
441 107 50         if (svp) {
442 107           hv_store_ent(*attr, *svp, newSViv(0), 0);
443             }
444             }
445             }
446             else
447 0           croak("Tag list must be plain scalars and arrays");
448             }
449             else {
450 27           hv_store_ent(*attr, sv, newSViv(0), 0);
451             }
452             }
453             }
454 3 50         else if (*attr) {
455 3           SvREFCNT_dec(*attr);
456 3           *attr = 0;
457             }
458              
459             void
460             handler(pstate, eventname,...)
461             PSTATE* pstate
462             SV* eventname
463             PREINIT:
464             STRLEN name_len;
465 526 100         char *name = SvPV(eventname, name_len);
466 526           int event = -1;
467             int i;
468             struct p_handler *h;
469             PPCODE:
470             /* map event name string to event_id */
471 2271 100         for (i = 0; i < EVENT_COUNT; i++) {
472 2270 100         if (strEQ(name, event_id_str[i])) {
473 525           event = i;
474 525           break;
475             }
476             }
477 526 100         if (event < 0)
478 1           croak("No handler for %s events", name);
479              
480 525           h = &pstate->handlers[event];
481              
482             /* set up return value */
483 525 100         if (h->cb) {
484 64 100         PUSHs((SvTYPE(h->cb) == SVt_PVAV)
485             ? sv_2mortal(newRV_inc(h->cb))
486             : sv_2mortal(newSVsv(h->cb)));
487             }
488             else {
489 461           PUSHs(&PL_sv_undef);
490             }
491              
492             /* update */
493 525 100         if (items > 3) {
494 463           SvREFCNT_dec(h->argspec);
495 463           h->argspec = 0;
496 463           h->argspec = argspec_compile(ST(3), pstate);
497             }
498 519 100         if (items > 2) {
499 514           SvREFCNT_dec(h->cb);
500 514           h->cb = 0;
501 514           h->cb = check_handler(aTHX_ ST(2));
502             }
503              
504              
505             MODULE = HTML::Parser PACKAGE = HTML::Entities
506              
507             void
508             decode_entities(...)
509             PREINIT:
510             int i;
511 65           HV *entity2char = get_hv("HTML::Entities::entity2char", 0);
512             PPCODE:
513 65 50         if (GIMME_V == G_SCALAR && items > 1)
    100          
    50          
514 0           items = 1;
515 130 100         for (i = 0; i < items; i++) {
516 65 50         if (GIMME_V != G_VOID)
    100          
517 36           ST(i) = sv_2mortal(newSVsv(ST(i)));
518             else {
519             #ifdef SV_CHECK_THINKFIRST
520 29 100         SV_CHECK_THINKFIRST(ST(i));
521             #endif
522 29 50         if (SvREADONLY(ST(i)))
523 0           croak("Can't inline decode readonly string in decode_entities()");
524             }
525 65           decode_entities(aTHX_ ST(i), entity2char, 0);
526             }
527 65           SP += items;
528              
529             void
530             _decode_entities(string, entities, ...)
531             SV* string
532             SV* entities
533             PREINIT:
534             HV* entities_hv;
535 14 100         bool expand_prefix = (items > 2) ? SvTRUE(ST(2)) : 0;
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
536             CODE:
537 14 100         if (SvOK(entities)) {
    50          
    50          
538 12 100         if (SvROK(entities) && SvTYPE(SvRV(entities)) == SVt_PVHV) {
    100          
539 10           entities_hv = (HV*)SvRV(entities);
540             }
541             else {
542 2           croak("2nd argument must be hash reference");
543             }
544             }
545             else {
546 2           entities_hv = 0;
547             }
548             #ifdef SV_CHECK_THINKFIRST
549 12 100         SV_CHECK_THINKFIRST(string);
550             #endif
551 11 50         if (SvREADONLY(string))
552 0           croak("Can't inline decode readonly string in _decode_entities()");
553 11           decode_entities(aTHX_ string, entities_hv, expand_prefix);
554              
555             bool
556             _probably_utf8_chunk(string)
557             SV* string
558             PREINIT:
559             STRLEN len;
560             char *s;
561             CODE:
562 8           sv_utf8_downgrade(string, 0);
563 8 50         s = SvPV(string, len);
564 8           RETVAL = probably_utf8_chunk(aTHX_ s, len);
565             OUTPUT:
566             RETVAL
567              
568             int
569             UNICODE_SUPPORT()
570             PROTOTYPE:
571             CODE:
572 3           RETVAL = 1;
573             OUTPUT:
574             RETVAL
575              
576              
577             MODULE = HTML::Parser PACKAGE = HTML::Parser