File Coverage

blib/lib/File/Findgrep.pm
Criterion Covered Total %
statement 9 117 7.6
branch 0 66 0.0
condition 0 15 0.0
subroutine 3 6 50.0
pod 0 1 0.0
total 12 205 5.8


line stmt bran cond sub pod time code
1              
2             package File::Findgrep;
3             require 5.005; # we want qr's !
4             $VERSION = '0.02';
5 2     2   7838 use strict;
  2         4  
  2         147  
6              
7             # POD AT THE END!
8              
9             sub Locale::Maketext::DEBUG () {0}
10             # set to 1 or higher to see trace messages.
11              
12             sub DEBUG () {0}
13              
14 2     2   995 use File::Findgrep::I18N;
  2         5  
  2         52  
15 2     2   9 use vars qw($LH $orig_rs $binary_re);
  2         2  
  2         3271  
16              
17             $LH = File::Findgrep::I18N->get_handle()
18             || die "Can't get a language handle!";
19              
20             #------------------------------------------------------------------------
21             $orig_rs = $/;
22              
23             $binary_re = # file suffixes to ignore:
24             qr<\.(?:
25             gif|png|jpg|jpeg|bmp|wav|snd|ra|ram|au|exe|com|img
26             |pdf|ps|jar|mcp|ico|cur
27             |mid|sit|mp3|hqx|uu|uue|swf|tgz|tar\.gz|zip|z|gz
28             )(?:~.*)?$>xis;
29              
30             sub findgrep {
31 0 0   0 0   @_ = @ARGV unless @_;
32 0 0         _usage($LH->maketext("What options?")) unless @_;
33            
34 0           my($_R, $_m, $_M) = (0,1,10_000_000); # defaults
35             # Lame switch processing...
36 0   0       while(@_ and $_[0] =~ m/^-/s) {
37 0 0         if($_[0] eq '-R') { $_R = 1 }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
38 0           elsif($_[0] =~ m/^-m=?(\d+)/s) { $_m = $1 * 1 }
39 0           elsif($_[0] =~ m/^-m=?(\d+)[Kk]$/s) { $_m = $1 * 1024 }
40 0           elsif($_[0] =~ m/^-m=?(\d+)M$/s) { $_m = $1 * (1024 ** 2) }
41 0           elsif($_[0] =~ m/^-m=?(\d+)G$/s) { $_m = $1 * (1024 ** 3) }
42 0           elsif($_[0] =~ m/^-M=?(\d+)/s) { $_M = $1 * 1 }
43 0           elsif($_[0] =~ m/^-M=?(\d+)[Kk]$/s) { $_M = $1 * 1024 }
44 0           elsif($_[0] =~ m/^-M=?(\d+)M$/s) { $_M = $1 * (1024 ** 2) }
45 0           elsif($_[0] =~ m/^-M=?(\d+)G$/s) { $_M = $1 * (1024 ** 3) }
46             # two bonus switches:
47 0           elsif($_[0] eq '--') { shift @_; last; }
  0            
48 0           elsif($_[0] eq '-h') { _usage() }
49 0           else { _usage($LH->maketext("Unknown switch \"[_1]\"\n", $_[0])) }
50 0           shift @_;
51             }
52            
53 0 0         die $LH->maketext(
54             "Minimum ([_1]) is larger than maximum ([_2])!\n",
55             $_m, $_M
56             ) if $_m > $_M # sanity
57             ;
58            
59 0 0         _usage($LH->maketext("Not enough arguments for findgrep!")) unless @_;
60 0           my($line_pattern, $file_pattern);
61              
62 0           eval { $line_pattern = qr/$_[0]/i };
  0            
63 0 0         $@ and die $LH->maketext("Invalid line-regexp: [_1] -- [_2]",
64             $_[0], $@
65             );
66 0           shift @_;
67            
68 0 0         if(@_) {
69 0           $file_pattern = $_[0];
70 0 0         if($file_pattern =~ m/^[*?]/s) {
71             # forgive things that look like wildcards instead of REs, I guess
72 0           $file_pattern = '^' . $file_pattern . '$';
73 0           $file_pattern =~ s/\*/.*/gs;
74 0           $file_pattern =~ s/\?/./gs;
75             }
76              
77 0           eval { $file_pattern = qr/$file_pattern/i };
  0            
78 0 0         $@ and die $LH->maketext("Invalid file-regexp: [_1] -- [_2]",
79             $_[0], $@
80             );
81 0           shift @_;
82             } else {
83 0           $file_pattern = qr/^[^.~][^~]+$/s;
84             # we can ignore the possibilty of a zero-length filename, I think.
85             }
86            
87 0           my @dirs = @_;
88 0 0         @dirs = ('.') unless @dirs;
89 0           my($lines_matched, $files_matched, $directory_count) = (0,0,0);
90            
91 0           my $recursor;
92             $recursor = sub {
93 0     0     my $dir = $_[0];
94 0 0         $dir .= '/' unless $dir =~ m<[\\/]$>s;
95 0           my @files;
96 0 0         unless(opendir(INDIR, $dir)) {
97 0           warn $LH->maketext("Can't open directory [_1]: [_2]\n", $dir, $!);
98 0           closedir(INDIR);
99 0           return;
100             }
101              
102 0           @files = sort readdir(INDIR);
103 0           DEBUG and print "Items in $dir: <@files>\n";
104 0           ++$directory_count;
105 0           closedir(INDIR);
106 0           print STDERR $LH->maketext("# Searching in directory [_1]\n", $dir);
107              
108 0           my $basename;
109             File:
110 0           foreach my $f (@files) {
111 0 0 0       next File if $f eq '.' or $f eq '..'; # skip scary things
112 0           $basename = $f;
113 0           $f = "$dir$f"; # fully qualify it
114 0           DEBUG > 2 and print "Considering $f\n";
115 0 0 0       if(-l $f) {
    0 0        
    0 0        
116             # skip symlinks
117 0           DEBUG and print "$f is a symlink. Skipping.\n";
118             } elsif(-d _ and $_R) {
119 0           DEBUG and print "$f is a dir. Recursing.\n";
120 0           $recursor->($f); # recurse into the subdir
121             } elsif(
122             -f _ and
123             -s _ >= $_m and -s _ <= $_M
124             ) {
125 0           DEBUG and print "Considering file $f...\n";
126 0 0         if($basename =~ $binary_re) {
    0          
127 0           DEBUG and print "The filename $basename is excluded by binary_re.\n";
128 0           next File;
129             } elsif($basename =~ $file_pattern ) {
130 0           DEBUG > 1 and print "The filename $basename matches $file_pattern\n";
131             } else {
132 0           DEBUG > 1 and print
133             "The filename $basename doesn't match $file_pattern! Skipping\n";
134 0           next File;
135             }
136 0 0         unless(open(IN, "<$f")) {
137 0           close(IN);
138 0           warn $LH->maketext( "Can't open file [_1]: [_2]\n", $f, $! );
139 0           next File;
140             }
141 0           my $chunk = '';
142 0           binmode(IN);
143 0           read(IN, $chunk, 1024);
144              
145 0 0         if($chunk =~ m/[\x00-\x08\x0b\x0e-\x1F]/s) {
    0          
146             # any control codes but tab (09), lf(0a), ff (0c), and cr (0d)
147 0           print STDERR "# ", $LH->maketext(
148             "[_1] looks like a binary file. Skipping.\n", $f
149             );
150 0           close(IN);
151 0           next;
152             } elsif($chunk =~ m<(\cm\cj|\cm|\cj)>s) {
153 0           $/ = $1;
154             } else {
155 0           $/ = $orig_rs;
156             }
157            
158 0           seek(IN,0,0); # rewind
159 0           my $count_this_file;
160 0           while() {
161 0 0         next unless $_ =~ $line_pattern;
162 0           chomp;
163 0           print "$f\:$.\:$_\n";
164 0           ++$lines_matched;
165 0           $count_this_file = 1;
166             }
167 0           close(IN);
168 0 0         ++$files_matched if $count_this_file;
169            
170             } # end of if-it's-a-file
171             } # end of File loop
172 0           return;
173 0           }; #end of closure
174            
175              
176             # Prep for the recursion:
177 0           local $/ = $/; # since the file loop alters $/
178 0           local($_); # since the file loop alters $_
179 0           ++$|;
180 0           { my $oldfh = select(STDERR); ++$|; select($oldfh); }
  0            
  0            
  0            
181 0           DEBUG and print "Dirs: <@dirs>\n";
182              
183             # Actually recurse now:
184 0           foreach my $dir (@dirs) { $recursor->($dir) }
  0            
185 0           undef $recursor; # break self-reference
186            
187 0           print $LH->maketext(
188             "Found [quant,_1,line] in [quant,_2,file], in [quant,_3,directory,directories] scanned.\n",
189             $lines_matched, $files_matched, $directory_count
190             )
191             }
192              
193             #---------------------------------------------------------------------------
194              
195             sub _usage {
196 0     0     die join("\n", @_, $LH->maketext('_USAGE_MESSAGE'));
197             }
198              
199             #------------------------------------------------------------------------
200             findgrep(@ARGV) unless caller; # if executed instead of used, go run!
201             1;
202              
203             __END__