File Coverage

blib/lib/App/Ack/File.pm
Criterion Covered Total %
statement 40 73 54.7
branch 8 36 22.2
condition 3 18 16.6
subroutine 10 13 76.9
pod 8 9 88.8
total 69 149 46.3


line stmt bran cond sub pod time code
1             package App::Ack::File;
2              
3 12     12   30491 use warnings;
  12         85  
  12         379  
4 12     12   63 use strict;
  12         21  
  12         214  
5              
6 12     12   4821 use App::Ack ();
  12         30  
  12         271  
7 12     12   77 use File::Spec ();
  12         22  
  12         9637  
8              
9             =head1 NAME
10              
11             App::Ack::File
12              
13             =head1 DESCRIPTION
14              
15             Abstracts a file from the filesystem.
16              
17             =head1 METHODS
18              
19             =head2 new( $filename )
20              
21             Opens the file specified by I<$filename> and returns a filehandle and
22             a flag that says whether it could be binary.
23              
24             If there's a failure, it throws a warning and returns an empty list.
25              
26             =cut
27              
28             sub new {
29 850     850 1 57606 my $class = shift;
30 850         994 my $filename = shift;
31              
32 850         1657 my $self = bless {
33             filename => $filename,
34             fh => undef,
35             }, $class;
36              
37 850 50       1543 if ( $self->{filename} eq '-' ) {
38 0         0 $self->{fh} = *STDIN;
39             }
40              
41 850         1477 return $self;
42             }
43              
44              
45             =head2 $file->name()
46              
47             Returns the name of the file.
48              
49             =cut
50              
51             sub name {
52 900     900 1 11577 return $_[0]->{filename};
53             }
54              
55              
56             =head2 $file->basename()
57              
58             Returns the basename (the last component the path)
59             of the file.
60              
61             =cut
62              
63             sub basename {
64 170     170 1 241 my ( $self ) = @_;
65              
66 170   66     376 return $self->{basename} //= (File::Spec->splitpath($self->name))[2];
67             }
68              
69              
70             =head2 $file->open()
71              
72             Opens a filehandle for reading this file and returns it, or returns
73             undef if the operation fails (the error is in C<$!>). Instead of calling
74             C, C<$file-Eclose> should be called.
75              
76             =cut
77              
78             sub open {
79 170     170 1 237 my ( $self ) = @_;
80              
81 170 50       335 if ( !$self->{fh} ) {
82 170 50       5558 if ( open $self->{fh}, '<', $self->{filename} ) {
83             # Do nothing.
84             }
85             else {
86 0         0 $self->{fh} = undef;
87             }
88             }
89              
90 170         577 return $self->{fh};
91             }
92              
93              
94             sub may_be_present {
95 0     0 0 0 my $self = shift;
96 0         0 my $regex = shift;
97              
98             # Tells if the file needs a line-by-line scan. This is a big
99             # optimization because if you can tell from the outset that the pattern
100             # is not found in the file at all, then there's no need to do the
101             # line-by-line iteration.
102              
103             # Slurp up an entire file up to 10M, see if there are any matches
104             # in it, and if so, let us know so we can iterate over it directly.
105              
106             # The $regex may be undef if it had a "$" in it, and is therefore unsuitable for this heuristic.
107              
108 0         0 my $may_be_present = 1;
109 0 0 0     0 if ( $regex && $self->open() && -f $self->{fh} ) {
      0        
110 0         0 my $buffer;
111 0         0 my $size = 10_000_000;
112 0         0 my $rc = sysread( $self->{fh}, $buffer, $size );
113 0 0       0 if ( !defined($rc) ) {
114 0 0       0 if ( $App::Ack::report_bad_filenames ) {
115 0         0 App::Ack::warn( $self->name . ": $!" );
116             }
117 0         0 $may_be_present = 0;
118             }
119             else {
120             # If we read all 10M, then we need to scan the rest.
121             # If there are any carriage returns, our results are flaky, so scan the rest.
122 0 0 0     0 if ( ($rc == $size) || (index($buffer,"\r") >= 0) ) {
123 0         0 $may_be_present = 1;
124             }
125             else {
126 0 0       0 if ( $buffer !~ /$regex/o ) {
127 0         0 $may_be_present = 0;
128             }
129             }
130             }
131             }
132              
133 0         0 return $may_be_present;
134             }
135              
136              
137             =head2 $file->reset()
138              
139             Resets the file back to the beginning. This is only called if
140             C is true, but not always if C
141             is true.
142              
143             =cut
144              
145             sub reset {
146 170     170 1 268 my $self = shift;
147              
148 170 50       344 if ( defined($self->{fh}) ) {
149 170 50       1402 return unless -f $self->{fh};
150              
151 170 0 33     1313 if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
152 0         0 App::Ack::warn( "$self->{filename}: $!" );
153             }
154             }
155              
156 170         416 return;
157             }
158              
159              
160             =head2 $file->close()
161              
162             Close the file.
163              
164             =cut
165              
166             sub close {
167 0     0 1 0 my $self = shift;
168              
169 0 0       0 if ( $self->{fh} ) {
170 0 0 0     0 if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
171 0         0 App::Ack::warn( $self->name() . ": $!" );
172             }
173 0         0 $self->{fh} = undef;
174             }
175              
176 0         0 return;
177             }
178              
179              
180             =head2 $file->clone()
181              
182             Clones this file.
183              
184             =cut
185              
186             sub clone {
187 0     0 1 0 my ( $self ) = @_;
188              
189 0         0 return __PACKAGE__->new($self->name);
190             }
191              
192              
193             =head2 $file->firstliney()
194              
195             Returns the first line of a file (or first 250 characters, whichever
196             comes first).
197              
198             =cut
199              
200             sub firstliney {
201 170     170 1 263 my ( $self ) = @_;
202              
203 170 50       327 if ( !exists $self->{firstliney} ) {
204 170         275 my $fh = $self->open();
205 170 50       344 if ( !$fh ) {
206 0 0       0 if ( $App::Ack::report_bad_filenames ) {
207 0         0 App::Ack::warn( $self->name . ': ' . $! );
208             }
209 0         0 $self->{firstliney} = '';
210             }
211             else {
212 170         210 my $buffer;
213 170         1534 my $rc = sysread( $fh, $buffer, 250 );
214 170 50       371 if ( $rc ) {
215 170         848 $buffer =~ s/[\r\n].*//s;
216             }
217             else {
218 0 0       0 if ( !defined($rc) ) {
219 0         0 App::Ack::warn( $self->name . ': ' . $! );
220             }
221 0         0 $buffer = '';
222             }
223 170         449 $self->{firstliney} = $buffer;
224 170         390 $self->reset;
225             }
226             }
227              
228 170         1088 return $self->{firstliney};
229             }
230              
231             1;