File Coverage

blib/lib/IO/InnerFile.pm
Criterion Covered Total %
statement 62 108 57.4
branch 20 40 50.0
condition 2 6 33.3
subroutine 11 33 33.3
pod 18 21 85.7
total 113 208 54.3


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