File Coverage

blib/lib/Acme/InputRecordSeparatorIsRegexp.pm
Criterion Covered Total %
statement 219 291 75.2
branch 71 110 64.5
condition 27 53 50.9
subroutine 30 34 88.2
pod 4 5 80.0
total 351 493 71.2


line stmt bran cond sub pod time code
1             package Acme::InputRecordSeparatorIsRegexp;
2              
3 11     11   636788 use 5.006;
  11         108  
4 11     11   47 use strict;
  11         19  
  11         309  
5 11     11   51 use warnings FATAL => 'all';
  11         17  
  11         406  
6 11     11   4897 use Symbol;
  11         7225  
  11         570  
7 11     11   65 use Carp;
  11         19  
  11         543  
8 11     11   5063 use IO::Handle;
  11         46536  
  11         843  
9             require Exporter;
10             our @ISA = 'Exporter';
11             our @EXPORT_OK = ('open','autochomp','input_record_separator');
12             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
13              
14             BEGIN {
15 11     11   69 no strict 'refs';
  11         22  
  11         373  
16 11     11   27 *{ 'Acme::IRSRegexp' . "::" } = \*{ __PACKAGE__ . "::" };
  11         2082  
  11         27  
17             }
18              
19             our $VERSION = '0.05';
20              
21             sub TIEHANDLE {
22 16     16   47753 my ($pkg, @opts) = @_;
23 16         82 my $handle;
24 16 100       59 if (@opts % 2) {
25 2         5 $handle = Symbol::gensym;
26             } else {
27 14         23 my $fh = *{shift @opts};
  14         43  
28             # will fail if open for $fh failed, but that's not important
29 14         39 eval { CORE::open $handle, '<&+', $fh };
  14         509  
30             }
31 16         73 my $rs = shift @opts;
32 16         44 my %opts = @opts;
33 16   50     174 $opts{maxrecsize} ||= ($opts{bufsize} || 16384) / 4;
      33        
34 16   33     90 $opts{bufsize} ||= $opts{maxrecsize} * 4;
35 16         124 my $self = bless {
36             %opts,
37             handle => $handle,
38             rs => $rs,
39             records => [],
40             buffer => ''
41             }, $pkg;
42 16         69 $self->_compile_rs;
43 16         64 return $self;
44             }
45              
46             sub open (*;$@) {
47 11     11   75 no strict 'refs'; # or else bareword file handles will break
  11         25  
  11         19354  
48 17     17 1 63119 my (undef,$mode,$expr,@list) = @_;
49 17 50       149 if (!defined $_[0]) {
50 17         56 $_[0] = Symbol::gensym;
51             }
52 17         257 my $glob = $_[0];
53 17 0 33     54 if (!ref($glob) && $glob !~ /::/) {
54 0   0     0 $glob = join("::",caller(0) || "", $glob);
55 0         0 print STDERR "new glob is $glob\n";
56             }
57              
58 17 100 66     103 if ($mode && index($mode,":irs(") >= 0) {
59 7         17 my $irs = "";
60 7         18 my $p0 = index($mode,":irs(");
61 7         15 my $p1 = $p0 + 5;
62 7         14 my $nest = 1;
63 7         27 while ($nest) {
64 63         81 my $c = eval { substr($mode,$p1++,1) };
  63         100  
65 63 50 33     184 if ($@ || !defined($c)) {
66 0         0 carp "Argument list not closed for PerlIO layer \"$irs\"";
67 0         0 return;
68             }
69 63 100       104 if ($c eq "\\") {
70 16         26 $c .= substr($mode,$p1++,1);
71             }
72 63 50       103 if ($c eq "(") { $nest++ }
  0         0  
73 63 100       102 if ($c eq ")") { $nest-- }
  7         11  
74 63 100       107 if ($nest) { $irs .= $c; }
  56         102  
75             }
76 7         21 substr($mode,$p0,length($irs)+6, "");
77 7 50   1   264 my $z = @list ? CORE::open *$glob, $mode, $expr, @list
  1         11  
  1         1  
  1         17  
78             : CORE::open *$glob, $mode, $expr;
79 7         936 tie *$glob, __PACKAGE__, *$glob, $irs;
80 7         29 return $z;
81             }
82 10 50       35 if (@list) {
    50          
    0          
83 0         0 return CORE::open(*$glob,$mode,$expr,@list);
84             } elsif ($expr) {
85 10         668 return CORE::open(*$glob,$mode,$expr);
86             } elsif ($mode) {
87 0         0 return CORE::open(*$glob,$mode);
88             } else {
89 0         0 return CORE::open(*$glob);
90             }
91             }
92              
93             sub _compile_rs {
94 40     40   62 my $self = shift;
95 40         123 my $rs = $self->{rs};
96              
97 40         60 my $q = eval { my @q = split /(?<=${rs})/,""; 1 };
  40         739  
  14         32  
98 40 100       108 if ($q) {
99 14         108 $self->{rsc} = qr/(?<=${rs})/s;
100 14 50       43 if ($rs =~ /\?\^\w*m/) {
101 0         0 $self->{rsc} = qr/(?<=${rs})/ms;
102             }
103 14         28 $self->{can_use_lookbehind} = 1;
104             } else {
105 26         301 $self->{rsc} = qr/(.*?(?:${rs}))/s;
106 26 50       107 if ($rs =~ /\?\^\w*m/) {
107 0         0 $self->{rsc} = qr/(.*?(?:${rs}))/ms;
108             }
109 26         63 $self->{can_use_lookbehind} = 0;
110             }
111 40         68 return;
112             }
113              
114             sub READLINE {
115 5668     5668   689999 my $self = shift;
116 5668 100       11501 if (wantarray) {
117 3         13 local $/ = undef;
118 3         1981 $self->{buffer} .= readline($self->{handle});
119 3         11 push @{$self->{records}}, $self->_split;
  3         18  
120 3         233 $self->{buffer} = "";
121 3         8 my @rec = splice @{$self->{records}};
  3         332  
122 3 50 33     60 if (@rec && $self->{autochomp}) {
123 0         0 $self->chomp( @rec );
124             }
125 3         1709 return @rec;
126             }
127             # want scalar
128 5665 100       7355 if (!@{$self->{records}}) {
  5665         12095  
129 2849         4831 $self->_populate_buffer;
130             }
131 5665         8133 my $rec = shift @{$self->{records}};
  5665         10008  
132 5665 100 100     17976 if (defined($rec) && $self->{autochomp}) {
133 41         86 $self->chomp( $rec );
134             }
135 5665         13143 return $rec;
136             }
137              
138             sub _populate_buffer {
139 2850     2850   4062 my $self = shift;
140 2850         3967 my $handle = $self->{handle};
141 2850 100 66     22490 return if !$handle || eof($handle);
142            
143             # my $rs = $self->{rsc} || $self->{rs};
144 2840         5277 my @rec;
145             {
146 2840         3766 my $buffer = '';
  2842         4110  
147 2842         19598 my $n = read $handle, $buffer, $self->{bufsize};
148 2842         16370 $self->{buffer} .= $buffer;
149 2842         6331 @rec = $self->_split;
150 2842 100 100     25124 redo if !eof($handle) && @rec == 1;
151             }
152 2840         5327 push @{$self->{records}}, @rec;
  2840         49707  
153 2840         5624 $self->{buffer} = '';
154 2840 100       5444 if (eof($handle)) {
155 477         1954 return;
156             }
157              
158 2363 50       3119 if (@{$self->{records}} > 1) {
  2363         4978  
159 2363         2998 $self->{buffer} = pop @{$self->{records}};
  2363         4456  
160             }
161 2363         12748 return;
162             }
163              
164             sub EOF {
165 0     0   0 my $self = shift;
166 0         0 foreach my $rec (@{$self->{records}}, $self->{buffer}) {
  0         0  
167 0 0       0 return if length($rec) > 0;
168             }
169 0         0 return eof($self->{handle});
170             }
171              
172             sub _split {
173 2845     2845   3828 my $self = shift;
174 2845 50       5739 if (!defined $self->{can_use_lookbehind}) {
175 0         0 $self->_compile_rs;
176             }
177 2845         3816 my $rs = $self->{rsc};
178 2845         1615771 my @rec = split $rs, $self->{buffer};
179 2845 100       8926 if ($self->{can_use_lookbehind}) {
180 1         5 return @rec;
181             } else {
182 2844         108490 return grep length, @rec;
183             }
184             }
185              
186             sub CLOSE {
187 8     8   16697 my $self = shift;
188 8         28 $self->_clear_buffer;
189 8         101 my $z = close $self->{handle};
190             # delete $self->{handle};
191 8         38 return $z;
192             }
193              
194             sub _clear_buffer {
195 2778     2778   3967 my $self = shift;
196 2778         4606 $self->{buffer} = '';
197 2778         29750 $self->{records} = [];
198             }
199              
200             sub OPEN {
201 3     3   939 my ($self, $mode, @args) = @_;
202 3 50       14 if ($self->{handle}) {
203             # close $self->{handle};
204             }
205 3         171 my $z = CORE::open $self->{handle}, $mode, @args;
206 3 50       19 if ($z) {
207 3         11 $self->_clear_buffer;
208             }
209 3         12 return $z;
210             }
211              
212             sub FILENO {
213 1     1   450 my $self = shift;
214 1         5 return fileno($self->{handle});
215             }
216              
217             sub WRITE {
218 0     0   0 my ($self, $buf, $len, $offset) = @_;
219 0   0     0 $offset ||= 0;
220 0 0       0 if (!defined $len) {
221 0         0 $len = length($buf)-$offset;
222             }
223 0         0 $self->PRINT( substr($buf,$offset,$len) );
224             }
225              
226             sub PRINT {
227 13     13   2907 my ($self, @msg) = @_;
228 13 100       29 if ($self->TELL() != tell($self->{handle})) {
229 1         3 $self->SEEK(0,1);
230             } else {
231 12         23 $self->_clear_buffer;
232             }
233 13         17 print {$self->{handle}} @msg;
  13         56  
234             }
235              
236             sub PRINTF {
237 0     0   0 my ($self, $template, @args) = @_;
238 0         0 $self->PRINT(sprintf($template,@args));
239             }
240              
241             sub READ {
242 0     0   0 my $self = shift;
243 0         0 my $bufref = \$_[0];
244 0         0 my (undef, $len, $offset) = @_;
245 0         0 my $nread = 0;
246              
247 0   0     0 while ($len > 0 && @{$self->{records}}) {
  0         0  
248 0 0       0 if (length($self->{records}[0])>=$len) {
249 0         0 my $rec = shift @{$self->{records}};
  0         0  
250 0         0 my $reclen = length($rec);
251 0         0 substr( $$bufref, $offset, $reclen, $rec);
252 0         0 $len -= $reclen;
253 0         0 $offset += $reclen;
254 0         0 $nread += $reclen;
255             } else {
256 0         0 my $rec = substr($self->{records}[0], 0, $len, "");
257 0         0 substr( $$bufref, $offset, $len, $rec);
258 0         0 $offset += $len;
259 0         0 $nread += $len;
260 0         0 $len = 0;
261             }
262             }
263 0 0 0     0 if ($len > 0 && length($self->{buffer}) > 0) {
264 0         0 my $reclen = length($self->{buffer});
265 0 0       0 if ($reclen >= $len) {
266 0         0 my $rec = substr( $self->{buffer}, 0, $len, "" );
267 0         0 substr( $$bufref, $offset, $len, $rec );
268 0         0 $offset += $len;
269 0         0 $nread += $len;
270 0         0 $len = 0;
271             } else {
272 0         0 substr( $$bufref, $offset, $reclen, $self->{buffer} );
273 0         0 $self->{buffer} = "";
274 0         0 $offset += $reclen;
275 0         0 $nread += $reclen;
276 0         0 $len -= $reclen;
277             }
278             }
279 0 0       0 if ($len > 0) {
280 0         0 return $nread + read $self->{handle}, $$bufref, $len, $offset;
281             } else {
282 0         0 return $nread;
283             }
284             }
285              
286             sub GETC {
287 1     1   3 my $self = shift;
288 1 50 33     2 if (@{$self->{records}}==0 && 0 == length($self->{buffer})) {
  1         9  
289 1         4 $self->_populate_buffer;
290             }
291              
292 1 50       2 if (@{$self->{records}}) {
  1 0       4  
293 1         4 my $c = substr( $self->{records}[0], 0, 1, "" );
294 1 50       4 if (0 == length($self->{records}[0])) {
295 0         0 shift @{$self->{records}};
  0         0  
296             }
297 1         12 return $c;
298             } elsif (0 != length($self->{buffer})) {
299 0         0 my $c = substr( $self->{buffer}, 0, 1, "" );
300 0         0 return $c;
301             } else {
302             # eof?
303 0         0 return undef;
304             }
305             }
306              
307             sub BINMODE {
308 1     1   246 my $self = shift;
309 1         3 my $handle = $self->{handle};
310 1 50       3 if (@_) {
311 0         0 binmode $handle, @_;
312             } else {
313 1         4 binmode $handle;
314             }
315             }
316              
317             sub SEEK {
318 2755     2755   1251460 my ($self, $pos, $whence) = @_;
319              
320 2755 100       6791 if ($whence == 1) {
321 1         2 $whence = 0;
322 1         3 $pos += $self->TELL;
323             }
324              
325             # easy implementation:
326             # on any seek, clear records, buffer
327              
328 2755         6672 $self->_clear_buffer;
329 2755         24424 seek $self->{handle}, $pos, $whence;
330              
331             # more sophisticated implementation
332             # on a seek forward, remove bytes from the front
333             # of buffered data
334             }
335              
336             sub TELL {
337 2782     2782   18427 my $self = shift;
338             # virtual cursor position is actual position on the file handle
339             # minus the length of any buffered data
340 2782         5201 my $tell = tell $self->{handle};
341 2782         4309 $tell -= length($self->{buffer});
342 2782         3482 $tell -= length($_) for @{$self->{records}};
  2782         36359  
343 2782         5520 return $tell;
344             }
345              
346              
347 11     11   88 no warnings 'redefine';
  11         22  
  11         5599  
348             sub IO::Handle::input_record_separator {
349 8     8 0 771 my $self = shift;
350 8 50 66     31 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
351 8 100       17 if (tied(*$self)) {
352 3 50       9 if (ref(tied(*$self)) eq __PACKAGE__) {
353 3         8 return input_record_separator($self,@_);
354             }
355 0         0 my $z = eval { (tied *$self)->input_record_separator(@_) };
  0         0  
356 0 0       0 if ($@) {
357 0         0 carp "input_record_separator is not supported on tied handle";
358             }
359 0         0 return $z;
360             }
361 5 100       12 if (!@_) { return $/ }
  3         12  
362 2         11 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
363 2         5 return input_record_separator($self,@_);
364             } else {
365 0         0 carp "input to input_record_separator was not a handle";
366 0         0 return;
367             }
368             }
369              
370              
371             sub input_record_separator {
372 26     26 1 3569 my $self = shift;
373 26 100 100     106 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
374 16 100       43 if (!tied *$self) {
375 4 100       12 if (!@_) {
376 2         5 return IO::Handle::input_record_separator(*$self);
377             }
378 2         14 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
379             } else {
380 12         20 $self = tied *$self;
381             }
382             }
383 24 100       49 if (@_) {
384 10         22 $self->{rs} = shift;
385 10         14 delete $self->{can_use_lookbehind};
386             }
387 24         58 $self->_compile_rs;
388 24         54 return $self->{rs};
389             }
390              
391             sub autochomp {
392 14     14 1 1783 my $self = shift;
393 14 100 100     56 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
394 7 100       21 if (!tied *$self) {
395 2 100       7 if (!@_) {
396 1         2 return 0;
397             }
398 1         6 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
399             } else {
400 5         9 $self = tied *$self;
401             }
402             }
403 13   100     36 my $val = $self->{autochomp} || 0;
404 13 100       28 if (@_) {
405 5         11 $self->{autochomp} = 0+!!$_[0];
406             }
407 13         32 return 0+$val;
408             }
409              
410             sub chomp {
411 79     79 1 13382 my $self = shift;
412 79         101 my $removed = 0;
413 79         125 my $rs = $self->{rs};
414 79         130 foreach my $line (@_) {
415 79         737 $line =~ s/($rs)$//;
416 79 100       236 if (defined($1)) {
417 60         138 $removed += length($1);
418             }
419             }
420 79         142 return $removed;
421             }
422              
423             1; #
424              
425             __END__