241216.1 · 286e234edd47f95f86a9abc3a640a47b2fd461b3
Add --and and --or options
reading declarations
@@ -1,9 +1,12 @@ History file for ack 3. https://beyondgrep.com/ -NEXT+v3.8.0 ======================================== [FEATURES]+Add --and and --or options to allow combinations of search terms. This is+in addition to the --not that was added in v3.7.0.+ Add support for Starlark and Pytest filetypes. Add support for Terraform. Thanks, Thiago Perrotta. (GH #377)
@@ -55,14 +55,17 @@ our $opt_v; # Flag if we need any context tracking. our $is_tracking_context; -# The regex that we search for in each file.-our $search_re;+# The regex that we use to match each line in the file.+our $re_match;++# Regex for matching for highlighting in matched lines.+our $re_hilite; # The regex that matches for things we want to exclude via the --not option.-our $search_not_re;+our $re_not; -# Special /m version of our $search_re.-our $scan_re;+# Version of the match regex for checking to see if the file should be scanned line-by-line.+our $re_scan; our @special_vars_used_by_opt_output; @@ -206,16 +209,18 @@ MAIN: { } } + # Set up file filters. my $files; if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x $files = App::Ack::Files->from_stdin(); $opt_regex //= shift @ARGV;- ($search_re, $scan_re) = App::Ack::build_regex( $opt_regex, $opt );- $search_not_re = App::Ack::build_search_not_re( $opt );- $stats{search_re} = $search_re;- $stats{scan_re} = $scan_re;- $stats{search_not_re} = $search_not_re;+ defined $opt_regex or App::Ack::die( 'No regular expression found.' );+ ($re_match, $re_not, $re_hilite, $re_scan) = App::Ack::build_all_regexes( $opt_regex, $opt );+ $stats{re_match} = $re_match;+ $stats{re_not} = $re_not;+ $stats{re_hilite} = $re_hilite;+ $stats{re_scan} = $re_scan; } else { if ( $opt_f ) {@@ -223,11 +228,12 @@ MAIN: { } else { $opt_regex //= shift @ARGV;- ($search_re, $scan_re) = App::Ack::build_regex( $opt_regex, $opt );- $search_not_re = App::Ack::build_search_not_re( $opt );- $stats{search_re} = $search_re;- $stats{scan_re} = $scan_re;- $stats{search_not_re} = $search_not_re;+ defined $opt_regex or App::Ack::die( 'No regular expression found.' );+ ($re_match, $re_not, $re_hilite, $re_scan) = App::Ack::build_all_regexes( $opt_regex, $opt );+ $stats{re_match} = $re_match;+ $stats{re_not} = $re_not;+ $stats{re_hilite} = $re_hilite;+ $stats{re_scan} = $re_scan; } my @start; if ( not defined $opt->{files_from} ) {@@ -275,7 +281,7 @@ MAIN: { if ( $opt_debug ) { require List::Util;- my @stats = qw( search_re scan_re search_not_re prescans linescans filematches linematches );+ my @stats = qw( re_match re_scan re_not prescans linescans filematches linematches ); my $width = List::Util::max( map { length } @stats ); for my $stat ( @stats ) {@@ -429,7 +435,7 @@ sub _compile_file_filter { return sub { if ( $opt_g ) {- if ( $File::Next::name =~ /$search_re/o ) {+ if ( $File::Next::name =~ /$re_match/o ) { return 0 if $opt_v; } else {@@ -578,7 +584,7 @@ sub file_loop_normal { my $needs_line_scan = 1; if ( !$opt_passthru && !$opt_v ) { $stats{prescans}++;- if ( $file->may_be_present( $scan_re ) ) {+ if ( $file->may_be_present( $re_scan ) ) { $file->reset(); } else {@@ -655,10 +661,10 @@ sub pmif_context { my $does_match; if ( $in_range ) {- $does_match = /$search_re/o;- if ( $does_match && defined($search_not_re) ) {+ $does_match = /$re_match/o;+ if ( $does_match && defined($re_not) ) { local @-;- $does_match = !/$search_not_re/o;+ $does_match = !/$re_not/o; } if ( $opt_v ) { $does_match = !$does_match;@@ -728,10 +734,10 @@ sub pmif_passthru { $in_range = 1 if ( $using_ranges && !$in_range && defined($opt_range_start) && /$opt_range_start/o ); $match_colno = undef;- my $does_match = /$search_re/o;- if ( $does_match && defined($search_not_re) ) {+ my $does_match = /$re_match/o;+ if ( $does_match && defined($re_not) ) { local @-;- $does_match = !/$search_not_re/o;+ $does_match = !/$re_not/o; } if ( $in_range && $does_match ) { $match_colno = $-[0] + 1;@@ -784,10 +790,10 @@ sub pmif_opt_v { $in_range = 1 if ( $using_ranges && !$in_range && defined($opt_range_start) && /$opt_range_start/o ); if ( $in_range ) {- my $does_match = /$search_re/o;- if ( $does_match && defined($search_not_re) ) {+ my $does_match = /$re_match/o;+ if ( $does_match && defined($re_not) ) { # local @-; No need to localize this because we don't use @-.- $does_match = !/$search_not_re/o;+ $does_match = !/$re_not/o; } if ( !$does_match ) { if ( !$has_printed_from_this_file ) {@@ -834,10 +840,10 @@ sub pmif_normal { if ( $in_range ) { $match_colno = undef;- my $is_match = /$search_re/o;- if ( $is_match && defined($search_not_re) ) {+ my $is_match = /$re_match/o;+ if ( $is_match && defined($re_not) ) { local @-;- $is_match = !/$search_not_re/o;+ $is_match = !/$re_not/o; } if ( $is_match ) { $match_colno = $-[0] + 1;@@ -905,7 +911,7 @@ sub print_line_with_options { } if ( $opt_output ) {- while ( $line =~ /$search_re/og ) {+ while ( $line =~ /$re_match/og ) { my $output = $opt_output; if ( @special_vars_used_by_opt_output ) { no strict;@@ -927,7 +933,7 @@ sub print_line_with_options { # We have to do underlining before any highlighting because highlighting modifies string length. if ( $opt_underline && !$skip_coloring ) {- while ( $line =~ /$search_re/og ) {+ while ( $line =~ /$re_hilite/og ) { my $match_start = $-[0] // next; my $match_end = $+[0]; my $match_length = $match_end - $match_start;@@ -942,7 +948,7 @@ sub print_line_with_options { if ( $opt_color && !$skip_coloring ) { my $highlighted = 0; # If highlighted, need to escape afterwards. - while ( $line =~ /$search_re/og ) {+ while ( $line =~ /$re_hilite/og ) { my $match_start = $-[0] // next; my $match_end = $+[0]; my $match_length = $match_end - $match_start;@@ -1054,7 +1060,7 @@ sub count_matches_in_file { } else { if ( !$opt_v ) {- if ( !$file->may_be_present( $scan_re ) ) {+ if ( !$file->may_be_present( $re_scan ) ) { $do_scan = 0; } }@@ -1071,9 +1077,9 @@ sub count_matches_in_file { chomp; $in_range = 1 if ( !$in_range && defined($opt_range_start) && /$opt_range_start/o ); if ( $in_range ) {- my $is_match = /$search_re/o;- if ( $is_match && defined($search_not_re) ) {- $is_match = !/$search_not_re/o;+ my $is_match = /$re_match/o;+ if ( $is_match && defined($re_not) ) {+ $is_match = !/$re_not/o; } if ( $is_match xor $opt_v ) { ++$nmatches;@@ -1086,9 +1092,9 @@ sub count_matches_in_file { else { while ( <$fh> ) { chomp;- my $is_match = /$search_re/o;- if ( $is_match && defined($search_not_re) ) {- $is_match = !/$search_not_re/o;+ my $is_match = /$re_match/o;+ if ( $is_match && defined($re_not) ) {+ $is_match = !/$re_not/o; } if ( $is_match xor $opt_v ) { ++$nmatches;@@ -1293,6 +1299,24 @@ matches for lines within the range. Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">. +=item B<--and=PATTERN>++Specifies a I<PATTERN> that MUST ALSO be found on a given line for a match to+occur. This option can be repeated.++If you want to find all the lines with both "dogs" or "cats", use:++ ack dogs --and cats++Note that the options that affect "dogs" also affect "cats", so if you have++ ack -i -w dogs --and cats++then the search for both "dogs" and "cats" will be case-insensitive and be+word-limited.++See also C<--or> and C<--not>.+ =item B<-A I<NUM>>, B<--after-context=I<NUM>> Print I<NUM> lines of trailing context after matching lines.@@ -1547,14 +1571,34 @@ if you have ack -i -w dogs --not cats -the the search for both "dogs" and "cats" will be case-insensitive and be+then the search for both "dogs" and "cats" will be case-insensitive and be word-limited. +See also C<--and> and C<--or>.+ =item B<-o> Show only the part of each line matching PATTERN (turns off text highlighting). This is exactly the same as C<--output=$&>. +=item B<--or=PATTERN>++Specifies a I<PATTERN> that MAY be found on a given line for a match to+occur. This option can be repeated.++If you want to find all the lines with "dogs" or "cats", use:++ ack dogs --or cats++Note that the options that affect "dogs" also affect "cats", so if you have++ ack -i -w dogs --or cats++then the search for both "dogs" and "cats" will be case-insensitive and be+word-limited.++See also C<--and> and C<--not>.+ =item B<--output=I<expr>> Output the evaluation of I<expr> for each line (turns off text@@ -1585,7 +1629,7 @@ The number of the line in the file. =item C<$&>, C<$`> and C<$'> -C<$&> is the the string matched by the pattern, C<$`> is what+C<$&> is the string matched by the pattern, C<$`> is what precedes the match, and C<$'> is what follows it. If the pattern is C<gra(ph|nd)> and the string is "lexicographic", then C<$&> is "graph", C<$`> is "lexico" and C<$'> is "ic".@@ -1844,7 +1888,7 @@ an F<.ackrc> file - then you do not have to define your types over and over again. In the following examples the options will always be shown on one command line so that they can be easily copy & pasted. -File types can be specified both with the the I<--type=xxx> option,+File types can be specified both with the I<--type=xxx> option, or the file type as an option itself. For example, if you create a filetype of "cobol", you can specify I<--type=cobol> or simply I<--cobol>. File types must be at least two characters long. This
@@ -15,8 +15,10 @@ sub invalid_combinations { my @output = qw( -o --output ); my @fg = qw( -f -g ); my @case = qw( -i -I --smart-case );+ my @booleans = qw( --and --or --not ); return (+ [qw(--and)] => [qw( --or --not )], [qw(-l)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types)], [qw(-L)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types -c -v)], [@output] => [@context, @file_lists, @output, qw(-c --column --show-types)],@@ -29,7 +31,7 @@ sub invalid_combinations { [qw(--column)] => [@file_lists], [@context] => [@file_lists], [qw(-f)] => [qw(-v), @case],- [qw(-g)] => [qw(--not)],+ [qw(-g)] => [@booleans], [@fg] => [@fg, @pretty, qw(-x -c -u --files-from)], [qw(-p)] => [@context, @file_lists, qw(--passthru -c)], [qw(-v)] => [qw(--column -o --output --passthru)],
@@ -246,7 +246,11 @@ Searching: --range-start PATTERN Specify PATTERN as the start of a match range. --range-end PATTERN Specify PATTERN as the end of a match range. --match PATTERN Specify PATTERN explicitly. Typically omitted.- --not PATTERN Specifies PATTERN that must not be found on+ --and PATTERN Specifies PATTERN that MUST also be found on+ the line for a match to occur. Repeatable.+ --or PATTERN Specifies PATTERN that MAY also be found on+ the line for a match to occur. Repeatable.+ --not PATTERN Specifies PATTERN that must NOT be found on the line for a match to occur. Repeatable. Search output:@@ -741,8 +745,6 @@ sub build_regex { my $str = shift; my $opt = shift; - defined $str or App::Ack::die( 'No regular expression found.' );- # Check for lowercaseness before we do any modifications. my $regex_is_lc = App::Ack::is_lowercase( $str ); @@ -828,23 +830,65 @@ sub build_regex { } -sub build_search_not_re {+sub build_all_regexes {+ my $opt_regex = shift; my $opt = shift; - my @not = @{$opt->{not}};+ my $re_match;+ my $re_not;+ my $re_hilite;+ my $re_scan;++ my @parts; - if ( @not ) {- my @built;- for my $re ( @not ) {- my ($built,undef) = App::Ack::build_regex( $re, $opt );- push( @built, $built );+ # AND: alpha and beta+ if ( @parts = @{$opt->{and}} ) {+ my @match_parts;+ my @hilite_parts;++ for my $part ( $opt_regex, @parts ) {+ my ($match, $scan) = build_regex( $part, $opt );+ push @match_parts, "(?=.*$match)";+ push @hilite_parts, $match;+ $re_scan //= $scan; }- return join( '|', @built );+ $re_match = join( '', @match_parts ); # All required, not optional.+ $re_hilite = join( '|', @hilite_parts ); }+ # OR: alpha OR beta+ elsif ( @parts = @{$opt->{or}} ) {+ my @match_parts;+ my @scan_parts;++ for my $part ( $opt_regex, @parts ) {+ my ($match, $scan) = build_regex( $part, $opt );+ push @match_parts, $match;+ push @scan_parts, $scan;+ } - return;-}+ $re_match = join( '|', @match_parts );+ $re_hilite = $re_match;+ $re_scan = join( '|', @scan_parts );+ }+ # NOT: alpha NOT beta+ elsif ( @parts = @{$opt->{not}} ) {+ ($re_match, $re_scan) = build_regex( $opt_regex, $opt );++ my @not_parts;+ for my $part ( @parts ) {+ (my $re, undef) = build_regex( $part, $opt );+ push @not_parts, $re;+ }+ $re_not = join( '|', @not_parts );+ }+ # No booleans.+ else {+ ($re_match, $re_scan) = build_regex( $opt_regex, $opt );+ $re_hilite = $re_match;+ } + return ($re_match, $re_not, $re_hilite, $re_scan);+} 1; # End of App::Ack
@@ -267,10 +267,13 @@ sub get_arg_spec { return; } + $opt->{and} = [];+ $opt->{or} = []; $opt->{not} = []; return { 1 => sub { $opt->{1} = $opt->{m} = 1 },+ 'and=s' => $opt->{and}, 'A|after-context:-1' => sub { shift; $opt->{A} = _context_value(shift) }, 'B|before-context:-1' => sub { shift; $opt->{B} = _context_value(shift) }, 'C|context:-1' => sub { shift; $opt->{B} = $opt->{A} = _context_value(shift) },@@ -333,6 +336,7 @@ sub get_arg_spec { 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt), 'nopager' => sub { $opt->{pager} = undef }, 'not=s' => $opt->{not},+ 'or=s' => $opt->{or}, 'passthru' => \$opt->{passthru}, 'print0' => \$opt->{print0}, 'p|proximate:1' => \$opt->{p},@@ -928,6 +932,11 @@ sub mutex_options { v => 1, 'with-filename' => 1, },+ and => {+ g => 1,+ not => 1,+ or => 1,+ }, break => { L => 1, c => 1,@@ -1003,6 +1012,7 @@ sub mutex_options { C => 1, H => 1, L => 1,+ and => 1, break => 1, c => 1, column => 1,@@ -1017,6 +1027,7 @@ sub mutex_options { match => 1, not => 1, o => 1,+ or => 1, output => 1, p => 1, passthru => 1,@@ -1084,6 +1095,7 @@ sub mutex_options { l => 1, }, not => {+ and => 1, g => 1, }, o => {@@ -1103,6 +1115,10 @@ sub mutex_options { 'show-types' => 1, v => 1, },+ or => {+ and => 1,+ g => 1,+ }, output => { A => 1, B => 1,
@@ -75,6 +75,8 @@ our @EXPORT = qw( msg subtest_name++ permutate ); my $orig_wd;@@ -1255,4 +1257,22 @@ sub regex_eq { } +# From https://stackoverflow.com/questions/635768/how-can-i-generate-all-permutations-of-an-array-in-perl+sub permutate {+ return [@_] if @_ <= 1;++ return map {+ my ($f, @r) = list_with_x_first($_, @_);+ map [$f, @$_], permutate(@r);+ } 0..$#_;+}+++sub list_with_x_first {+ return if @_ == 1;+ my $i = shift;+ return @_[$i, 0..$i-1, $i+1..$#_];+}++ 1;
t/ack-c.t
@@ -16,6 +16,7 @@ DASH_C: { t/text/bill-of-rights.txt:0 t/text/constitution.txt:0 t/text/gettysburg.txt:1+ t/text/movies.txt:0 t/text/number.txt:0 t/text/numbered-text.txt:0 t/text/ozymandias.txt:0@@ -38,6 +39,7 @@ WITH_DASH_V: { t/text/bill-of-rights.txt:45 t/text/constitution.txt:259 t/text/gettysburg.txt:15+ t/text/movies.txt:25 t/text/number.txt:1 t/text/numbered-text.txt:20 t/text/ozymandias.txt:9
@@ -234,6 +234,7 @@ subtest '-v works on -g' => sub { my @expected = qw( t/text/bill-of-rights.txt t/text/gettysburg.txt+ t/text/movies.txt ); my $file_regex = 'n'; @@ -291,6 +292,7 @@ subtest 'test -g on a path' => sub { t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+ t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt@@ -311,6 +313,7 @@ t/(text)/amontillado.txt t/(text)/bill-of-rights.txt t/(text)/constitution.txt t/(text)/gettysburg.txt+t/(text)/movies.txt t/(text)/number.txt t/(text)/numbered-(text).txt t/(text)/ozymandias.txt@@ -343,6 +346,7 @@ t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt
t/ack-l.t
@@ -21,6 +21,7 @@ my @matching = qw( my @nonmatching = qw( t/text/amontillado.txt t/text/gettysburg.txt+ t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt@@ -59,6 +60,7 @@ DASH_CAPITAL_L: { my @expected = qw( t/text/bill-of-rights.txt t/text/constitution.txt+ t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt@@ -83,6 +85,7 @@ DASH_LV: { t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+ t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt
@@ -15,6 +15,7 @@ G_NO_PRINT0: { t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt+ t/text/movies.txt t/text/ozymandias.txt ); @@ -32,6 +33,7 @@ G_PRINT0: { t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt+ t/text/movies.txt t/text/ozymandias.txt ) ) . "\0"; # string of filenames separated and concluded with null byte
@@ -9,7 +9,7 @@ use Test::More; use lib 't'; use Util; -plan tests => 4;+plan tests => 90; prep_environment(); @@ -83,6 +83,192 @@ HERE } -done_testing();+NOT: {+ _movies_are(+ [qw( Murray )],+ [+ 'Caddyshack',+ 'Ghostbusters',+ 'Groundhog Day',+ 'Little Shop of Horrors',+ 'Stripes',+ ]+ );++ _movies_are(+ [qw( Murray --not Ramis )],+ [+ 'Caddyshack',+ 'Groundhog Day',+ 'Little Shop of Horrors',+ ]+ );+}+++AND: {+ _movies_are(+ [qw( Aykroyd )],+ [+ '1941',+ 'The Blues Brothers',+ 'Dragnet',+ 'Ghostbusters',+ 'Neighbors',+ ]+ );++ _movies_are(+ [qw( Aykroyd --and Belushi )],+ [+ '1941',+ 'The Blues Brothers',+ 'Neighbors',+ ]+ );++ _movies_are(+ [qw( Belushi --and Aykroyd )],+ [+ '1941',+ 'The Blues Brothers',+ 'Neighbors',+ ]+ );++ # Permute through all combinations.+ my @who = qw( Aykroyd Belushi Candy );+ for my $permutation ( permutate( @who ) ) {+ my @args = _argjoin( '--and', $permutation );+ _movies_are(+ [ @args ],+ [+ 'The Blues Brothers',+ ]+ );+ }+} ++OR: {+ _movies_are(+ [qw( Fisher )],+ [+ 'The Blues Brothers',+ 'When Harry Met Sally',+ ]+ );++ _movies_are(+ [qw( Crystal )],+ [+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );++ _movies_are(+ [qw( Crystal --or Fisher )],+ [+ 'The Blues Brothers',+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );++ _movies_are(+ [qw( Fisher --or Crystal )],+ [+ 'The Blues Brothers',+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );++ _movies_are( # Note this is an --and.+ [qw( Fisher --and Crystal )],+ [+ 'When Harry Met Sally',+ ]+ );++ # One of the OR options doesn't exist.+ _movies_are(+ [qw( Crystal --or Hemsworth )],+ [+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );+ _movies_are(+ [qw( Hemsworth --or Crystal )],+ [+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );++ _movies_are(+ [qw( Crystal --or Fisher --or Hemsworth )],+ [+ 'The Blues Brothers',+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );++ # Permute through all combinations.+ my @who = qw( Crystal Fisher Hemsworth Moranis );+ for my $permutation ( permutate( @who ) ) {+ my @args = _argjoin( '--or', $permutation );+ _movies_are(+ [ @args ],+ [+ 'The Blues Brothers',+ 'Ghostbusters',+ 'Little Shop of Horrors',+ 'My Blue Heaven',+ 'Spaceballs',+ 'This is Spinal Tap',+ 'When Harry Met Sally',+ ]+ );+ }+}++++# XXX We need tests on highlighting, too.+#+# XXX And tests on none of them matching.+# XXX And tests that --and themselves out of matching.+done_testing(); exit 0;+++sub _movies_are {+ local $Test::Builder::Level = $Test::Builder::Level + 1;++ my $args = shift;+ my $exp = shift;++ my @base_args = qw( -w -i --noenv );+ my @files = reslash( 't/text/movies.txt' );+ my @got = run_ack( @base_args, @{$args}, @files );++ s/:.+// for @got;++ return is_deeply( \@got, $exp, join( ' ', @{$args} ) );+}+++sub _argjoin {+ my $opt = shift;+ my $args = shift;++ my @args = map { ($_, $opt) } @{$args};++ pop @args;++ return @args;+}
@@ -13,6 +13,7 @@ use App::Ack::ConfigLoader; delete @ENV{qw( PAGER ACK_PAGER ACK_PAGER_COLOR )}; my %defaults = (+ and => [], 'break' => undef, c => undef, color => undef,@@ -31,6 +32,7 @@ my %defaults = ( m => undef, n => undef, not => [],+ or => [], output => undef, p => undef, pager => undef,
@@ -0,0 +1,27 @@+1941: Aykroyd, Belushi+Animal House: Belushi, Matheson, Sutherland+Blazing Saddles: Brooks, Kahn, Korman, Little, Pickens, Wilder+The Blues Brothers: Aykroyd, Belushi, Candy, Fisher+Caddyshack: Chase, Dangerfield, Murray+Dragnet: Aykroyd, Hanks+Elf: Asner, Caan, Deschanel, Ferrell, Newhart, Steenburgen+Ghostbusters: Aykroyd, Murray, Moranis, Ramis+Groundhog Day: Elliott, Murray+Little Shop of Horrors: Guest, Martin, Moranis, Murray+My Blue Heaven: Martin, Moranis+Neighbors: Aykroyd, Belushi+Parenthood: Martin, Steenburgen+Planes, Trains and Automobiles: Candy, Martin, McKean+The Princess Bride: Elwes, Falk, Guest, Patinkin, Shawn+Roxanne: Martin, Willard+Silver Streak: Pryor, Wilder, Willard+Spaceballs: Candy, Moranis, Pullman, Rivers+Stir Crazy: Pryor, Willard+Stripes: Candy, Murray, Ramis+This is Spinal Tap: Crystal, Guest, Hendra, Kirby, McKean, Reiner, Shearer, Willard+Three Amigos: Chase, Martin, Short+Trading Places: Akyroyd, Murphy+Vacation: Candy, Chase, D'Angelo+Waiting For Guffman: Guest, Levy, O'Hara, Willard+When Harry Met Sally: Crystal, Fisher, Kirby, Ryan+Young Frankenstein: Feldman, Garr, Kahn, Leachmann, Wilder
@@ -47,6 +47,7 @@ t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt
@@ -28,6 +28,7 @@ !_TAG_PROGRAM_VERSION 6.1.0 // ADJACENT_CAPTURE_COLORING t/ack-color.t /^ADJACENT_CAPTURE_COLORING: {$/;" l AFTER t/context.t /^AFTER: {$/;" l+AND t/boolean.t /^AND: {$/;" l ARG_MULTIPLE_FILES t/ack-output.t /^ARG_MULTIPLE_FILES: {$/;" l App::Ack lib/App/Ack.pm /^package App::Ack;$/;" p App::Ack::ConfigDefault lib/App/Ack/ConfigDefault.pm /^package App::Ack::ConfigDefault;$/;" p@@ -160,6 +161,7 @@ NOIGNORE_SUBDIR_WINS t/ack-ignore-dir.t /^NOIGNORE_SUBDIR_WINS: {$/;" l NORMAL_CASE t/ack-v.t /^NORMAL_CASE: {$/;" l NORMAL_COLOR t/ack-color.t /^NORMAL_COLOR: {$/;" l NOT t/ack-c.t /^NOT: {$/;" l+NOT t/boolean.t /^NOT: {$/;" l NO_BOOLEANS t/boolean.t /^NO_BOOLEANS: {$/;" l NO_GROUPING t/ack-group.t /^NO_GROUPING: {$/;" l NO_O t/ack-o.t /^NO_O: {$/;" l@@ -172,6 +174,7 @@ NO_SWITCHES_ONE_FILE t/ack-h.t /^NO_SWITCHES_ONE_FILE: {$/;" l NO_SWITCHES_ONE_FILE t/basic.t /^NO_SWITCHES_ONE_FILE: {$/;" l NTESTS t/file-permission.t /^use constant NTESTS => 5;$/;" c NUMERIC_SUBSTITUTIONS t/ack-output.t /^NUMERIC_SUBSTITUTIONS: {$/;" l+OR t/boolean.t /^OR: {$/;" l PAGER t/ack-pager.t /^PAGER: {$/;" l PAGER_ACKRC t/ack-pager.t /^PAGER_ACKRC: {$/;" l PAGER_ACKRC_OVERRIDE t/ack-pager.t /^PAGER_ACKRC_OVERRIDE: {$/;" l@@ -228,6 +231,7 @@ WITH_SWITCHES_MULTIPLE_FILES t/ack-h.t /^WITH_SWITCHES_MULTIPLE_FILES: {$/;" l WITH_SWITCHES_MULTIPLE_FILES t/basic.t /^WITH_SWITCHES_MULTIPLE_FILES: {$/;" l WITH_SWITCHES_ONE_FILE t/ack-h.t /^WITH_SWITCHES_ONE_FILE: {$/;" l WITH_SWITCHES_ONE_FILE t/basic.t /^WITH_SWITCHES_ONE_FILE: {$/;" l+_argjoin t/boolean.t /^sub _argjoin {$/;" s _big_split t/lowercase.t /^sub _big_split {$/;" s _check t/build_regex.t /^sub _check {$/;" s _check_command_for_taintedness t/Util.pm /^sub _check_command_for_taintedness {$/;" s@@ -248,6 +252,7 @@ _generate_error_handler lib/App/Ack/Files.pm /^sub _generate_error_handler {$/;" _generate_ignore_dir lib/App/Ack/ConfigLoader.pm /^sub _generate_ignore_dir {$/;" s _get_code_from_file squash /^sub _get_code_from_file {$/;" s _get_good_and_bad t/ack-w.t /^sub _get_good_and_bad {$/;" s+_movies_are t/boolean.t /^sub _movies_are {$/;" s _my_program lib/App/Ack.pm /^sub _my_program {$/;" s _options_block lib/App/Ack/ConfigDefault.pm /^sub _options_block {$/;" s _options_used lib/App/Ack/ConfigLoader.pm /^sub _options_used {$/;" s@@ -289,8 +294,8 @@ bar t/range/rangefile.pm /^sub bar {$/;" s basename lib/App/Ack/File.pm /^sub basename {$/;" s bisect dev/find-minimum-dep-versions.pl /^sub bisect (&@) {$/;" s build_ack_invocation t/Util.pm /^sub build_ack_invocation {$/;" s+build_all_regexes lib/App/Ack.pm /^sub build_all_regexes {$/;" s build_regex lib/App/Ack.pm /^sub build_regex {$/;" s-build_search_not_re lib/App/Ack.pm /^sub build_search_not_re {$/;" s caret_X t/Util.pm /^sub caret_X {$/;" s cathy lib/App/Ack.pm /^sub cathy {$/;" s check_for_option_in_man_output xt/man.t /^sub check_for_option_in_man_output {$/;" s@@ -382,6 +387,7 @@ is_lowercase lib/App/Ack.pm /^sub is_lowercase {$/;" s is_nonempty_array t/Util.pm /^sub is_nonempty_array {$/;" s is_windows t/Util.pm /^sub is_windows {$/;" s line_split t/Util.pm /^sub line_split {$/;" s+list_with_x_first t/Util.pm /^sub list_with_x_first {$/;" s lists_match t/Util.pm /^sub lists_match {$/;" s main Makefile.PL /^package main;$/;" p make_unreadable t/Util.pm /^sub make_unreadable {$/;" s@@ -413,6 +419,7 @@ option_in_usage t/ack-help.t /^sub option_in_usage {$/;" s options lib/App/Ack/ConfigDefault.pm /^sub options {$/;" s options_clean lib/App/Ack/ConfigDefault.pm /^sub options_clean {$/;" s output_to_pipe lib/App/Ack.pm /^sub output_to_pipe {$/;" s+permutate t/Util.pm /^sub permutate {$/;" s pipe_into_ack t/Util.pm /^sub pipe_into_ack {$/;" s pipe_into_ack_with_stderr t/Util.pm /^sub pipe_into_ack_with_stderr {$/;" s pmif_context ack /^sub pmif_context {$/;" s@@ -430,6 +437,7 @@ process_args lib/App/Ack/ConfigLoader.pm /^sub process_args {$/;" s range_setup ack /^sub range_setup {$/;" s read_file t/Util.pm /^sub read_file {$/;" s read_rcfile lib/App/Ack/ConfigLoader.pm /^sub read_rcfile {$/;" s+regex_eq t/Util.pm /^sub regex_eq {$/;" s register_filter lib/App/Ack/Filter.pm /^sub register_filter {$/;" s remove_defaults_and_globals t/noenv.t /^sub remove_defaults_and_globals {$/;" s reset lib/App/Ack/File.pm /^sub reset {$/;" s