3 # Programname: fpg - Frams' Perl grep
4 # Author: framstag@rus.uni-stuttgart.de
8 # 2003-02-27 Framstag initial version
9 # 2003-02-28 Framstag added exit status
10 # 2007-03-09 Framstag added option -Q
11 # 2007-06-01 Framstag added options -s and -c
12 # and changed default output mode
13 # 2007-06-03 Framstag added ReadLine-support
14 # 2007-08-31 Framstag added option -x
15 # 2008-02-06 Framstag added implicit gunzip
16 # -F ==> -R, new -F option
17 # 2008-10-07 Framstag added option -p
18 # -n ==> -S, new -n option
19 # 2008-10-14 Framstag added option -M
20 # 2008-11-23 Framstag added option -~
28 usage: $0 [options] 'EXP' [file...]
29 or: $0 [options] -Q file...
30 options: -r recursively scan through directories
32 -v print only lines that do NOT match
33 -s verbose scanning/searching
34 -n prefix with line number
35 -l list filenames only
36 -L list filenames only that do NOT match
37 -p show paragraphs, not lines (multiline record separator)
38 -o show only matched strings, not whole lines
39 -M mail-mode: search and show complete mails from mbox files
40 -c print (count) only number of matches (NOT LINES!)
41 -F EXP is a string, not a Perl regular expression
42 -e EXP is any perl code which returns TRUE/FALSE
43 -S \# minimum string length \# for binary files, default: 4
44 -C \# \# lines of context
45 -R 'RS' record separator, default: newline (\\n) if not set -p
46 -x 'exp' extra regexp for highlighting (not used for searching)
47 -X 'exp' exclude files (filename matching this regexp) when searching
48 -~ search in backup files *~ #*#, too
49 -Q query-loop-prompt for search expression (with readline)
50 arguments: EXP is a Perl regular expression
51 file... can be one or more files, even binary or compressed ones
53 #examples: $0 -r 'from.*STDIN' *
54 # $0 -e 'length>30 and not /\\w/' script
55 #See "perldoc perlre" for help on regular expressions.
63 $opt_i = $opt_r = $opt_v = $opt_l = $opt_h = $opt_e = $opt_n = $opt_o = 0;
64 $opt_s = $opt_c = $opt_Q = $opt_F = $opt_p = $opt_M = $opt_C = $opt_S = 0;
70 usage() if !getopts('hirvlLFMopscQen~S:R:C:x:X:') or $opt_h and not @ARGV;
73 $exp = shift or usage();
76 if ($opt_C and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_M)) {
77 die "$0: cannot mix option -C with any of -l -L -s -v -p -M\n";
80 if ($opt_M and ($opt_l or $opt_L or $opt_s or $opt_v or $opt_p or $opt_C)) {
81 die "$0: cannot mix option -M with any of -l -L -s -v -p -C\n";
84 if ($opt_o and ($opt_v or $opt_l or $opt_L or $opt_c or $opt_F or $opt_C)) {
85 die "$0: cannot mix option -E with any of -l -L -v -c -C -F\n";
90 @bfiles = grep(/~$|^#.*#$/,@ARGV);
92 (grep(/[^~]$/,@ARGV) or grep(/(^|\/)#[^\/]*#$/,@ARGV))) {
94 warn "$0: ignoring @bfiles\n"; # unless $opt_r;
105 if ($opt_p) { $/ = '' }
107 #else { eval '$/ = "'.$opt_R.'"' }
109 $opt_h = 1 if not $opt_r and @ARGV < 2;
112 $q = new Term::ReadLine $0;
113 $q->ornaments(0) unless $ENV{PERL_RL};
115 $exp = $q->readline("$B\nsearch-expression:$N ");
128 eval "\$egrep = sub { $exp }";
130 $exp =~ s/([\@\$\%\^\&\*\(\)\+\[\]\{\}\\\|\.\?])/\\$1/g if $opt_F;
131 $exp = '(?i)'.$exp if $opt_i;
132 $exp = '(?s)'.$exp if $opt_p or $opt_R;
133 #? $exp =~ s/\.\*\*/[.\n]*/g;
139 foreach $file (@ARGV) {
140 next if $opt_X and $file =~ /$opt_X/;
141 next if $opt_XX and ($file =~ /~$/ or $file =~ m{(^|/)#[^/]*#$});
142 my $error = ''; open $file,$file or $error = $!; close $file;
144 warn "$0: cannot read file $file - $error\n";
147 unless (-f $file or -d $file or -c $file or -S $file or -p $file) {
148 warn "$0: ignoring special file $file\n";
151 $maxlen = length $file if $maxlen < length $file;
152 # printf "%s\r",substr("scanning $file".(" " x 255),0,$maxlen+9) if -t STDOUT;
153 # print $B."scanning $file\n".$N if -t STDOUT and not $opt_l||$opt_L;
154 if ($opt_r and -d $file) {
155 $found += grepd($file);
158 # next if -z $file; # Achtung: special files unter /proc sind "empty" !
159 # $type = `file -L $file`;
160 # if ($type =~ /text/i and open F,$file or open F,"strings $file|") {
161 $fileq = quotemeta $file;
166 if ($file =~ /\.bz2$/) {
167 open $file,"bunzip2 <$fileq|";
168 # warn "gunzip <$file|\n";
169 } elsif ($file =~ /\.gz$/) {
170 open $file,"gunzip <$fileq|";
171 # warn "gunzip <$file|\n";
173 open $file,"strings -a -n $opt_S $fileq|";
174 # warn "strings -n $opt_S $file|\n";
178 $found += grepf($file,$file);
181 warn "$0: cannot open $file - $!\n";
185 # print " " x ($maxlen+9),"\r" if -t STDOUT;
187 $found = grepf(STDIN);
196 opendir $dir,$dir or return;
197 while (defined($file = readdir $dir)) {
198 next if $file eq '.' or $file eq '..';
199 if (not ${'opt_~'} and $file =~ /~$|^#[^\/]*#$/) {
200 # warn "$0: ignoring $dir/$file\n";
203 $file = "$dir/$file";
204 next unless -r $file;
205 if (-d $file and not -l $file) {
206 $found += grepd($file);
209 next unless -f $file or -c $file or -S $file or -p $file or -z $file;
210 $fileq = quotemeta $file;
211 if (-T $file and open $file,$file or
212 open $file,"strings -a -n $opt_S $fileq|") {
213 $found += grepf($file,$file);
228 warn $B."scanning $file".$N."\n" if -t STDOUT and $opt_s;
231 $_ .= "\n" unless /\n$/;
233 if ($mail and (/^From / or eof $F)) {
245 for (my $i=$opt_C;$i;$i--) {
246 $C{$i} = $C{$i-1} if defined $C{$i-1};
256 print "$l:" if $opt_n;
267 # print ">>>$_" if $opt_i and /$exp/oi or /$exp/o;
276 if ($opt_Q) { $n++ while /$exp/mg }
277 else { $n++ while /$exp/omg }
287 $n += s/($exp)/$B$1$N/mg;
289 $n += s/($exp)/$B$1$N/omg;
296 print "$l:" if $opt_n;
304 # print " " x ($maxlen+9),"\r" if -t STDOUT and $found==1;
306 last if $opt_l or $opt_L;
307 if ($file and not $opt_s) {
308 print "\n$B$file$N:\n";
312 if ($opt_i) { s/($opt_x)/$B$1$N/ogi }
313 else { s/($opt_x)/$B$1$N/og }
315 for (my $i=$opt_C;$i;$i--) {
316 if (defined $C{$i}) {
317 my ($ln,$ls) = @{$C{$i}};
318 unless (defined $L{$ln}) {
320 print "$ln:" if $opt_n;
325 print "$l:" if $opt_n;
332 print "$file:" if @ARGV>1;
335 print "$file\n" if $opt_l and $found or $opt_L and not $found;