File Coverage

blib/lib/POE/Filter/Line.pm
Criterion Covered Total %
statement 91 93 97.8
branch 47 52 90.3
condition 17 21 80.9
subroutine 10 10 100.0
pod 5 5 100.0
total 170 181 93.9


line stmt bran cond sub pod time code
1             package POE::Filter::Line;
2              
3 106     106   208220 use strict;
  106         159  
  106         3324  
4 106     106   37972 use POE::Filter;
  106         276  
  106         3098  
5              
6 106     106   467 use vars qw($VERSION @ISA);
  106         135  
  106         5560  
7             $VERSION = '1.370'; # NOTE - Should be #.### (three decimal places)
8             @ISA = qw(POE::Filter);
9              
10 106     106   434 use Carp qw(carp croak);
  106         137  
  106         13428  
11              
12             sub DEBUG () { 0 }
13              
14             sub FRAMING_BUFFER () { 0 }
15             sub INPUT_REGEXP () { 1 }
16             sub OUTPUT_LITERAL () { 2 }
17             sub AUTODETECT_STATE () { 3 }
18             sub MAX_LENGTH () { 4 }
19             sub MAX_BUFFER () { 5 }
20             sub FIRST_UNUSED () { 6 } # First unused $self offset.
21              
22             sub AUTO_STATE_DONE () { 0x00 }
23             sub AUTO_STATE_FIRST () { 0x01 }
24             sub AUTO_STATE_SECOND () { 0x02 }
25              
26 106     106   429 use base 'Exporter';
  106         151  
  106         117815  
27             our @EXPORT_OK = qw( FIRST_UNUSED );
28              
29             #------------------------------------------------------------------------------
30              
31             sub new {
32 1065     1065 1 32741 my $type = shift;
33              
34 1065 100 100     5373 croak "$type requires an even number of parameters" if @_ and @_ & 1;
35 1064         4421 my %params = @_;
36              
37             croak "$type cannot have both Regexp and Literal line endings" if (
38             defined $params{Regexp} and defined $params{Literal}
39 1064 100 66     2659 );
40              
41 1063         1760 my ($input_regexp, $output_literal);
42 1063         1471 my $autodetect = AUTO_STATE_DONE;
43              
44             # Literal newline for both incoming and outgoing. Every other known
45             # parameter conflicts with this one.
46 1063 100       2119 if (defined $params{Literal}) {
47             croak "A defined Literal must have a nonzero length"
48 707 100 66     3683 unless defined($params{Literal}) and length($params{Literal});
49 706         1585 $input_regexp = quotemeta $params{Literal};
50 706         973 $output_literal = $params{Literal};
51 706 100 100     4887 if (
      100        
52             exists $params{InputLiteral} or # undef means something
53             defined $params{InputRegexp} or
54             defined $params{OutputLiteral}
55             ) {
56 3         352 croak "$type cannot have Literal with any other parameter";
57             }
58             }
59              
60             # Input and output are specified separately, then.
61             else {
62              
63             # Input can be either a literal or a regexp. The regexp may be
64             # compiled or not; we don't rightly care at this point.
65 356 100       795 if (exists $params{InputLiteral}) {
    100          
66 5         6 $input_regexp = $params{InputLiteral};
67              
68             # InputLiteral is defined. Turn it into a regexp and be done.
69             # Otherwise we will autodetect it.
70 5 100 66     13 if (defined($input_regexp) and length($input_regexp)) {
71 2         4 $input_regexp = quotemeta $input_regexp;
72             }
73             else {
74 3         3 $autodetect = AUTO_STATE_FIRST;
75 3         4 $input_regexp = '';
76             }
77              
78             croak "$type cannot have both InputLiteral and InputRegexp"
79 5 100       124 if defined $params{InputRegexp};
80             }
81             elsif (defined $params{InputRegexp}) {
82 2         3 $input_regexp = $params{InputRegexp};
83             croak "$type cannot have both InputLiteral and InputRegexp"
84 2 50       4 if defined $params{InputLiteral};
85             }
86             else {
87 349         488 $input_regexp = "(\\x0D\\x0A?|\\x0A\\x0D?)";
88             }
89              
90 355 100       570 if (defined $params{OutputLiteral}) {
91 6         6 $output_literal = $params{OutputLiteral};
92             }
93             else {
94 349         474 $output_literal = "\x0D\x0A";
95             }
96             }
97              
98 1058         5323 my $max_buffer = $type->__param_max( MaxBuffer => 512*1024*1024, \%params );
99 1056         2317 my $max_length = $type->__param_max( MaxLength => 64*1024*1024, \%params );
100 1054 100       2087 croak "MaxBuffer is not large enough for MaxLength blocks"
101             unless $max_buffer >= $max_length;
102              
103 1053         2115 delete @params{qw(Literal InputLiteral OutputLiteral InputRegexp MaxLength MaxBuffer)};
104 1053 50       2067 carp("$type ignores unknown parameters: ", join(', ', sort keys %params))
105             if scalar keys %params;
106              
107 1053         4446 my $self = bless [
108             '', # FRAMING_BUFFER
109             $input_regexp, # INPUT_REGEXP
110             $output_literal, # OUTPUT_LITERAL
111             $autodetect, # AUTODETECT_STATE
112             $max_length, # MAX_LENGTH
113             $max_buffer # MAX_BUFFER
114             ], $type;
115              
116 1053         1365 DEBUG and warn join ':', @$self;
117              
118 1053         3931 $self;
119             }
120              
121              
122             #------------------------------------------------------------------------------
123             # get() is inherited from POE::Filter.
124              
125             #------------------------------------------------------------------------------
126             # 2001-07-27 RCC: Add get_one_start() and get_one() to correct filter
127             # changing and make input flow control possible.
128              
129             sub get_one_start {
130 466     466 1 2281 my ($self, $stream) = @_;
131              
132 466         618 DEBUG and do {
133             my $temp = join '', @$stream;
134             $temp = unpack 'H*', $temp;
135             warn "got some raw data: $temp\n";
136             };
137              
138 466         3326 $self->[FRAMING_BUFFER] .= join '', @$stream;
139 466 100       1564 die "Framing buffer exceeds the limit"
140             if $self->[MAX_BUFFER] < length( $self->[FRAMING_BUFFER] );
141             }
142              
143             # TODO There is a lot of code duplicated here. What can be done?
144              
145             sub get_one {
146 2808     2808 1 4555 my $self = shift;
147              
148             # Process as many newlines an we can find.
149 2808         3058 LINE: while (1) {
150              
151             # Autodetect is done, or it never started. Parse some buffer!
152 2810 100       4863 unless ($self->[AUTODETECT_STATE]) {
153 2803         2733 DEBUG and warn unpack 'H*', $self->[INPUT_REGEXP];
154             last LINE
155 2803 100       56788 unless $self->[FRAMING_BUFFER] =~ s/^(.*?)$self->[INPUT_REGEXP]//s;
156 2354         3137 DEBUG and warn "got line: <<", unpack('H*', $1), ">>\n";
157 2354         6064 my $line = $1;
158 2354 100       12234 die "Next line exceeds maximum line length"
159             if length( $line ) > $self->[MAX_LENGTH];
160              
161 2353         5204 return [ $line ];
162             }
163              
164             # Waiting for the first line ending. Look for a generic newline.
165 7 100       15 if ($self->[AUTODETECT_STATE] & AUTO_STATE_FIRST) {
166             last LINE
167 3 50       19 unless $self->[FRAMING_BUFFER] =~ s/^(.*?)(\x0D\x0A?|\x0A\x0D?)//;
168              
169 3         9 my $line = $1;
170              
171             # The newline can be complete under two conditions. First: If
172             # it's two characters. Second: If there's more data in the
173             # framing buffer. Loop around in case there are more lines.
174 3 100 66     10 if ( (length($2) == 2) or
175             (length $self->[FRAMING_BUFFER])
176             ) {
177 1         2 DEBUG and warn "detected complete newline after line: <<$1>>\n";
178 1         2 $self->[INPUT_REGEXP] = $2;
179 1         2 $self->[AUTODETECT_STATE] = AUTO_STATE_DONE;
180             }
181              
182             # The regexp has matched a potential partial newline. Save it,
183             # and move to the next state. There is no more data in the
184             # framing buffer, so we're done.
185             else {
186 2         2 DEBUG and warn "detected suspicious newline after line: <<$1>>\n";
187 2         3 $self->[INPUT_REGEXP] = $2;
188 2         3 $self->[AUTODETECT_STATE] = AUTO_STATE_SECOND;
189             }
190 3 50       5 die "Next line exceeds maximum line length"
191             if length( $line ) > $self->[MAX_LENGTH];
192              
193 3         6 return [ $line ];
194             }
195              
196             # Waiting for the second line beginning. Bail out if we don't
197             # have anything in the framing buffer.
198 4 50       6 if ($self->[AUTODETECT_STATE] & AUTO_STATE_SECOND) {
199 4 100       7 return [ ] unless length $self->[FRAMING_BUFFER];
200              
201             # Test the first character to see if it completes the previous
202             # potentially partial newline.
203 2 100       7 if (
    100          
204             substr($self->[FRAMING_BUFFER], 0, 1) eq
205             ( $self->[INPUT_REGEXP] eq "\x0D" ? "\x0A" : "\x0D" )
206             ) {
207              
208             # Combine the first character with the previous newline, and
209             # discard the newline from the buffer. This is two statements
210             # for backward compatibility.
211 1         1 DEBUG and warn "completed newline after line: <<$1>>\n";
212 1         2 $self->[INPUT_REGEXP] .= substr($self->[FRAMING_BUFFER], 0, 1);
213 1         4 substr($self->[FRAMING_BUFFER], 0, 1) = '';
214             }
215 0         0 elsif (DEBUG) {
216             warn "decided prior suspicious newline is okay\n";
217             }
218              
219             # Regardless, whatever is in INPUT_REGEXP is now a complete
220             # newline. End autodetection, post-process the found newline,
221             # and loop to see if there are other lines in the buffer.
222 2         3 $self->[INPUT_REGEXP] = $self->[INPUT_REGEXP];
223 2         2 $self->[AUTODETECT_STATE] = AUTO_STATE_DONE;
224 2         2 next LINE;
225             }
226              
227 0         0 die "consistency error: AUTODETECT_STATE = $self->[AUTODETECT_STATE]";
228             }
229              
230 449         1156 return [ ];
231             }
232              
233             #------------------------------------------------------------------------------
234             # New behavior. First translate system newlines ("\n") into whichever
235             # newlines are supposed to be sent. Second, add a trailing newline if
236             # one doesn't already exist. Since the referenced output list is
237             # supposed to contain one line per element, we also do a split and
238             # join. Bleah. ... why isn't the code doing what the comment says?
239              
240             sub put {
241 328     328 1 4406 my ($self, $lines) = @_;
242              
243 328         448 my @raw;
244 328         630 foreach (@$lines) {
245 352         1606 push @raw, $_ . $self->[OUTPUT_LITERAL];
246             }
247              
248 328         5960 \@raw;
249             }
250              
251             #------------------------------------------------------------------------------
252              
253             sub get_pending {
254 18     18 1 726 my $self = shift;
255 18 100       50 return [ $self->[FRAMING_BUFFER] ] if length $self->[FRAMING_BUFFER];
256 12         23 return undef;
257             }
258              
259             1;
260              
261             __END__