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   776356 use 5.006;
  11         138  
4 11     11   62 use strict;
  11         23  
  11         366  
5 11     11   74 use warnings FATAL => 'all';
  11         24  
  11         514  
6 11     11   5421 use Symbol;
  11         9269  
  11         742  
7 11     11   84 use Carp;
  11         24  
  11         684  
8 11     11   6113 use IO::Handle;
  11         55828  
  11         1029  
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   89 no strict 'refs';
  11         27  
  11         511  
16 11     11   34 *{ 'Acme::IRSRegexp' . "::" } = \*{ __PACKAGE__ . "::" };
  11         2506  
  11         36  
17             }
18              
19             our $VERSION = '0.06';
20              
21             sub TIEHANDLE {
22 16     16   57038 my ($pkg, @opts) = @_;
23 16         121 my $handle;
24 16 100       76 if (@opts % 2) {
25 2         9 $handle = Symbol::gensym;
26             } else {
27 14         28 my $fh = *{shift @opts};
  14         57  
28             # will fail if open for $fh failed, but that's not important
29 14         43 eval { CORE::open $handle, '<&+', $fh };
  14         614  
30             }
31 16         96 my $rs = shift @opts;
32 16         48 my %opts = @opts;
33 16   50     191 $opts{maxrecsize} ||= ($opts{bufsize} || 16384) / 4;
      33        
34 16   33     111 $opts{bufsize} ||= $opts{maxrecsize} * 4;
35 16         150 my $self = bless {
36             %opts,
37             handle => $handle,
38             rs => $rs,
39             records => [],
40             buffer => ''
41             }, $pkg;
42 16         88 $self->_compile_rs;
43 16         81 return $self;
44             }
45              
46             sub open (*;$@) {
47 11     11   114 no strict 'refs'; # or else bareword file handles will break
  11         26  
  11         24217  
48 17     17 1 75863 my (undef,$mode,$expr,@list) = @_;
49 17 50       177 if (!defined $_[0]) {
50 17         69 $_[0] = Symbol::gensym;
51             }
52 17         290 my $glob = $_[0];
53 17 0 33     65 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     127 if ($mode && index($mode,":irs(") >= 0) {
59 7         25 my $irs = "";
60 7         28 my $p0 = index($mode,":irs(");
61 7         19 my $p1 = $p0 + 5;
62 7         17 my $nest = 1;
63 7         31 while ($nest) {
64 63         99 my $c = eval { substr($mode,$p1++,1) };
  63         126  
65 63 50 33     214 if ($@ || !defined($c)) {
66 0         0 carp "Argument list not closed for PerlIO layer \"$irs\"";
67 0         0 return;
68             }
69 63 100       132 if ($c eq "\\") {
70 16         34 $c .= substr($mode,$p1++,1);
71             }
72 63 50       129 if ($c eq "(") { $nest++ }
  0         0  
73 63 100       116 if ($c eq ")") { $nest-- }
  7         18  
74 63 100       130 if ($nest) { $irs .= $c; }
  56         112  
75             }
76 7         36 substr($mode,$p0,length($irs)+6, "");
77 7 50   1   292 my $z = @list ? CORE::open *$glob, $mode, $expr, @list
  1         10  
  1         2  
  1         23  
78             : CORE::open *$glob, $mode, $expr;
79 7         1040 tie *$glob, __PACKAGE__, *$glob, $irs;
80 7         38 return $z;
81             }
82 10 50       43 if (@list) {
    50          
    0          
83 0         0 return CORE::open(*$glob,$mode,$expr,@list);
84             } elsif ($expr) {
85 10         886 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   83 my $self = shift;
95 40         145 my $rs = $self->{rs};
96              
97 40         80 my $q = eval { my @q = split /(?<=${rs})/,""; 1 };
  40         925  
  14         37  
98 40 100       138 if ($q) {
99 14         227 $self->{rsc} = qr/(?<=${rs})/s;
100 14 50       59 if ($rs =~ /\?\^\w*m/) {
101 0         0 $self->{rsc} = qr/(?<=${rs})/ms;
102             }
103 14         48 $self->{can_use_lookbehind} = 1;
104             } else {
105 26         381 $self->{rsc} = qr/(.*?(?:${rs}))/s;
106 26 50       127 if ($rs =~ /\?\^\w*m/) {
107 0         0 $self->{rsc} = qr/(.*?(?:${rs}))/ms;
108             }
109 26         80 $self->{can_use_lookbehind} = 0;
110             }
111 40         119 return;
112             }
113              
114             sub READLINE {
115 5668     5668   863044 my $self = shift;
116 5668 100       14078 if (wantarray) {
117 3         26 local $/ = undef;
118 3         2420 $self->{buffer} .= readline($self->{handle});
119 3         40 push @{$self->{records}}, $self->_split;
  3         33  
120 3         302 $self->{buffer} = "";
121 3         9 my @rec = splice @{$self->{records}};
  3         386  
122 3 50 33     75 if (@rec && $self->{autochomp}) {
123 0         0 $self->chomp( @rec );
124             }
125 3         2082 return @rec;
126             }
127             # want scalar
128 5665 100       8724 if (!@{$self->{records}}) {
  5665         14624  
129 2849         6431 $self->_populate_buffer;
130             }
131 5665         9477 my $rec = shift @{$self->{records}};
  5665         11617  
132 5665 100 100     21311 if (defined($rec) && $self->{autochomp}) {
133 41         120 $self->chomp( $rec );
134             }
135 5665         16193 return $rec;
136             }
137              
138             sub _populate_buffer {
139 2850     2850   4622 my $self = shift;
140 2850         4658 my $handle = $self->{handle};
141 2850 100 66     27096 return if !$handle || eof($handle);
142            
143             # my $rs = $self->{rsc} || $self->{rs};
144 2840         6317 my @rec;
145             {
146 2840         4609 my $buffer = '';
  2842         5220  
147 2842         24829 my $n = read $handle, $buffer, $self->{bufsize};
148 2842         19308 $self->{buffer} .= $buffer;
149 2842         8158 @rec = $self->_split;
150 2842 100 100     30925 redo if !eof($handle) && @rec == 1;
151             }
152 2840         6627 push @{$self->{records}}, @rec;
  2840         60075  
153 2840         6785 $self->{buffer} = '';
154 2840 100       6773 if (eof($handle)) {
155 477         2309 return;
156             }
157              
158 2363 50       4067 if (@{$self->{records}} > 1) {
  2363         6036  
159 2363         3558 $self->{buffer} = pop @{$self->{records}};
  2363         5554  
160             }
161 2363         15799 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   4823 my $self = shift;
174 2845 50       6959 if (!defined $self->{can_use_lookbehind}) {
175 0         0 $self->_compile_rs;
176             }
177 2845         4609 my $rs = $self->{rsc};
178 2845         1978845 my @rec = split $rs, $self->{buffer};
179 2845 100       10835 if ($self->{can_use_lookbehind}) {
180 1         7 return @rec;
181             } else {
182 2844         131990 return grep length, @rec;
183             }
184             }
185              
186             sub CLOSE {
187 8     8   19749 my $self = shift;
188 8         37 $self->_clear_buffer;
189 8         120 my $z = close $self->{handle};
190             # delete $self->{handle};
191 8         54 return $z;
192             }
193              
194             sub _clear_buffer {
195 2778     2778   5119 my $self = shift;
196 2778         5705 $self->{buffer} = '';
197 2778         36675 $self->{records} = [];
198             }
199              
200             sub OPEN {
201 3     3   1113 my ($self, $mode, @args) = @_;
202 3 50       16 if ($self->{handle}) {
203             # close $self->{handle};
204             }
205 3         207 my $z = CORE::open $self->{handle}, $mode, @args;
206 3 50       18 if ($z) {
207 3         23 $self->_clear_buffer;
208             }
209 3         15 return $z;
210             }
211              
212             sub FILENO {
213 1     1   548 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   3511 my ($self, @msg) = @_;
228 13 100       39 if ($self->TELL() != tell($self->{handle})) {
229 1         3 $self->SEEK(0,1);
230             } else {
231 12         28 $self->_clear_buffer;
232             }
233 13         26 print {$self->{handle}} @msg;
  13         65  
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     3 if (@{$self->{records}}==0 && 0 == length($self->{buffer})) {
  1         10  
289 1         5 $self->_populate_buffer;
290             }
291              
292 1 50       2 if (@{$self->{records}}) {
  1 0       4  
293 1         5 my $c = substr( $self->{records}[0], 0, 1, "" );
294 1 50       5 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   301 my $self = shift;
309 1         3 my $handle = $self->{handle};
310 1 50       4 if (@_) {
311 0         0 binmode $handle, @_;
312             } else {
313 1         5 binmode $handle;
314             }
315             }
316              
317             sub SEEK {
318 2755     2755   1540654 my ($self, $pos, $whence) = @_;
319              
320 2755 100       8177 if ($whence == 1) {
321 1         3 $whence = 0;
322 1         3 $pos += $self->TELL;
323             }
324              
325             # easy implementation:
326             # on any seek, clear records, buffer
327              
328 2755         8540 $self->_clear_buffer;
329 2755         29246 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   22489 my $self = shift;
338             # virtual cursor position is actual position on the file handle
339             # minus the length of any buffered data
340 2782         6242 my $tell = tell $self->{handle};
341 2782         5152 $tell -= length($self->{buffer});
342 2782         4236 $tell -= length($_) for @{$self->{records}};
  2782         43816  
343 2782         6971 return $tell;
344             }
345              
346              
347 11     11   116 no warnings 'redefine';
  11         23  
  11         6510  
348             sub IO::Handle::input_record_separator {
349 8     8 0 1042 my $self = shift;
350 8 50 66     39 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
351 8 100       23 if (tied(*$self)) {
352 3 50       21 if (ref(tied(*$self)) eq __PACKAGE__) {
353 3         10 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       14 if (!@_) { return $/ }
  3         16  
362 2         13 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
363 2         8 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 4438 my $self = shift;
373 26 100 100     123 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
374 16 100       50 if (!tied *$self) {
375 4 100       18 if (!@_) {
376 2         7 return IO::Handle::input_record_separator(*$self);
377             }
378 2         13 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
379             } else {
380 12         27 $self = tied *$self;
381             }
382             }
383 24 100       64 if (@_) {
384 10         25 $self->{rs} = shift;
385 10         21 delete $self->{can_use_lookbehind};
386             }
387 24         71 $self->_compile_rs;
388 24         97 return $self->{rs};
389             }
390              
391             sub autochomp {
392 14     14 1 2265 my $self = shift;
393 14 100 100     68 if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
394 7 100       54 if (!tied *$self) {
395 2 100       6 if (!@_) {
396 1         4 return 0;
397             }
398 1         8 $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
399             } else {
400 5         17 $self = tied *$self;
401             }
402             }
403 13   100     44 my $val = $self->{autochomp} || 0;
404 13 100       31 if (@_) {
405 5         14 $self->{autochomp} = 0+!!$_[0];
406             }
407 13         53 return 0+$val;
408             }
409              
410             sub chomp {
411 79     79 1 17014 my $self = shift;
412 79         134 my $removed = 0;
413 79         154 my $rs = $self->{rs};
414 79         163 foreach my $line (@_) {
415 79         856 $line =~ s/($rs)$//;
416 79 100       312 if (defined($1)) {
417 60         171 $removed += length($1);
418             }
419             }
420 79         166 return $removed;
421             }
422              
423             1; #
424              
425             __END__