File Coverage

blib/lib/XML/Smart/HTMLParser.pm
Criterion Covered Total %
statement 127 146 86.9
branch 49 62 79.0
condition 17 22 77.2
subroutine 10 13 76.9
pod 0 4 0.0
total 203 247 82.1


line stmt bran cond sub pod time code
1             #############################################################################
2             ## Name: HTMLParser.pm
3             ## Purpose: XML::Smart::HTMLParser
4             ## Author: Graciliano M. P.
5             ## Modified by: Harish Madabushi
6             ## Created: 29/05/2003
7             ## RCS-ID:
8             ## Copyright: (c) 2003 Graciliano M. P.
9             ## Licence: This program is free software; you can redistribute it and/or
10             ## modify it under the same terms as Perl itself
11             #############################################################################
12              
13             package XML::Smart::HTMLParser ;
14              
15 4     4   116 use 5.006 ;
  4         13  
  4         148  
16              
17 4     4   23 use strict ;
  4         9  
  4         131  
18 4     4   20 use warnings ;
  4         6  
  4         145  
19              
20 4     4   128 use XML::Smart::Shared qw( _unset_sig_warn _reset_sig_warn ) ;
  4         9  
  4         9859  
21              
22             our ($VERSION , @ISA) ;
23             $VERSION = '1.12' ;
24              
25             #######
26             # NEW #
27             #######
28              
29             sub new {
30 25     25 0 4669 my $this = shift ;
31 25   33     119 my $class = ref($this) || $this ;
32 25 50       69 return $this if ref $this ;
33              
34 25         168 $this = bless {} => $class ;
35            
36 25         95 _unset_sig_warn() ;
37 25         69 my %args = @_ ;
38 25         102 _reset_sig_warn() ;
39 25         109 $this->setHandlers(%args) ;
40            
41 25         57 $this->{NOENTITY} = 1 ;
42            
43 25         84 return $this ;
44             }
45              
46             ###############
47             # SETHANDLERS #
48             ###############
49              
50             sub setHandlers {
51 47     47 0 67 my $this = shift ;
52 47         115 _unset_sig_warn() ;
53 47         129 my %args = @_;
54 47         120 _reset_sig_warn() ;
55            
56 47   100 3   279 $this->{Init} = $args{Init} || sub{} ;
  3         8  
57 47   100 0   237 $this->{Start} = $args{Start} || sub{} ;
  0         0  
58 47   100 0   205 $this->{Char} = $args{Char} || sub{} ;
  0         0  
59 47   100 0   210 $this->{End} = $args{End} || sub{} ;
  0         0  
60 47   100 3   227 $this->{Final} = $args{Final} || sub{} ;
  3         16  
61            
62 47         115 return( 1 ) ;
63              
64             }
65              
66             #########
67             # PARSE #
68             #########
69              
70             sub parse {
71              
72 25     25 0 54 my $this = shift ;
73 25         40 my $data = shift ;
74            
75 25         51 $data =~ s/\r\n?/\n/gs ;
76            
77 25         94 $data =~ s/^\s*<\?xml.*?>//gsi ;
78            
79 25         36 my @parsed ;
80            
81 25         134 while( $data =~ /(.*?)<(.*?)>/gsi ) {
82              
83 186         310 my $cont = $1 ;
84 186         264 my $markup = $2 ;
85            
86 186         304 my ( $more_q , @args ) = &parse_tag( $markup ) ;
87            
88 186         408 while( $more_q ) {
89 17         22 my $more ;
90 17         75 ( $more ) = ( $data =~ /\G(.*?)>/s ) ;
91 17         52 pos( $data ) += length( $more ) + 1 ;
92 17         42 $markup = $markup.'>'.$more ;
93 17         38 ( $more_q , @args ) = &parse_tag( $markup ) ;
94             }
95            
96 186 100       408 if( $cont =~ /\S/s ) {
97 37         61 push( @parsed , 'Char' , $cont ) ;
98             }
99            
100 186 100       569 if( $args[0] =~ /^\/(.*)/ ) {
    100          
101 72         338 push( @parsed , 'End' , $1 ) ;
102             } elsif( $args[-1] =~ /^\/$/ ) {
103 17         26 pop @args ;
104 17         113 push( @parsed , 'StartEnd' , [@args] ) ;
105             } else {
106 97         670 push( @parsed , 'Start' , [@args] ) ;
107             }
108             }
109            
110             {
111            
112 25         40 my ( %close, @close, %open ) ;
  25         33  
113              
114 25         101 for( my $i = ( $#parsed-1 ); $i >= 0; $i-=2 ) {
115              
116 223         241 my $type = $parsed[$i] ;
117            
118 223 100       499 if( $type eq 'End' ) {
    100          
119 72         104 my $tag = $parsed[ $i+1 ] ;
120 72         159 $close{ lc( $tag ) }++ ;
121 72         183 push( @close , $i ) ;
122             } elsif ( $type eq 'Start' ) {
123              
124 97         102 my $tag = @{ $parsed[$i+1] }[0] ;
  97         178  
125            
126 97 100       202 if( !$close{lc($tag)} ) {
127 25 50 33     32 if( @{$parsed[$i+1]}[-1] eq '/' && $#{$parsed[$i+1]} % 2 ) {
  25 50       122  
  0         0  
128 0         0 pop @{$parsed[$i+1]} ;
  0         0  
129 0         0 $parsed[$i] = 'StartEnd' ;
130             } elsif( $parsed[$i+2] ne 'Char') {
131 25         4505 $parsed[$i] = 'StartEnd' ;
132             } else {
133 0         0 push( @{ $open{$close[-1]} } , 'End' , $tag ) ;
  0         0  
134             }
135             } else {
136 72         100 $close{lc($tag)}-- ;
137 72         201 pop(@close) ;
138             }
139             }
140             }
141            
142 25 50       96 if ( %open ) {
143 0         0 my @parsed2 ;
144 0         0 for( my $i=0 ; $i <= $#parsed ; ++$i ) {
145 0 0       0 push( @parsed2, @{$open{ $i } } ) if $open{ $i } ;
  0         0  
146 0         0 push( @parsed2, $parsed[ $i ] ) ;
147             }
148 0         0 @parsed = @parsed2 ;
149             }
150            
151             }
152            
153 25         64 &{$this->{Init}}($this) ;
  25         124  
154            
155 25         88 for( my $i = 0 ; $i <= $#parsed ; $i+=2 ) {
156 223         322 my $type = $parsed[ $i ] ;
157 223         1426 my $args = $parsed[ $i+1 ] ;
158            
159 223 50       2105 if ($type eq 'Start' ) { &{$this->{Start}}( $this , ref($args) ? @{$args} : $args ) ;}
  72 100       136  
  72 100       219  
  72 100       104  
    50          
160 37 50       75 elsif ($type eq 'Char' ) { &{$this->{Char}}( $this , ref($args) ? @{$args} : $args ) ;}
  37         273  
  0         0  
161 72 50       288 elsif ($type eq 'End' ) { &{$this->{End}}( $this , ref($args) ? @{$args} : $args ) ;}
  72         242  
  0         0  
162             elsif ($type eq 'StartEnd') {
163 42 50       94 &{$this->{Start}}( $this , ref($args) ? @{$args} : $args ) ;
  42         120  
  42         60  
164 42 50       102 &{$this->{End}}( $this , ref($args) ? @{$args}[0] : $args ) ;
  42         115  
  42         70  
165             }
166             }
167            
168 25         49 return &{$this->{Final}}($this) ;
  25         79  
169              
170             }
171              
172             #############
173             # PARSE_TAG #
174             #############
175              
176             sub parse_tag {
177 203     203 0 265 my $args = shift ;
178            
179             #print "[$args]\n" ;
180            
181 203 100       401 if ($args =~ /^!--/s) {
182 18 100       54 if ($args !~ /--$/s) { return('--') ;}
  8         24  
183            
184 10         32 $args =~ s/^!--//s ;
185 10         25 $args =~ s/--$//s ;
186            
187 10         41 return('' , '!--' , 'CONTENT' , $args ) ;
188             }
189            
190            
191 185         181 my @args ;
192 185         234 my ($type , $type_last) = (-1,-1) ;
193            
194 185         799 while($args =~ /(?:^\s*)?(?:
195             (
196             \w+:\/\/[^'"\s]+ ## URI without quotes
197             |
198             [\w:\.-]+ ## words
199             )
200              
201             |
202             ([^'"=\s]+) ## unquoted values
203             |
204             (=) ## equal between name and value
205             |
206             ## Quote: '...'
207             ('
208             (?:
209             '
210             |
211             (?:(?:\\')?[^'])+(?:'{1,2}|.*)
212             )
213             )
214            
215             |
216             ## Quote: "..."
217             ("
218             (?:
219             "
220             |
221             (?:(?:\\")?[^"])+(?:"{1,2}|.*)
222             )
223             )
224              
225             )/gsx) {
226 460         499 my $got ;
227 460         1006 _unset_sig_warn() ;
228 460 100       3595 if ($1 ne '') { $got = $1 ;}
  220 100       312  
    100          
    100          
    50          
229 95         137 elsif ($2 ne '') { $got = $2 ;}
230 83         138 elsif ($3 ne '') { $got = $3 ;}
231 10         24 elsif ($4 ne '') { $got = $4 ;}
232 52         84 elsif ($5 ne '') { $got = $5 ;}
233             else {
234 0         0 _reset_sig_warn() ;
235 0         0 next ;
236             }
237 460         1147 _reset_sig_warn() ;
238            
239 460 100       1597 if ($got =~ /^(['"])/s) {
240 62         106 my $q = $1 ;
241 62 100 66     862 if ($got !~ /$q$/s || $got =~ /\\$q$/s) { return($q) ;}
  9         44  
242 53         313 else { $got =~ s/^$q//s ; $got =~ s/$q$//s ;}
  53         351  
243             }
244 451 100       753 if ($got eq '=') { $type = 1 ;}
  83         559  
245             else {
246 368 100 100     839 if ($type_last == 0 && $type == 0) { push(@args , '') ;}
  3         7  
247 368         542 push(@args , $got) ;
248 368         367 $type_last = $type ;
249 368         1374 $type = 0 ;
250             }
251             }
252            
253             #print "@args\n" ;
254            
255 176         622 return( '' , @args ) ;
256             }
257              
258             #######
259             # END #
260             #######
261              
262             1;
263              
264