# Please DO NOT EDIT or send patches for it.
#
# Please take a look at the source from
-# https://github.com/petdance/ack2
+# https://github.com/beyondgrep/ack2
# and submit patches against the individual files
# that build ack.
#
use strict;
use warnings;
-
-our $VERSION = '2.15_01'; # Check http://beyondgrep.com/ for updates
+our $VERSION = '2.22'; # Check https://beyondgrep.com/ for updates
use 5.008008;
use Getopt::Long 2.38 ();
# XXX Don't make this so brute force
-# See also: https://github.com/petdance/ack2/issues/89
+# See also: https://github.com/beyondgrep/ack2/issues/89
our $opt_after_context;
our $opt_before_context;
our $opt_l;
our $opt_passthru;
our $opt_column;
-# flag if we need any context tracking
+
+# Flag if we need any context tracking.
our $is_tracking_context;
# These are all our globals.
last if ( $arg eq '--' );
# Get the --thpppt, --bar, --cathy checking out of the way.
- $arg =~ /^--th[pt]+t+$/ and App::Ack::_thpppt($arg);
- $arg eq '--bar' and App::Ack::_bar();
- $arg eq '--cathy' and App::Ack::_cathy();
+ $arg =~ /^--th[pt]+t+$/ and App::Ack::thpppt($arg);
+ $arg eq '--bar' and App::Ack::ackbar();
+ $arg eq '--cathy' and App::Ack::cathy();
# See if we want to ignore the environment. (Don't tell Al Gore.)
$arg eq '--env' and $env_is_usable = 1;
}
}
- # if we have one or more --noignore-dir directives, we can't ignore
+ # If we have one or more --noignore-dir directives, we can't ignore
# entire subdirectory hierarchies, so we return an "accept all"
- # filter and scrutinize the files more in _compile_file_filter
+ # filter and scrutinize the files more in _compile_file_filter.
return if $dont_ignore_dirs;
return unless $idirs;
$idirs = $opt->{idirs};
return sub {
- my $resource = App::Ack::Resource::Basic->new($File::Next::dir);
+ my $resource = App::Ack::Resource->new($File::Next::dir);
return !grep { $_->filter($resource) } @{$idirs};
};
}
my @ignore_dir_filter = @{$opt->{idirs} || []};
my @is_inverted = map { $_->is_inverted() } @ignore_dir_filter;
- # this depends on InverseFilter->invert returning the original
- # filter (for optimization)
+ # This depends on InverseFilter->invert returning the original filter (for optimization).
@ignore_dir_filter = map { $_->is_inverted() ? $_->invert() : $_ } @ignore_dir_filter;
my $dont_ignore_dir_filter = grep { $_ } @is_inverted;
my $previous_dir = '';
return sub {
if ( $opt_g ) {
if ( $File::Next::name =~ /$opt_regex/ && $opt_v ) {
- return;
+ return 0;
}
if ( $File::Next::name !~ /$opt_regex/ && !$opt_v ) {
- return;
+ return 0;
}
}
# ack always selects files that are specified on the command
my $is_ignoring = 0;
for ( my $i = 0; $i < @dirs; $i++) {
- my $dir_rsrc = App::Ack::Resource::Basic->new(File::Spec->catfile(@dirs[0 .. $i]));
+ my $dir_rsrc = App::Ack::Resource->new(File::Spec->catfile(@dirs[0 .. $i]));
my $j = 0;
for my $filter (@ignore_dir_filter) {
}
}
- my $resource = App::Ack::Resource::Basic->new($File::Next::name);
+ my $resource = App::Ack::Resource->new($File::Next::name);
if ( $ifiles_filters && $ifiles_filters->filter($resource) ) {
return 0;
my $filters = $App::Ack::mappings{$k};
foreach my $filter (@{$filters}) {
- # clone the resource
+ # Clone the resource.
my $clone = $resource->clone;
if ( $filter->filter($clone) ) {
push @matches, $k;
return File::Next::reslash( $filename );
}
else {
- # XXX is this the best method? it always hits the FS
+ # XXX Is this the best method? It always hits the FS.
if( my ( $dev, $inode ) = (stat($filename))[0, 1] ) {
return join(':', $dev, $inode);
}
else {
- # XXX this could be better
+ # XXX This could be better.
return $filename;
}
}
{
-# number of context lines
+# Number of context lines
my $n_before_ctx_lines;
my $n_after_ctx_lines;
-# array to keep track of lines that might be required for a "before" context
+# Array to keep track of lines that might be required for a "before" context
my @before_context_buf;
-# position to insert next line in @before_context_buf
+# Position to insert next line in @before_context_buf
my $before_context_pos;
-# number of "after" context lines still pending
+# Number of "after" context lines still pending
my $after_context_pending;
-# number of latest line that got printed
+# Number of latest line that got printed
my $printed_line_no;
my $is_iterating;
$has_printed_something = 0;
}
-# setup context tracking variables
-sub setup_line_context {
-
+# Set up context tracking variables.
+sub set_up_line_context {
$n_before_ctx_lines = $opt_output ? 0 : ($opt_before_context || 0);
$n_after_ctx_lines = $opt_output ? 0 : ($opt_after_context || 0);
return;
}
-# adjust context tracking variables when entering a new file
-sub setup_line_context_for_file {
-
+# Adjust context tracking variables when entering a new file.
+sub set_up_line_context_for_file {
$printed_line_no = 0;
$after_context_pending = 0;
if ( $opt_heading && !$opt_lines ) {
return;
}
-=for Developers
+=begin Developers
This subroutine jumps through a number of optimization hoops to
try to be fast in the more common use cases of ack. For one thing,
does_match for performance reasons; any relevant changes that happen here
must also happen there.
+=end Developers
+
=cut
sub print_matches_in_resource {
- my ( $resource, $opt ) = @_;
+ my ( $resource ) = @_;
my $max_count = $opt_m || -1;
my $nmatches = 0;
$display_filename = Term::ANSIColor::colored($display_filename, $ENV{ACK_COLOR_FILENAME});
}
- # check for context before the main loop, so we don't
- # pay for it if we don't need it
+ # Check for context before the main loop, so we don't pay for it if we don't need it.
if ( $is_tracking_context ) {
$after_context_pending = 0;
while ( <$fh> ) {
- if ( does_match($opt, $_) && $max_count ) {
+ if ( does_match( $_ ) && $max_count ) {
if ( !$has_printed_for_this_resource ) {
if ( $opt_break && $has_printed_something ) {
App::Ack::print_blank_line();
App::Ack::print_filename( $display_filename, $ors );
}
}
- print_line_with_context($opt, $filename, $_, $.);
+ print_line_with_context( $filename, $_, $. );
$has_printed_for_this_resource = 1;
$nmatches++;
$max_count--;
}
elsif ( $opt_passthru ) {
- chomp; # XXX proper newline handling?
- # XXX inline this call?
+ chomp; # XXX Proper newline handling?
+ # XXX Inline this call?
if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {
App::Ack::print_blank_line();
}
- print_line_with_options($opt, $filename, $_, $., ':');
+ print_line_with_options( $filename, $_, $., ':' );
$has_printed_for_this_resource = 1;
}
else {
- chomp; # XXX proper newline handling?
- print_line_if_context($opt, $filename, $_, $., '-');
+ chomp; # XXX Proper newline handling?
+ print_line_if_context( $filename, $_, $., '-' );
}
last if ($max_count == 0) && ($after_context_pending == 0);
}
}
else {
- local $_;
-
if ( $opt_passthru ) {
+ local $_;
+
while ( <$fh> ) {
$match_column_number = undef;
if ( $opt_v ? !/$opt_regex/o : /$opt_regex/o ) {
App::Ack::print_filename( $display_filename, $ors );
}
}
- print_line_with_context($opt, $filename, $_, $.);
+ print_line_with_context( $filename, $_, $. );
$has_printed_for_this_resource = 1;
$nmatches++;
$max_count--;
if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {
App::Ack::print_blank_line();
}
- print_line_with_options($opt, $filename, $_, $., ':');
+ print_line_with_options( $filename, $_, $., ':' );
$has_printed_for_this_resource = 1;
}
last unless $max_count != 0;
}
}
elsif ( $opt_v ) {
+ local $_;
+
$match_column_number = undef;
while ( <$fh> ) {
if ( !/$opt_regex/o ) {
App::Ack::print_filename( $display_filename, $ors );
}
}
- print_line_with_context($opt, $filename, $_, $.);
+ print_line_with_context( $filename, $_, $. );
$has_printed_for_this_resource = 1;
$nmatches++;
$max_count--;
}
}
else {
- # XXX unroll first match check ($has_printed_for_this_resource)
- # XXX what if this is a *huge* file? (see also -l)
- my $contents = do {
- local $/;
- <$fh>;
- };
-
- my $prev_match_end = 0;
- my $line_no = 1;
-
- $match_column_number = undef;
- while ( $contents =~ /$opt_regex/og ) {
- my $match_start = $-[0];
- my $match_end = $+[0];
-
- pos($contents) = $prev_match_end;
- $prev_match_end = $match_end;
-
- while ( $contents =~ /\n/og && $-[0] < $match_start ) {
- $line_no++;
- }
-
- my $start_line = rindex($contents, "\n", $match_start);
- my $end_line = index($contents, "\n", $match_end);
-
- if ( $start_line == -1 ) {
- $start_line = 0;
- }
- else {
- $start_line++;
- }
+ local $_;
- if ( $end_line == -1 ) {
- $end_line = length($contents);
- }
- else {
- $end_line--;
- }
- $match_column_number = $match_start - $start_line + 1;
- if ( !$has_printed_for_this_resource ) {
- if ( $opt_break && $has_printed_something ) {
- App::Ack::print_blank_line();
- }
- if ( $opt_show_filename && $opt_heading ) {
- App::Ack::print_filename( $display_filename, $ors );
+ while ( <$fh> ) {
+ $match_column_number = undef;
+ if ( /$opt_regex/o ) {
+ $match_column_number = $-[0] + 1;
+ if ( !$has_printed_for_this_resource ) {
+ if ( $opt_break && $has_printed_something ) {
+ App::Ack::print_blank_line();
+ }
+ if ( $opt_show_filename && $opt_heading ) {
+ App::Ack::print_filename( $display_filename, $ors );
+ }
}
+ s/[\r\n]+$//g;
+ print_line_with_options( $filename, $_, $., ':' );
+ $has_printed_for_this_resource = 1;
+ $nmatches++;
+ $max_count--;
}
- my $line = substr($contents, $start_line, $end_line - $start_line + 1);
- $line =~ s/[\r\n]+$//g;
- print_line_with_options($opt, $filename, $line, $line_no, ':');
-
- pos($contents) = $end_line + 1;
-
- $has_printed_for_this_resource = 1;
- $nmatches++;
- $max_count--;
-
last unless $max_count != 0;
}
}
}
- $is_iterating = 0; # XXX this won't happen on an exception
- # then again, do we care? ack doesn't really
- # handle exceptions anyway.
+ $is_iterating = 0;
return $nmatches;
}
sub print_line_with_options {
- my ( $opt, $filename, $line, $line_no, $separator ) = @_;
+ my ( $filename, $line, $line_no, $separator ) = @_;
$has_printed_something = 1;
$printed_line_no = $line_no;
}
if( $opt_output ) {
while ( $line =~ /$opt_regex/og ) {
- # XXX We need to stop using eval() for --output. See https://github.com/petdance/ack2/issues/421
+ # XXX We need to stop using eval() for --output. See https://github.com/beyondgrep/ack2/issues/421
my $output = eval $opt_output;
App::Ack::print( join( $separator, @line_parts, $output ), $ors );
}
}
else {
if ( $opt_color ) {
- $line =~ /$opt_regex/o; # this match is redundant, but we need
- # to perfom it in order to get if
- # capture groups are set
+ # This match is redundant, but we need to perfom it in order to get if capture groups are set.
+ $line =~ /$opt_regex/o;
- if ( @+ > 1 ) { # if we have captures
+ if ( @+ > 1 ) { # If we have captures...
while ( $line =~ /$opt_regex/og ) {
- my $offset = 0; # additional offset for when we add stuff
+ my $offset = 0; # Additional offset for when we add stuff.
my $previous_match_end = 0;
+ last if $-[0] == $+[0];
+
for ( my $i = 1; $i < @+; $i++ ) {
my ( $match_start, $match_end ) = ( $-[$i], $+[$i] );
substr( $line, $offset + $match_start,
$match_end - $match_start, $substitution );
- $previous_match_end = $match_end; # offsets do not need to be applied
+ $previous_match_end = $match_end; # Offsets do not need to be applied.
$offset += length( $substitution ) - length( $substring );
}
}
}
else {
- my $matched = 0; # flag; if matched, need to escape afterwards
+ my $matched = 0; # If matched, need to escape afterwards.
while ( $line =~ /$opt_regex/og ) {
$matched = 1;
my ( $match_start, $match_end ) = ($-[0], $+[0]);
next unless defined($match_start);
+ last if $match_start == $match_end;
my $substring = substr( $line, $match_start,
$match_end - $match_start );
pos($line) = $match_end +
(length( $substitution ) - length( $substring ));
}
- # XXX why do we do this?
+ # XXX Why do we do this?
$line .= "\033[0m\033[K" if $matched;
}
}
}
sub iterate {
- my ( $resource, $opt, $cb ) = @_;
+ my ( $resource, $cb ) = @_;
$is_iterating = 1;
}
}
- $is_iterating = 0; # XXX this won't happen on an exception
- # then again, do we care? ack doesn't really
- # handle exceptions anyway.
+ $is_iterating = 0;
return;
}
sub print_line_with_context {
- my ( $opt, $filename, $matching_line, $line_no ) = @_;
+ my ( $filename, $matching_line, $line_no ) = @_;
my $ors = $opt_print0 ? "\0" : "\n";
my $is_tracking_context = $opt_after_context || $opt_before_context;
$matching_line =~ s/[\r\n]+$//g;
- # check if we need to print context lines first
- if( $is_tracking_context ) {
+ # Check if we need to print context lines first.
+ if ( $is_tracking_context ) {
my $before_unprinted = $line_no - $printed_line_no - 1;
if ( !$is_first_match && ( !$printed_line_no || $before_unprinted > $n_before_ctx_lines ) ) {
App::Ack::print('--', $ors);
}
- # We want at most $n_before_ctx_lines of context
+ # We want at most $n_before_ctx_lines of context.
if ( $before_unprinted > $n_before_ctx_lines ) {
$before_unprinted = $n_before_ctx_lines;
}
chomp $line;
- # Disable $opt->{column} since there are no matches in the context lines
+ # Disable $opt->{column} since there are no matches in the context lines.
local $opt_column = 0;
- print_line_with_options($opt, $filename, $line, $line_no-$before_unprinted, '-');
+ print_line_with_options( $filename, $line, $line_no-$before_unprinted, '-' );
$before_unprinted--;
}
}
- print_line_with_options($opt, $filename, $matching_line, $line_no, ':');
+ print_line_with_options( $filename, $matching_line, $line_no, ':' );
- # We want to get the next $n_after_ctx_lines printed
+ # We want to get the next $n_after_ctx_lines printed.
$after_context_pending = $n_after_ctx_lines;
$is_first_match = 0;
return;
}
-# print the line only if it's part of a context we need to display
+# Print the line only if it's part of a context we need to display.
sub print_line_if_context {
- my ( $opt, $filename, $line, $line_no, $separator ) = @_;
+ my ( $filename, $line, $line_no, $separator ) = @_;
if ( $after_context_pending ) {
- # Disable $opt->{column} since there are no matches in the context lines
+ # Disable $opt_column since there are no matches in the context lines.
local $opt_column = 0;
- print_line_with_options( $opt, $filename, $line, $line_no, $separator );
+ print_line_with_options( $filename, $line, $line_no, $separator );
--$after_context_pending;
}
elsif ( $n_before_ctx_lines ) {
- # save line for "before" context
+ # Save line for "before" context.
$before_context_buf[$before_context_pos] = $_;
$before_context_pos = ($before_context_pos+1) % $n_before_ctx_lines;
}
# does_match() MUST have an $opt_regex set.
-=for Developers
+=begin Developers
This subroutine is inlined a few places in print_matches_in_resource
for performance reasons, so any changes here must be copied there as
well.
+=end Developers
+
=cut
sub does_match {
- my ( $opt, $line ) = @_;
+ my ( $line ) = @_;
$match_column_number = undef;
}
sub resource_has_match {
- my ( $resource, $opt ) = @_;
+ my ( $resource ) = @_;
my $has_match = 0;
my $fh = $resource->open();
}
}
else {
- if ( $opt_v ) {
- while ( <$fh> ) {
- if (!/$opt_regex/o) {
- $has_match = 1;
- last;
- }
+ while ( <$fh> ) {
+ if (/$opt_regex/o xor $opt_v) {
+ $has_match = 1;
+ last;
}
}
- else {
- # XXX read in chunks
- # XXX only do this for certain file sizes?
- my $content = do {
- local $/;
- <$fh>;
- };
- $has_match = $content =~ /$opt_regex/o;
- }
close $fh;
}
}
sub count_matches_in_resource {
- my ( $resource, $opt ) = @_;
+ my ( $resource ) = @_;
my $nmatches = 0;
my $fh = $resource->open();
}
}
else {
- if ( $opt_v ) {
- while ( <$fh> ) {
- ++$nmatches if (!/$opt_regex/o);
- }
- }
- else {
- my $content = do {
- local $/;
- <$fh>;
- };
- $nmatches =()= ($content =~ /$opt_regex/og);
+ while ( <$fh> ) {
+ ++$nmatches if (/$opt_regex/o xor $opt_v);
}
close $fh;
}
my $nmatches = 0;
my $total_count = 0;
- setup_line_context( $opt );
+ set_up_line_context();
RESOURCES:
while ( my $resource = $resources->next ) {
if ($is_tracking_context) {
- setup_line_context_for_file($opt);
+ set_up_line_context_for_file();
}
- # XXX Combine the -f and -g functions
if ( $opt_f ) {
- # XXX printing should probably happen inside of App::Ack
if ( $opt->{show_types} ) {
show_types( $resource, $ors );
}
else {
local $opt_show_filename = 0; # XXX Why is this local?
- print_line_with_options($opt, '', $resource->name, 0, $ors);
+ print_line_with_options( '', $resource->name, 0, $ors );
}
++$nmatches;
last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;
local $opt_color = 0;
- iterate($resource, $opt, sub {
+ iterate( $resource, sub {
chomp;
if ( $line_numbers{$.} ) {
- print_line_with_context($opt, $filename, $_, $.);
+ print_line_with_context( $filename, $_, $. );
}
elsif ( $opt_passthru ) {
- print_line_with_options($opt, $filename, $_, $., ':');
+ print_line_with_options( $filename, $_, $., ':' );
}
elsif ( $is_tracking_context ) {
- print_line_if_context($opt, $filename, $_, $., '-');
+ print_line_if_context( $filename, $_, $., '-' );
}
return 1;
});
}
elsif ( $opt_count ) {
- my $matches_for_this_file = count_matches_in_resource( $resource, $opt );
+ my $matches_for_this_file = count_matches_in_resource( $resource );
if ( not $opt_show_filename ) {
$total_count += $matches_for_this_file;
}
}
elsif ( $opt_l || $opt_L ) {
- my $is_match = resource_has_match( $resource, $opt );
+ my $is_match = resource_has_match( $resource );
if ( $opt_L ? !$is_match : $is_match ) {
App::Ack::print( $resource->name, $ors );
App::Ack::exit_from_ack( $nmatches );
}
+=pod
+=encoding UTF-8
=head1 NAME
=head1 DESCRIPTION
-Ack is designed as an alternative to F<grep> for programmers.
+ack is designed as an alternative to F<grep> for programmers.
-Ack searches the named input FILEs (or standard input if no files
-are named, or the file name - is given) for lines containing a match
-to the given PATTERN. By default, ack prints the matching lines.
+ack searches the named input files or directories for lines containing a
+match to the given PATTERN. By default, ack prints the matching lines.
+If no FILE or DIRECTORY is given, the current directory will be searched.
PATTERN is a Perl regular expression. Perl regular expressions
are commonly found in other programming languages, but for the particulars
=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
Print I<NUM> lines (default 2) of context around matching lines.
+You can specify zero lines of context to override another context
+specified in an ackrc.
=item B<-c>, B<--count>
=item B<-g I<PATTERN>>
-Print files where the relative path + filename matches I<PATTERN>.
-This option can be combined with B<--color> to make it easier to spot
-the match.
+Print searchable files where the relative path + filename matches
+I<PATTERN>.
+
+Note that
+
+ ack -g foo
+
+is exactly the same as
+
+ ack -f | ack foo
+
+This means that just as ack will not search, for example, F<.jpg>
+files, C<-g> will not list F<.jpg> files either. ack is not intended
+to be a general-purpose file finder.
+
+Note also that if you have C<-i> in your .ackrc that the filenames
+to be matched will be case-insensitive as well.
+
+This option can be combined with B<--color> to make it easier to
+spot the match.
=item B<--[no]group>
=item B<-w>, B<--word-regexp>
+=item B<-w>, B<--word-regexp>
+
+Turn on "words mode". This sometimes matches a whole word, but the
+semantics is quite subtle. If the passed regexp begins with a word
+character, then a word boundary is required before the match. If the
+passed regexp ends with a word character, or with a word character
+followed by newline, then a word boundary is required after the match.
+
+Thus, for example, B<-w> with the regular expression C<ox> will not
+match the strings C<box> or C<oxen>. However, if the regular
+expression is C<(ox|ass)> then it will match those strings. Because
+the regular expression's first character is C<(>, the B<-w> flag has
+no effect at the start, and because the last character is C<)>, it has
+no effect at the end.
+
Force PATTERN to match only whole words. The PATTERN is wrapped with
C<\b> metacharacters.
# Always sort the files
--sort-files
- # Always color, even if piping to a another program
+ # Always color, even if piping to another program
--color
# Use "less -r" as my pager
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,
+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
+is why the C language is I<--cc> and the R language is I<--rr>.
+
I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>
tells you, that perl files are files ending
in .pl, .pm, .pod or .t. So what if you would like to include .xs
--type-set eiffel:ext:e,eiffel
-
In order to see all currently defined types, use I<--help-types>, e.g.
I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>
=head1 ACK & OTHER TOOLS
-=head2 Vim integration
+=head2 Simple vim integration
F<ack> integrates easily with the Vim text editor. Set this in your
F<.vimrc> to use F<ack> instead of F<grep>:
:grep Dumper perllib
-Miles Sterrett has written a Vim plugin for F<ack> which allows you to use
-C<:Ack> instead of C<:grep>, as well as several other advanced features.
-
-L<https://github.com/mileszs/ack.vim>
-
-=head2 Emacs integration
+=head2 Editor integration
-Phil Jackson put together an F<ack.el> extension that "provides a
-simple compilation mode ... has the ability to guess what files you
-want to search for based on the major-mode."
-
-L<http://www.shellarchive.co.uk/content/emacs.html>
-
-=head2 TextMate integration
-
-Pedro Melo is a TextMate user who writes "I spend my day mostly
-inside TextMate, and the built-in find-in-project sucks with large
-projects. So I hacked a TextMate command that was using find +
-grep to use ack. The result is the Search in Project with ack, and
-you can find it here:
-L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"
+Many users have integrated ack into their preferred text editors.
+For details and links, see L<https://beyondgrep.com/more-tools/>.
=head2 Shell and Return Code
input file contains "=head1 NAME"
output "1 : NAME"
-=head2 Share your knowledge
+=head1 COMMUNITY
-Join the ack-users mailing list. Send me your tips and I may add
-them here.
+There are ack mailing lists and a Slack channel for ack. See
+L<https://beyondgrep.com/community/> for details.
=head1 FAQ
=head2 Why isn't ack finding a match in (some file)?
-Probably because it's of a type that ack doesn't recognize. ack's
-searching behavior is driven by filetype. B<If ack doesn't know
-what kind of file it is, ack ignores the file.>
+First, take a look and see if ack is even looking at the file. ack is
+intelligent in what files it will search and which ones it won't, but
+sometimes that can be surprising.
-Use the C<-f> switch to see a list of files that ack will search
-for you. You can use the C<--show-types> switch to show which type
-ack thinks each file is.
+Use the C<-f> switch, with no regex, to see a list of files that ack
+will search for you. If your file doesn't show up in the list of files
+that C<ack -f> shows, then ack never looks in it.
+
+NOTE: If you're using an old ack before 2.0, it's probably because it's of
+a type that ack doesn't recognize. In ack 1.x, the searching behavior is
+driven by filetype. B<If ack 1.x doesn't know what kind of file it is,
+ack ignores the file.> You can use the C<--show-types> switch to show
+which type ack thinks each file is.
=head2 Wouldn't it be great if F<ack> did search & replace?
=head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?
-The Perl language limits the repetition quanitifier to 32K. You
+The Perl language limits the repetition quantifier to 32K. You
can search for C<.{32767}> but not C<.{32768}>.
+=head2 Ack does "X" and shouldn't, should it?
+
+We try to remain as close to grep's behavior as possible, so when in doubt,
+see what grep does! If there's a mismatch in functionality there, please
+bring it up on the ack-users mailing list.
+
=head1 ACKRC LOCATION SEMANTICS
Ack can load its configuration from many sources. The following list
=head1 BUGS
Please report any bugs or feature requests to the issues list at
-Github: L<https://github.com/petdance/ack2/issues>
+Github: L<https://github.com/beyondgrep/ack2/issues>
=head1 ENHANCEMENTS
ack users. This includes requests for new filetypes.
There is a list of enhancements I want to make to F<ack> in the ack
-issues list at Github: L<https://github.com/petdance/ack2/issues>
+issues list at Github: L<https://github.com/beyondgrep/ack2/issues>
Patches are always welcome, but patches with tests get the most
attention.
=item * The ack homepage
-L<http://beyondgrep.com/>
+L<https://beyondgrep.com/>
=item * The ack-users mailing list
=item * The ack issues list at Github
-L<https://github.com/petdance/ack2/issues>
+L<https://github.com/beyondgrep/ack2/issues>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://search.cpan.org/dist/ack>
+=item * MetaCPAN
+
+L<http://metacpan.org/release/ack>
+
=item * Git source repository
-L<https://github.com/petdance/ack2>
+L<https://github.com/beyondgrep/ack2>
=back
How appropriate to have I<ack>nowledgements!
Thanks to everyone who has contributed to ack in any way, including
+Michele Campeotto,
+H.Merijn Brand,
+Duke Leto,
+Gerhard Poul,
+Ethan Mallove,
+Marek Kubica,
+Ray Donnelly,
+Nikolaj Schumacher,
+Ed Avis,
+Nick Morrott,
+Austin Chamberlin,
+Varadinsky,
+SE<eacute>bastien FeugE<egrave>re,
+Jakub Wilk,
+Pete Houston,
Stephen Thirlwall,
Jonah Bishop,
Chris Rebert,
=head1 COPYRIGHT & LICENSE
-Copyright 2005-2015 Andy Lester.
+Copyright 2005-2017 Andy Lester.
This program is free software; you can redistribute it and/or modify
it under the terms of the Artistic License v2.0.
our $VERSION;
our $COPYRIGHT;
BEGIN {
- $VERSION = '2.15_01';
- $COPYRIGHT = 'Copyright 2005-2015 Andy Lester.';
+ $VERSION = '2.22';
+ $COPYRIGHT = 'Copyright 2005-2017 Andy Lester.';
}
our $fh;
$output_to_pipe = not -t *STDOUT;
$is_filter_mode = -p STDIN;
- $is_cygwin = ($^O eq 'cygwin');
+ $is_cygwin = ($^O eq 'cygwin' || $^O eq 'msys');
$is_windows = ($^O eq 'MSWin32');
$dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
}
return keys %mappings;
}
-sub _get_thpppt {
+sub thpppt {
my $y = q{_ /|,\\'!.x',=(www)=, U };
$y =~ tr/,x!w/\nOo_/;
- return $y;
-}
-sub _thpppt {
- my $y = _get_thpppt();
App::Ack::print( "$y ack $_[0]!\n" );
exit 0;
}
-sub _bar {
+sub ackbar {
my $x;
$x = <<'_BAR';
6?!I'7!I"?%+!
77I!+!7!?!7!I"71+!7,
_BAR
- return App::Ack::__pic($x);
+ return _pic_decode($x);
}
-sub _cathy {
+sub cathy {
my $x = <<'CATHY';
0+!--+!
0|! "C!H!O!C!O!L!A!T!E!!! !|!
0?!$! &N! )." .,! %."M! ":!M!.! 0
0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0
CATHY
- return App::Ack::__pic($x);
+ return _pic_decode($x);
}
-sub __pic {
+sub _pic_decode {
my($compressed) = @_;
$compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;
App::Ack::print( $compressed );
Exit status is 0 if match, 1 if no match.
-ack's home page is at http://beyondgrep.com/
+ack's home page is at https://beyondgrep.com/
The full ack manual is available by running "ack --man".
}
-# print*() subs added in order to make it easy for a third party
-# module (such as App::Wack) to redefine the display methods
-# and show the results in a different way.
sub print { print {$fh} @_; return; }
-sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }
sub print_blank_line { App::Ack::print( "\n" ); return; }
-sub print_separator { App::Ack::print( "--\n" ); return; }
sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }
-sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }
-sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }
-sub print_count {
- my $filename = shift;
- my $nmatches = shift;
- my $ors = shift;
- my $count = shift;
- my $show_filename = shift;
-
- if ($show_filename) {
- App::Ack::print( $filename );
- App::Ack::print( ':', $nmatches ) if $count;
- }
- else {
- App::Ack::print( $nmatches ) if $count;
- }
- App::Ack::print( $ors );
-
- return;
-}
-
-sub print_count0 {
- my $filename = shift;
- my $ors = shift;
- my $show_filename = shift;
-
- if ($show_filename) {
- App::Ack::print( $filename, ':0', $ors );
- }
- else {
- App::Ack::print( '0', $ors );
- }
-
- return;
-}
sub set_up_pager {
my $command = shift;
use overload
'""' => 'name';
-sub FAIL {
- require Carp;
- Carp::confess( 'Must be overloaded' );
-}
-
sub new {
- return FAIL();
+ my $class = shift;
+ my $filename = shift;
+
+ my $self = bless {
+ filename => $filename,
+ fh => undef,
+ opened => 0,
+ }, $class;
+
+ if ( $self->{filename} eq '-' ) {
+ $self->{fh} = *STDIN;
+ $self->{opened} = 1;
+ }
+
+ return $self;
}
+
sub name {
- return FAIL();
+ return $_[0]->{filename};
}
+
sub basename {
- return FAIL();
-}
+ my ( $self ) = @_;
+ # XXX Definedness? Pre-populate the slot with an undef?
+ unless ( exists $self->{basename} ) {
+ $self->{basename} = (File::Spec->splitpath($self->name))[2];
+ }
-sub is_binary {
- return FAIL();
+ return $self->{basename};
}
+
sub open {
- return FAIL();
+ my ( $self ) = @_;
+
+ if ( !$self->{opened} ) {
+ if ( open $self->{fh}, '<', $self->{filename} ) {
+ $self->{opened} = 1;
+ }
+ else {
+ $self->{fh} = undef;
+ }
+ }
+
+ return $self->{fh};
}
+
sub needs_line_scan {
- return FAIL();
+ my $self = shift;
+ my $opt = shift;
+
+ return 1 if $opt->{v};
+
+ my $size = -s $self->{fh};
+ if ( $size == 0 ) {
+ return 0;
+ }
+ elsif ( $size > 100_000 ) {
+ return 1;
+ }
+
+ my $buffer;
+ my $rc = sysread( $self->{fh}, $buffer, $size );
+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {
+ App::Ack::warn( "$self->{filename}: $!" );
+ return 1;
+ }
+ return 0 unless $rc && ( $rc == $size );
+
+ return $buffer =~ /$opt->{regex}/m;
}
+
sub reset {
- return FAIL();
+ my $self = shift;
+
+ # Return if we haven't opened the file yet.
+ if ( !defined($self->{fh}) ) {
+ return;
+ }
+
+ if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
+ App::Ack::warn( "$self->{filename}: $!" );
+ }
+
+ return;
}
+
sub close {
- return FAIL();
+ my $self = shift;
+
+ # Return if we haven't opened the file yet.
+ if ( !defined($self->{fh}) ) {
+ return;
+ }
+
+ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
+ App::Ack::warn( $self->name() . ": $!" );
+ }
+
+ $self->{opened} = 0;
+
+ return;
}
+
sub clone {
- return FAIL();
+ my ( $self ) = @_;
+
+ return __PACKAGE__->new($self->name);
}
+
sub firstliney {
- return FAIL();
+ my ( $self ) = @_;
+
+ if ( !exists $self->{firstliney} ) {
+ my $fh = $self->open();
+ if ( !$fh ) {
+ if ( $App::Ack::report_bad_filenames ) {
+ App::Ack::warn( $self->name . ': ' . $! );
+ }
+ return '';
+ }
+
+ my $buffer = '';
+ my $rc = sysread( $fh, $buffer, 250 );
+ unless($rc) { # XXX handle this better?
+ $buffer = '';
+ }
+ $buffer =~ s/[\r\n].*//s;
+ $self->{firstliney} = $buffer;
+ $self->reset;
+
+ $self->close;
+ }
+
+ return $self->{firstliney};
}
1;
+use Errno qw(EACCES);
+
use warnings;
use strict;
if ( $opt->{dont_report_bad_filenames} ) {
return sub {
my $msg = shift;
- # XXX restricting to specific error messages for now; I would
- # prefer a different way of doing this
- if ( $msg =~ /Permission denied/ ) {
+ if ( $! == EACCES ) {
return;
}
App::Ack::warn( $msg );
my $file = $self->{iter}->() or return;
- return App::Ack::Resource::Basic->new( $file );
-}
-
-1;
-package App::Ack::Resource::Basic;
-
-
-use warnings;
-use strict;
-
-use Fcntl ();
-use File::Spec ();
-
-BEGIN {
- our @ISA = 'App::Ack::Resource';
-}
-
-
-
-sub new {
- my $class = shift;
- my $filename = shift;
-
- my $self = bless {
- filename => $filename,
- fh => undef,
- opened => 0,
- }, $class;
-
- if ( $self->{filename} eq '-' ) {
- $self->{fh} = *STDIN;
- $self->{opened} = 1;
- }
-
- return $self;
-}
-
-
-sub name {
- return $_[0]->{filename};
-}
-
-sub basename {
- my ( $self ) = @_;
-
- # XXX definedness? pre-populate the slot with an undef?
- unless ( exists $self->{basename} ) {
- $self->{basename} = (File::Spec->splitpath($self->name))[2];
- }
-
- return $self->{basename};
-}
-
-
-sub needs_line_scan {
- my $self = shift;
- my $opt = shift;
-
- return 1 if $opt->{v};
-
- my $size = -s $self->{fh};
- if ( $size == 0 ) {
- return 0;
- }
- elsif ( $size > 100_000 ) {
- return 1;
- }
-
- my $buffer;
- my $rc = sysread( $self->{fh}, $buffer, $size );
- if ( !defined($rc) && $App::Ack::report_bad_filenames ) {
- App::Ack::warn( "$self->{filename}: $!" );
- return 1;
- }
- return 0 unless $rc && ( $rc == $size );
-
- my $regex = $opt->{regex};
- return $buffer =~ /$regex/m;
-}
-
-
-sub reset {
- my $self = shift;
-
- # return if we haven't opened the file yet
- if ( !defined($self->{fh}) ) {
- return;
- }
-
- if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
- App::Ack::warn( "$self->{filename}: $!" );
- }
-
- return;
-}
-
-
-sub close {
- my $self = shift;
-
- # return if we haven't opened the file yet
- if ( !defined($self->{fh}) ) {
- return;
- }
-
- if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
- App::Ack::warn( $self->name() . ": $!" );
- }
-
- $self->{opened} = 0;
-
- return;
-}
-
-
-sub clone {
- my ( $self ) = @_;
-
- return __PACKAGE__->new($self->name);
-}
-
-sub firstliney {
- my ( $self ) = @_;
-
- my $fh = $self->open();
-
- if ( !exists $self->{firstliney} ) {
- my $buffer = '';
- my $rc = sysread( $fh, $buffer, 250 );
- unless($rc) { # XXX handle this better?
- $buffer = '';
- }
- $buffer =~ s/[\r\n].*//s;
- $self->{firstliney} = $buffer;
- $self->reset;
- }
-
- $self->close;
-
- return $self->{firstliney};
-}
-
-sub open {
- my ( $self ) = @_;
-
- return $self->{fh} if $self->{opened};
-
- if ( ! open $self->{fh}, '<', $self->{filename} ) {
- return;
- }
-
- $self->{opened} = 1;
-
- return $self->{fh};
+ return App::Ack::Resource->new( $file );
}
1;
+
sub options {
return split( /\n/, _options_block() );
}
# Git
# http://git-scm.com/
--ignore-directory=is:.git
+# When using submodules, .git is a file.
+--ignore-file=is:.git
# Mercurial
# http://mercurial.selenic.com/
# Clojure
# http://clojure.org/
---type-add=clojure:ext:clj
+--type-add=clojure:ext:clj,cljs,edn,cljc
# C
# .xs are Perl C files
# http://groovy.codehaus.org/
--type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
+# GSP
+# http://groovy.codehaus.org/GSP
+--type-add=gsp:ext:gsp
+
# Haskell
# http://www.haskell.org/
--type-add=haskell:ext:hs,lhs
# HTML
---type-add=html:ext:htm,html
+--type-add=html:ext:htm,html,xhtml
# Jade
# http://jade-lang.com/
# JSP
# http://www.oracle.com/technetwork/java/javaee/jsp/index.html
---type-add=jsp:ext:jsp,jspx,jhtm,jhtml
+--type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml
# JSON
# http://www.json.org/
--type-add=json:ext:json
+# Kotlin
+# https://kotlinlang.org/
+--type-add=kotlin:ext:kt,kts
+
# Less
# http://www.lesscss.org/
--type-add=less:ext:less
# OCaml
# http://caml.inria.fr/
---type-add=ocaml:ext:ml,mli
+--type-add=ocaml:ext:ml,mli,mll,mly
# Matlab
# http://en.wikipedia.org/wiki/MATLAB
# http://learnboost.github.io/stylus/
--type-add=stylus:ext:styl
+# Swift
+# https://developer.apple.com/swift/
+--type-add=swift:ext:swift
+--type-add=swift:firstlinematch:/^#!.*\bswift/
+
# Tcl
# http://www.tcl.tk/
--type-add=tcl:ext:tcl,itcl,itk
# XML
# http://www.w3.org/TR/REC-xml/
---type-add=xml:ext:xml,dtd,xsl,xslt,ent
+--type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl
--type-add=xml:firstlinematch:/<[?]xml/
# YAML
push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});
}
- # XXX This should go through some untainted cwd-fetching function, and not get untainted inline like this.
my $cwd = Cwd::getcwd();
+ return () unless defined $cwd;
+
+ # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this.
$cwd =~ /(.+)/;
$cwd = $1;
my @dirs = File::Spec->splitdir( $cwd );
use warnings;
use Carp 1.04 ();
-use Getopt::Long 2.35 ();
+use Getopt::Long 2.38 ();
use Text::ParseWords 3.1 ();
$i--;
}
}
+
+ return;
}
$explanation ||= '';
return sub {
- warn "Option '$option' is not valid in ack 2\n$explanation";
+ warn "Option '$option' is not valid in ack 2.\n$explanation";
exit 1;
};
}
my ( $opt, $extra_specs ) = @_;
my $dash_a_explanation = <<'EOT';
-This is because we now have -k/--known-types which makes it only select files
-of known types, rather than any text file (which is the behavior of ack 1.x).
-You may have options in a .ackrc, or in the ACKRC_OPTIONS environment variable.
-Try using the --dump flag.
+You don't need -a, ack 1.x users. This is because ack 2.x has
+-k/--known-types which makes it only select files of known types, rather
+than any text file (which is the behavior of ack 1.x).
+
+If you're surprised to see this message because you didn't put -a on the
+command line, you may have options in an .ackrc, or in the ACKRC_OPTIONS
+environment variable. Try using the --dump flag to help find it.
EOT
+ sub _context_value {
+ my $val = shift;
+
+ # Contexts default to 2.
+ return (!defined($val) || ($val < 0)) ? 2 : $val;
+ }
+
return {
1 => sub { $opt->{1} = $opt->{m} = 1 },
- 'A|after-context=i' => \$opt->{after_context},
- 'B|before-context=i'
- => \$opt->{before_context},
- 'C|context:i' => sub { shift; my $val = shift; $opt->{before_context} = $opt->{after_context} = ($val || 2) },
+ 'A|after-context:-1' => sub { shift; $opt->{after_context} = _context_value(shift) },
+ 'B|before-context:-1' => sub { shift; $opt->{before_context} = _context_value(shift) },
+ 'C|context:-1' => sub { shift; $opt->{before_context} = $opt->{after_context} = _context_value(shift) },
'a' => removed_option('-a', $dash_a_explanation),
'all' => removed_option('--all', $dash_a_explanation),
'break!' => \$opt->{break},
die "Options --output, --pager and --match are forbidden in project .ackrc files.\n";
};
- $args_for_source = { %$args_for_source,
+ $args_for_source = {
+ %{$args_for_source},
'output=s' => $illegal,
'pager:s' => $illegal,
'match=s' => $illegal,
}
}
}
+
+ return;
}
1; # End of App::Ack::ConfigLoader
package App::Ack::Filter;
+
use strict;
use warnings;
1;
package App::Ack::Filter::Extension;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::FirstLineMatch;
+
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::Is;
+
use strict;
use warnings;
BEGIN {
use File::Spec 3.00;
+
sub new {
my ( $class, $re ) = @_;
1;
package App::Ack::Filter::Default;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::Inverse;
+
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::Collection;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::IsGroup;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::ExtensionGroup;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::MatchGroup;
+
use strict;
use warnings;
BEGIN {
sub inspect {
my ( $self ) = @_;
+
+ # XXX Needs an explicit return.
}
sub to_string {
my ( $self ) = @_;
+
+ # XXX Needs an explicit return.
}
1;
package App::Ack::Filter::IsPath;
+
use strict;
use warnings;
BEGIN {
1;
package App::Ack::Filter::IsPathGroup;
+
+
+
use strict;
use warnings;
BEGIN {
use warnings;
-our $VERSION = '1.12';
+our $VERSION = '1.16';
%files_defaults = (
file_filter => undef,
descend_filter => undef,
- error_handler => sub { CORE::die @_ },
+ error_handler => sub { CORE::die $_[0] },
warning_handler => sub { CORE::warn @_ },
sort_files => undef,
follow_symlinks => 1,
die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
my ($parms,@queue) = _setup( \%files_defaults, @_ );
- my $filter = $parms->{file_filter};
return sub {
+ my $filter = $parms->{file_filter};
while (@queue) {
my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
- if ( -f $fullpath || -p $fullpath || $fullpath =~ m{^/dev/fd} ) {
+ if ( -f $fullpath || -p _ || $fullpath =~ m{^/dev/fd} ) {
if ( $filter ) {
local $_ = $file;
local $File::Next::dir = $dirname;
}
return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
}
- elsif ( -d _ ) {
+ if ( -d _ ) {
unshift( @queue, _candidate_files( $parms, $fullpath ) );
}
} # while
my ($parms,@queue) = _setup( \%files_defaults, @_ );
my $err = $parms->{error_handler};
- my $warn = $parms->{error_handler};
+ my $warn = $parms->{warning_handler};
my $filename = $queue[1];
}
else {
if ( !open( $fh, '<', $filename ) ) {
- $err->( "Unable to open $filename: $!" );
+ $err->( "Unable to open $filename: $!", $! + 0 );
return undef;
}
}
- my $filter = $parms->{file_filter};
return sub {
+ my $filter = $parms->{file_filter};
local $/ = $parms->{nul_separated} ? "\x00" : $/;
while ( my $fullpath = <$fh> ) {
chomp $fullpath;
my $dh;
if ( !opendir $dh, $dirname ) {
- $parms->{error_handler}->( "$dirname: $!" );
+ $parms->{error_handler}->( "$dirname: $!", $! + 0 );
return;
}
for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
my $has_stat;
- # Only do directory checking if we have a descend_filter
my $fullpath = File::Spec->catdir( $dirname, $file );
if ( !$follow_symlinks ) {
next if -l $fullpath;
$has_stat = 1;
}
+ # Only do directory checking if we have a descend_filter
if ( $descend_filter ) {
if ( $has_stat ? (-d _) : (-d $fullpath) ) {
local $File::Next::dir = $fullpath;