File Coverage

lib/IO/InnerFile.pm
Criterion Covered Total %
statement 59 106 55.6
branch 20 40 50.0
condition 2 6 33.3
subroutine 10 32 31.2
pod 18 21 85.7
total 109 205 53.1


line stmt bran cond sub pod time code
1             package IO::InnerFile;
2              
3             =head1 NAME
4              
5             IO::InnerFile - define a file inside another file
6              
7              
8             =head1 SYNOPSIS
9              
10              
11             ### Read a subset of a file:
12             $inner = IO::InnerFile->new($fh, $start, $length);
13             while (<$inner>) {
14             ...
15             }
16              
17              
18             =head1 DESCRIPTION
19              
20             If you have a filehandle that can seek() and tell(), then you
21             can open an IO::InnerFile on a range of the underlying file.
22              
23              
24             =head1 PUBLIC INTERFACE
25              
26             =over
27              
28             =cut
29              
30 1     1   2360 use Symbol;
  1         931  
  1         1512  
31              
32             # The package version, both in 1.23 style *and* usable by MakeMaker:
33             $VERSION = "2.111";
34              
35             #------------------------------
36              
37             =item new FILEHANDLE, [START, [LENGTH]]
38              
39             I
40             Create a new inner-file opened on the given FILEHANDLE,
41             from bytes START to START+LENGTH. Both START and LENGTH
42             default to 0; negative values are silently coerced to zero.
43              
44             Note that FILEHANDLE must be able to seek() and tell(), in addition
45             to whatever other methods you may desire for reading it.
46              
47             =cut
48              
49             sub new {
50 2     2 1 421 my ($class, $fh, $start, $lg) = @_;
51 2 50 33     18 $start = 0 if (!$start or ($start < 0));
52 2 50 33     8 $lg = 0 if (!$lg or ($lg < 0));
53              
54             ### Create the underlying "object":
55 2         6 my $a = {
56             FH => $fh,
57             CRPOS => 0,
58             START => $start,
59             LG => $lg,
60             };
61              
62             ### Create a new filehandle tied to this object:
63 2         3 $fh = gensym;
64 2         22 tie(*$fh, $class, $a);
65 2         3 return bless($fh, $class);
66             }
67              
68             sub TIEHANDLE {
69 2     2   3 my ($class, $data) = @_;
70 2         5 return bless($data, $class);
71             }
72              
73             sub DESTROY {
74 4     4   106 my ($self) = @_;
75 4 50       12 $self->close() if (ref($self) eq 'SCALAR');
76             }
77              
78             #------------------------------
79              
80             =item set_length LENGTH
81              
82             =item get_length
83              
84             =item add_length NBYTES
85              
86             I
87             Get/set the virtual length of the inner file.
88              
89             =cut
90              
91 0     0 1 0 sub set_length { tied(${$_[0]})->{LG} = $_[1]; }
  0         0  
92 0     0 1 0 sub get_length { tied(${$_[0]})->{LG}; }
  0         0  
93 0     0 1 0 sub add_length { tied(${$_[0]})->{LG} += $_[1]; }
  0         0  
94              
95             #------------------------------
96              
97             =item set_start START
98              
99             =item get_start
100              
101             =item add_start NBYTES
102              
103             I
104             Get/set the virtual start position of the inner file.
105              
106             =cut
107              
108 0     0 1 0 sub set_start { tied(${$_[0]})->{START} = $_[1]; }
  0         0  
109 0     0 1 0 sub get_start { tied(${$_[0]})->{START}; }
  0         0  
110 0     0 0 0 sub set_end { tied(${$_[0]})->{LG} = $_[1] - tied(${$_[0]})->{START}; }
  0         0  
  0         0  
111 0     0 0 0 sub get_end { tied(${$_[0]})->{LG} + tied(${$_[0]})->{START}; }
  0         0  
  0         0  
112              
113              
114             #------------------------------
115              
116             =item binmode
117              
118             =item close
119              
120             =item flush
121              
122             =item getc
123              
124             =item getline
125              
126             =item print LIST
127              
128             =item printf LIST
129              
130             =item read BUF, NBYTES
131              
132             =item readline
133              
134             =item seek OFFFSET, WHENCE
135              
136             =item tell
137              
138             =item write ARGS...
139              
140             I
141             Standard filehandle methods.
142              
143             =cut
144              
145 0     0 1 0 sub write { shift->WRITE(@_) }
146 0     0 1 0 sub print { shift->PRINT(@_) }
147 0     0 1 0 sub printf { shift->PRINTF(@_) }
148 0     0 1 0 sub flush { "0 but true"; }
149 0     0 0 0 sub fileno { }
150 0     0 1 0 sub binmode { 1; }
151 0     0 1 0 sub getc { return GETC(tied(${$_[0]}) ); }
  0         0  
152 0     0 1 0 sub read { return READ( tied(${$_[0]}), @_[1,2,3] ); }
  0         0  
153 0     0 1 0 sub readline { return READLINE( tied(${$_[0]}) ); }
  0         0  
154              
155 0     0 1 0 sub getline { return READLINE( tied(${$_[0]}) ); }
  0         0  
156 1     1 1 73 sub close { return CLOSE(tied(${$_[0]}) ); }
  1         4  
157              
158             sub seek {
159 1     1 1 121 my ($self, $ofs, $whence) = @_;
160 1         2 $self = tied( $$self );
161              
162 1 50       4 $self->{CRPOS} = $ofs if ($whence == 0);
163 1 50       2 $self->{CRPOS}+= $ofs if ($whence == 1);
164 1 50       3 $self->{CRPOS} = $self->{LG} + $ofs if ($whence == 2);
165              
166 1 50       2 $self->{CRPOS} = 0 if ($self->{CRPOS} < 0);
167 1 50       4 $self->{CRPOS} = $self->{LG} if ($self->{CRPOS} > $self->{LG});
168 1         1 return 1;
169             }
170              
171             sub tell {
172 0     0 1 0 return tied(${$_[0]})->{CRPOS};
  0         0  
173             }
174              
175             sub WRITE {
176 0     0   0 die "inner files can only open for reading\n";
177             }
178              
179             sub PRINT {
180 0     0   0 die "inner files can only open for reading\n";
181             }
182              
183             sub PRINTF {
184 0     0   0 die "inner files can only open for reading\n";
185             }
186              
187             sub GETC {
188 0     0   0 my ($self) = @_;
189 0 0       0 return 0 if ($self->{CRPOS} >= $self->{LG});
190              
191 0         0 my $data;
192              
193             ### Save and seek...
194 0         0 my $old_pos = $self->{FH}->tell;
195 0         0 $self->{FH}->seek($self->{CRPOS}+$self->{START}, 0);
196              
197             ### ...read...
198 0         0 my $lg = $self->{FH}->read($data, 1);
199 0         0 $self->{CRPOS} += $lg;
200              
201             ### ...and restore:
202 0         0 $self->{FH}->seek($old_pos, 0);
203              
204 0 0       0 $self->{LG} = $self->{CRPOS} unless ($lg);
205 0 0       0 return ($lg ? $data : undef);
206             }
207              
208             sub READ {
209 1     1   2 my ($self, $undefined, $lg, $ofs) = @_;
210 1         1 $undefined = undef;
211              
212 1 50       4 return 0 if ($self->{CRPOS} >= $self->{LG});
213 1 50       4 $lg = $self->{LG} - $self->{CRPOS} if ($self->{CRPOS} + $lg > $self->{LG});
214 1 50       6 return 0 unless ($lg);
215              
216             ### Save and seek...
217 1         3 my $old_pos = $self->{FH}->tell;
218 1         6 $self->{FH}->seek($self->{CRPOS}+$self->{START}, 0);
219              
220             ### ...read...
221 1         11 $lg = $self->{FH}->read($_[1], $lg, $_[3] );
222 1         15 $self->{CRPOS} += $lg;
223              
224             ### ...and restore:
225 1         3 $self->{FH}->seek($old_pos, 0);
226              
227 1 50       4 $self->{LG} = $self->{CRPOS} unless ($lg);
228 1         1 return $lg;
229             }
230              
231             sub READLINE {
232 5     5   157 my ($self) = @_;
233 5 100       13 return $self->_readline_helper() unless wantarray;
234 1         1 my @arr;
235 1         3 while(defined(my $line = $self->_readline_helper())) {
236 2         3 push(@arr, $line);
237             }
238 1         3 return @arr;
239             }
240              
241             sub _readline_helper {
242 7     7   7 my ($self) = @_;
243 7 100       13 return undef if ($self->{CRPOS} >= $self->{LG});
244              
245             # Handle slurp mode (CPAN ticket #72710)
246 5 100       10 if (! defined($/)) {
247 1         1 my $text;
248 1         3 $self->READ($text, $self->{LG} - $self->{CRPOS});
249 1         3 return $text;
250             }
251              
252             ### Save and seek...
253 4         13 my $old_pos = $self->{FH}->tell;
254 4         25 $self->{FH}->seek($self->{CRPOS}+$self->{START}, 0);
255              
256             ### ...read...
257 4         87 my $text = $self->{FH}->getline;
258              
259             ### ...and restore:
260 4         82 $self->{FH}->seek($old_pos, 0);
261              
262             #### If we detected a new EOF ...
263 4 50       16 unless (defined $text) {
264 0         0 $self->{LG} = $self->{CRPOS};
265 0         0 return undef;
266             }
267              
268 4         4 my $lg=length($text);
269              
270 4 50       9 $lg = $self->{LG} - $self->{CRPOS} if ($self->{CRPOS} + $lg > $self->{LG});
271 4         4 $self->{CRPOS} += $lg;
272              
273 4         10 return substr($text, 0,$lg);
274             }
275              
276 1     1   2 sub CLOSE { %{$_[0]}=(); }
  1         9  
277              
278              
279              
280             1;
281             __END__