Skip to content

Commit

Permalink
grep: implement -Bn and -Cn
Browse files Browse the repository at this point in the history
* For -B, context lines before a matching line are saved in a buffer and emptied when a match occurs
* -C can then be added with no extra logic as a shortcut for -A and -B
* The old meaning of -C goes away, but I'm not aware of any other version of grep that had that special count feature
* To test, I modified the file "ar" so it has two consecutive lines matching "this"

%perl grep  -n -C 5 this ar
452-on the command line to ``select'' archive files for an operation, only
453-the first file with a matching name will be selected.
454-
455-The normal use of ar is for the creation and maintenance of libraries
456-suitable for use with the loader (see ld(1)) although it is not
457:this is good
458:restricted to this purpose.
459-
460-=head2 OPTIONS
461-
462-I<ar> accepts the following options:
463-
--
571-Extract the specified archive members into the files named by the
572-command line arguments.  If no members are specified, all the
573-owner and group will be unchanged.  The file access and modifica-
574-tion times are the time of the extraction (but see the -B<o> op-
575-tion).  The file permissions will be set to those of the file
576:when it was entered into the archive; this will fail if the user
577-is not the owner of the extracted file or the super-user.
578-
579-=back
580-
581-I<ar> exits 0 on success, and >0 if an error occurs.
--
596-=head1 COPYRIGHT and LICENSE
597-
598-This program is copyright by dkulp 1999.
599-
600-This program is free and open software. You may use, copy, modify, distribute
601:and sell this program (and any modified variants) in any way you wish,
602-provided you do not restrict others to do the same.
603-
604-=cut
605-


%perl grep  -n -C 1 this ar
456-suitable for use with the loader (see ld(1)) although it is not
457:this is good
458:restricted to this purpose.
459-
--
575-tion).  The file permissions will be set to those of the file
576:when it was entered into the archive; this will fail if the user
577-is not the owner of the extracted file or the super-user.
--
600-This program is free and open software. You may use, copy, modify, distribute
601:and sell this program (and any modified variants) in any way you wish,
602-provided you do not restrict others to do the same.
--
  • Loading branch information
mknos authored Nov 14, 2024
1 parent 529d42b commit 1ee53ee
Showing 1 changed file with 40 additions and 20 deletions.
60 changes: 40 additions & 20 deletions bin/grep
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use File::Spec;
use File::Temp qw();
use Getopt::Std;

our $VERSION = '1.012';
our $VERSION = '1.013';

$| = 1; # autoflush output

Expand Down Expand Up @@ -88,14 +88,13 @@ sub VERSION_MESSAGE {

sub usage {
die <<EOF;
usage: $Me [-inCcwsxvHhLlFgurpaqT] [-e pattern] [-A NUM] [-m NUM]
[-f pattern-file] [-P sep] [pattern] [file...]
usage: $Me [-incwsxvHhLlFgurpaqT] [-e pattern] [-A NUM] [-B NUM] [-C NUM]
[-m NUM] [-f pattern-file] [-P sep] [pattern] [file...]
Options:
-i case insensitive
-n number lines
-c give count of lines matching
-C ditto, but >1 match per line possible
-w word boundaries only
-q quiet; nothing is written to standard output
-x exact matches only
Expand All @@ -113,6 +112,8 @@ Options:
-r recursive on directories or dot if none
-p paragraph mode (default: line mode)
-P ditto, but specify separator, e.g. -P '%%\\n'
-C show lines of context around each matching line
-B show lines before each matching line
-A show lines after each matching line
-a treat binary files as plain text files
-s suppress errors for failed file and dir opens
Expand Down Expand Up @@ -147,11 +148,6 @@ sub parse_args {
$Matches++ if m/$pattern/;
}
};
my $cls_grep_C = sub {
for my $pattern (@patterns) {
$Matches++ while m/$pattern/g;
}
};
my $cls_grep_v = sub {
for my $pattern (@patterns) {
$Matches += !/$pattern/;
Expand Down Expand Up @@ -249,13 +245,18 @@ sub parse_args {
@ARGV = @tmparg;

$opt{'p'} = $opt{'P'} = ''; # argument to print()
getopts('inCcwsxvHhe:f:LlgurpP:aqTFZm:A:', \%opt) or usage();
getopts('inC:cwsxvHhe:f:LlgurpP:aqTFZm:A:B:', \%opt) or usage();

if (defined $opt{'m'} && $opt{'m'} !~ m/\A[0-9]+\z/) {
die "$Me: invalid max count\n";
}
if (defined $opt{'A'} && $opt{'A'} !~ m/\A[0-9]+\z/) {
die "$Me: bad line count for -A\n";
foreach my $o (qw(A B C)) {
if (defined $opt{$o} && $opt{$o} !~ m/\A[0-9]+\z/) {
die "$Me: bad context count for -$o\n";
}
}
if (defined $opt{'C'}) {
$opt{'A'} = $opt{'B'} = $opt{'C'};
}
$opt{'l'} = 0 if $opt{'L'};
my $no_re = $opt{F} || ( $Me =~ /\bfgrep\b/ );
Expand Down Expand Up @@ -355,13 +356,11 @@ sub parse_args {
$opt{w} && ( @patterns = map { '(?:\b|(?!\w))' . $_ . '(?:\b|(?<!\w))' } @patterns );
$opt{'x'} && ( @patterns = map {"^$_\$"} @patterns );
$opt{'g'} ||= $opt{'u'};
$opt{'c'} ||= $opt{'C'};

foreach (@patterns) {s(/)(\\/)g}

if ($opt{'g'}) { $matcher = $cls_grep_g; }
elsif ($opt{'v'}) { $matcher = $cls_grep_v; }
elsif ($opt{'C'}) { $matcher = $cls_grep_C; }
else { $matcher = $cls_grep; }

return ( \%opt, $matcher );
Expand Down Expand Up @@ -459,8 +458,10 @@ FILE: while ( defined( $file = shift(@_) ) ) {

$total = $Matches = 0;
my $ctx_a = 0;
my @ctx_b;

LINE: while (<$fh>) {
my $ctxb_dump = 0;
if (defined $opt->{'m'}) { # maximum may be zero
last LINE if $total >= $opt->{'m'} && $ctx_a == 0;
}
Expand All @@ -470,12 +471,26 @@ FILE: while ( defined( $file = shift(@_) ) ) {
&{$matcher}(); # do it! (check for matches)
##############

if (!$Matches && defined($opt->{'B'})) {
push @ctx_b, $_;
shift @ctx_b if scalar(@ctx_b) > $opt->{'B'};
}
if ($ctx_a > 0) {
$ctx_a--; # show context line
} elsif (!$Matches) {
next LINE;
}

if ($Matches && @ctx_b) {
$ctxb_dump = 1;
my $n = $. - $opt->{'B'};
foreach my $bline (@ctx_b) {
print($n++, '-') if $opt->{'n'};
print $bline;
}
@ctx_b = ();
}

$total += $Matches;
last FILE if $opt->{'q'}; # single match for all files
last LINE if $opt->{'L'}; # one match is enough
Expand Down Expand Up @@ -515,6 +530,9 @@ FILE: while ( defined( $file = shift(@_) ) ) {
if ($ctx_a == 0 && !$Matches) {
print "--\n";
}
if ($ctxb_dump && $ctx_a == 0) {
print "--\n";
}
}
close $fh;
}
Expand All @@ -540,8 +558,8 @@ grep - search for regular expressions and print
=head1 SYNOPSIS
grep [-incCwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-m NUM]
[-f pattern-file] [-P sep] [pattern] [file ...]
grep [-incwsxvhHlLFigurpaqT] [-e pattern] [-A NUM] [-B NUM] [-C NUM]
[-m NUM] [-f pattern-file] [-P sep] [pattern] [file ...]
=head1 DESCRIPTION
Expand Down Expand Up @@ -575,11 +593,13 @@ Display NUM lines of context after each matching line.
List matching lines from binary files as if they were plain text files.
=item B<-C>
=item B<-B> I<NUM>
Display NUM lines of context before each matching line.
=item B<-C> I<NUM>
Output the count of the matching lines or paragraphs. This is similar
to the B<-c> option (in fact, it implies the B<-c> option), except more
than one match is possible in each line or paragraph.
Display NUM lines of context surrounding each matching line.
=item B<-c>
Expand Down

0 comments on commit 1ee53ee

Please sign in to comment.