171222.1 · b7a90465c51b18b0084e3441d2e78767b4aefad3
Added 2.22 to the garage
reading declarations
Changes to garage/ack-2.22. All files
@@ -0,0 +1,5346 @@+#!/usr/bin/env perl+#+# This file, ack, is generated code.+# Please DO NOT EDIT or send patches for it.+#+# Please take a look at the source from+# https://github.com/beyondgrep/ack2+# and submit patches against the individual files+# that build ack.+#++package main;++use strict;+use warnings;+our $VERSION = '2.22'; # Check https://beyondgrep.com/ for updates++use 5.008008;+use Getopt::Long 2.38 ();+use Carp 1.04 ();++use File::Spec ();+++# XXX Don't make this so brute force+# See also: https://github.com/beyondgrep/ack2/issues/89++our $opt_after_context;+our $opt_before_context;+our $opt_output;+our $opt_print0;+our $opt_color;+our $opt_heading;+our $opt_show_filename;+our $opt_regex;+our $opt_break;+our $opt_count;+our $opt_v;+our $opt_m;+our $opt_g;+our $opt_f;+our $opt_lines;+our $opt_L;+our $opt_l;+our $opt_passthru;+our $opt_column;++# Flag if we need any context tracking.+our $is_tracking_context;++# These are all our globals.++MAIN: {+ $App::Ack::orig_program_name = $0;+ $0 = join(' ', 'ack', $0);+ if ( $App::Ack::VERSION ne $main::VERSION ) {+ App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );+ }++ # Do preliminary arg checking;+ my $env_is_usable = 1;+ for my $arg ( @ARGV ) {+ 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::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;+ $arg eq '--noenv' and $env_is_usable = 0;+ }++ if ( !$env_is_usable ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ load_colors();++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure('pass_through', 'no_auto_abbrev');+ Getopt::Long::GetOptions(+ 'help' => sub { App::Ack::show_help(); exit; },+ 'version' => sub { App::Ack::print_version_statement(); exit; },+ 'man' => sub { App::Ack::show_man(); exit; },+ );+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ if ( !@ARGV ) {+ App::Ack::show_help();+ exit 1;+ }++ main();+}++sub _compile_descend_filter {+ my ( $opt ) = @_;++ my $idirs = 0;+ my $dont_ignore_dirs = 0;++ for my $filter (@{$opt->{idirs} || []}) {+ if ($filter->is_inverted()) {+ $dont_ignore_dirs++;+ }+ else {+ $idirs++;+ }+ }++ # 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.+ return if $dont_ignore_dirs;+ return unless $idirs;++ $idirs = $opt->{idirs};++ return sub {+ my $resource = App::Ack::Resource->new($File::Next::dir);+ return !grep { $_->filter($resource) } @{$idirs};+ };+}++sub _compile_file_filter {+ my ( $opt, $start ) = @_;++ my $ifiles_filters = $opt->{ifiles};++ my $filters = $opt->{'filters'} || [];+ my $direct_filters = App::Ack::Filter::Collection->new();+ my $inverse_filters = App::Ack::Filter::Collection->new();++ foreach my $filter (@{$filters}) {+ if ($filter->is_inverted()) {+ # We want to check if files match the uninverted filters+ $inverse_filters->add($filter->invert());+ }+ else {+ $direct_filters->add($filter);+ }+ }++ my %is_member_of_starting_set = map { (get_file_id($_) => 1) } @{$start};++ 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).+ @ignore_dir_filter = map { $_->is_inverted() ? $_->invert() : $_ } @ignore_dir_filter;+ my $dont_ignore_dir_filter = grep { $_ } @is_inverted;+ my $previous_dir = '';+ my $previous_dir_ignore_result;++ return sub {+ if ( $opt_g ) {+ if ( $File::Next::name =~ /$opt_regex/ && $opt_v ) {+ return 0;+ }+ if ( $File::Next::name !~ /$opt_regex/ && !$opt_v ) {+ return 0;+ }+ }+ # ack always selects files that are specified on the command+ # line, regardless of filetype. If you want to ack a JPEG,+ # and say "ack foo whatever.jpg" it will do it for you.+ return 1 if $is_member_of_starting_set{ get_file_id($File::Next::name) };++ if ( $dont_ignore_dir_filter ) {+ if ( $previous_dir eq $File::Next::dir ) {+ if ( $previous_dir_ignore_result ) {+ return 0;+ }+ }+ else {+ my @dirs = File::Spec->splitdir($File::Next::dir);++ my $is_ignoring = 0;++ for ( my $i = 0; $i < @dirs; $i++) {+ my $dir_rsrc = App::Ack::Resource->new(File::Spec->catfile(@dirs[0 .. $i]));++ my $j = 0;+ for my $filter (@ignore_dir_filter) {+ if ( $filter->filter($dir_rsrc) ) {+ $is_ignoring = !$is_inverted[$j];+ }+ $j++;+ }+ }++ $previous_dir = $File::Next::dir;+ $previous_dir_ignore_result = $is_ignoring;++ if ( $is_ignoring ) {+ return 0;+ }+ }+ }++ # Ignore named pipes found in directory searching. Named+ # pipes created by subprocesses get specified on the command+ # line, so the rule of "always select whatever is on the+ # command line" wins.+ return 0 if -p $File::Next::name;++ # We can't handle unreadable filenames; report them.+ if ( not -r _ ) {+ use filetest 'access';++ if ( not -R $File::Next::name ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "${File::Next::name}: cannot open file for reading" );+ }+ return 0;+ }+ }++ my $resource = App::Ack::Resource->new($File::Next::name);++ if ( $ifiles_filters && $ifiles_filters->filter($resource) ) {+ return 0;+ }++ my $match_found = $direct_filters->filter($resource);++ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && $inverse_filters->filter( $resource ) ) {+ $match_found = 0;+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++# Set default colors, load Term::ANSIColor+sub load_colors {+ eval 'use Term::ANSIColor 1.10 ()';+ eval 'use Win32::Console::ANSI' if $App::Ack::is_windows;++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}++sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %App::Ack::mappings) {+ my $filters = $App::Ack::mappings{$k};++ foreach my $filter (@{$filters}) {+ # Clone the resource.+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ # http://search.cpan.org/dist/Perl-Critic/lib/Perl/Critic/Policy/Subroutines/ProhibitReturnSort.pm+ @matches = sort @matches;+ return @matches;+}++# Returns a (fairly) unique identifier for a file.+# Use this function to compare two files to see if they're+# equal (ie. the same file, but with a different path/links/etc).+sub get_file_id {+ my ( $filename ) = @_;++ if ( $App::Ack::is_windows ) {+ return File::Next::reslash( $filename );+ }+ else {+ # 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.+ return $filename;+ }+ }+}++# Returns a regex object based on a string and command-line options.+# Dies when the regex $str is undefined (i.e. not given on command line).++sub build_regex {+ my $str = shift;+ my $opt = shift;++ defined $str or App::Ack::die( 'No regular expression found.' );++ $str = quotemeta( $str ) if $opt->{Q};+ if ( $opt->{w} ) {+ my $pristine_str = $str;++ $str = "(?:$str)";+ $str = "\\b$str" if $pristine_str =~ /^\w/;+ $str = "$str\\b" if $pristine_str =~ /\w$/;+ }++ my $regex_is_lc = $str eq lc $str;+ if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {+ $str = "(?i)$str";+ }++ my $re = eval { qr/$str/m };+ if ( !$re ) {+ die "Invalid regex '$str':\n $@";+ }++ return $re;++}++my $match_column_number;++{++# 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+my @before_context_buf;+# Position to insert next line in @before_context_buf+my $before_context_pos;++# Number of "after" context lines still pending+my $after_context_pending;++# Number of latest line that got printed+my $printed_line_no;++my $is_iterating;++my $is_first_match;+my $has_printed_something;++BEGIN {+ $has_printed_something = 0;+}++# 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);++ @before_context_buf = (undef) x $n_before_ctx_lines;+ $before_context_pos = 0;++ $is_tracking_context = $n_before_ctx_lines || $n_after_ctx_lines;++ $is_first_match = 1;++ return;+}++# 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 ) {+ $is_first_match = 1;+ }++ return;+}++=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,+in non-context tracking searches (not using -A, -B, or -C),+conditions that normally would be checked inside the loop happen+outside, resulting in three nearly identical loops for -v, --passthru,+and normal searching. Any changes that happen to one should propagate+to the others if they make sense. The non-context branches also inline+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 ) = @_;++ my $max_count = $opt_m || -1;+ my $nmatches = 0;+ my $filename = $resource->name;+ my $ors = $opt_print0 ? "\0" : "\n";++ my $has_printed_for_this_resource = 0;++ $is_iterating = 1;++ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$filename: $!" );+ }+ return 0;+ }++ my $display_filename = $filename;+ if ( $opt_show_filename && $opt_heading && $opt_color ) {+ $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.+ if ( $is_tracking_context ) {+ $after_context_pending = 0;+ while ( <$fh> ) {+ if ( does_match( $_ ) && $max_count ) {+ 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 );+ }+ }+ 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?+ if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_resource = 1;+ }+ else {+ chomp; # XXX Proper newline handling?+ print_line_if_context( $filename, $_, $., '-' );+ }++ last if ($max_count == 0) && ($after_context_pending == 0);+ }+ }+ else {+ if ( $opt_passthru ) {+ local $_;++ while ( <$fh> ) {+ $match_column_number = undef;+ if ( $opt_v ? !/$opt_regex/o : /$opt_regex/o ) {+ if ( !$opt_v ) {+ $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 );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ else {+ chomp; # XXX proper newline handling?+ if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ 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 ) {+ 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 );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ last unless $max_count != 0;+ }+ }+ else {+ local $_;++ 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--;+ }+ last unless $max_count != 0;+ }+ }++ }++ $is_iterating = 0;++ return $nmatches;+}++sub print_line_with_options {+ my ( $filename, $line, $line_no, $separator ) = @_;++ $has_printed_something = 1;+ $printed_line_no = $line_no;++ my $ors = $opt_print0 ? "\0" : "\n";++ my @line_parts;++ if( $opt_color ) {+ $filename = Term::ANSIColor::colored($filename,+ $ENV{ACK_COLOR_FILENAME});+ $line_no = Term::ANSIColor::colored($line_no,+ $ENV{ACK_COLOR_LINENO});+ }++ if($opt_show_filename) {+ if( $opt_heading ) {+ push @line_parts, $line_no;+ }+ else {+ push @line_parts, $filename, $line_no;+ }++ if( $opt_column ) {+ push @line_parts, get_match_column();+ }+ }+ if( $opt_output ) {+ while ( $line =~ /$opt_regex/og ) {+ # 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 ) {+ # 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...+ while ( $line =~ /$opt_regex/og ) {+ 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] );++ next unless defined($match_start);+ next if $match_start < $previous_match_end;++ my $substring = substr( $line,+ $offset + $match_start, $match_end - $match_start );+ my $substitution = Term::ANSIColor::colored( $substring,+ $ENV{ACK_COLOR_MATCH} );++ substr( $line, $offset + $match_start,+ $match_end - $match_start, $substitution );++ $previous_match_end = $match_end; # Offsets do not need to be applied.+ $offset += length( $substitution ) - length( $substring );+ }++ pos($line) = $+[0] + $offset;+ }+ }+ else {+ 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 );+ my $substitution = Term::ANSIColor::colored( $substring,+ $ENV{ACK_COLOR_MATCH} );++ substr( $line, $match_start, $match_end - $match_start,+ $substitution );++ pos($line) = $match_end ++ (length( $substitution ) - length( $substring ));+ }+ # XXX Why do we do this?+ $line .= "\033[0m\033[K" if $matched;+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++sub iterate {+ my ( $resource, $cb ) = @_;++ $is_iterating = 1;++ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ return;+ }++ # 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> ) {+ last unless $cb->();+ }+ }+ else {+ local $_;++ while ( <$fh> ) {+ last unless $cb->();+ }+ }++ $is_iterating = 0;++ return;+}++sub print_line_with_context {+ 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 ) {+ 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.+ if ( $before_unprinted > $n_before_ctx_lines ) {+ $before_unprinted = $n_before_ctx_lines;+ }++ while ( $before_unprinted > 0 ) {+ my $line = $before_context_buf[($before_context_pos - $before_unprinted + $n_before_ctx_lines) % $n_before_ctx_lines];++ chomp $line;++ # Disable $opt->{column} since there are no matches in the context lines.+ local $opt_column = 0;++ print_line_with_options( $filename, $line, $line_no-$before_unprinted, '-' );+ $before_unprinted--;+ }+ }++ print_line_with_options( $filename, $matching_line, $line_no, ':' );++ # 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.+sub print_line_if_context {+ my ( $filename, $line, $line_no, $separator ) = @_;++ if ( $after_context_pending ) {+ # Disable $opt_column since there are no matches in the context lines.+ local $opt_column = 0;+ print_line_with_options( $filename, $line, $line_no, $separator );+ --$after_context_pending;+ }+ elsif ( $n_before_ctx_lines ) {+ # Save line for "before" context.+ $before_context_buf[$before_context_pos] = $_;+ $before_context_pos = ($before_context_pos+1) % $n_before_ctx_lines;+ }++ return;+}++}++# does_match() MUST have an $opt_regex set.++=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 ( $line ) = @_;++ $match_column_number = undef;++ if ( $opt_v ) {+ return ( $line !~ /$opt_regex/o );+ }+ else {+ if ( $line =~ /$opt_regex/o ) {+ # @- = @LAST_MATCH_START+ # @+ = @LAST_MATCH_END+ $match_column_number = $-[0] + 1;+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_match_column {+ return $match_column_number;+}++sub resource_has_match {+ my ( $resource ) = @_;++ my $has_match = 0;+ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ if (/$opt_regex/o xor $opt_v) {+ $has_match = 1;+ last;+ }+ }+ close $fh;+ }++ return $has_match;+}++sub count_matches_in_resource {+ my ( $resource ) = @_;++ my $nmatches = 0;+ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ ++$nmatches if (/$opt_regex/o xor $opt_v);+ }+ close $fh;+ }++ return $nmatches;+}++sub main {+ my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();++ my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );++ $opt_after_context = $opt->{after_context};+ $opt_before_context = $opt->{before_context};+ $opt_output = $opt->{output};+ $opt_print0 = $opt->{print0};+ $opt_color = $opt->{color};+ $opt_heading = $opt->{heading};+ $opt_show_filename = $opt->{show_filename};+ $opt_regex = $opt->{regex};+ $opt_break = $opt->{break};+ $opt_count = $opt->{count};+ $opt_v = $opt->{v};+ $opt_m = $opt->{m};+ $opt_g = $opt->{g};+ $opt_f = $opt->{f};+ $opt_lines = $opt->{lines};+ $opt_L = $opt->{L};+ $opt_l = $opt->{l};+ $opt_passthru = $opt->{passthru};+ $opt_column = $opt->{column};++ $App::Ack::report_bad_filenames = !$opt->{dont_report_bad_filenames};++ if ( $opt->{flush} ) {+ $| = 1;+ }++ if ( !defined($opt_color) && !$opt_g ) {+ my $windows_color = 1;+ if ( $App::Ack::is_windows ) {+ $windows_color = eval { require Win32::Console::ANSI; };+ }+ $opt_color = !App::Ack::output_to_pipe() && $windows_color;+ }+ if ( not defined $opt_heading and not defined $opt_break ) {+ $opt_heading = $opt_break = $opt->{break} = !App::Ack::output_to_pipe();+ }++ if ( defined($opt->{H}) || defined($opt->{h}) ) {+ $opt_show_filename = $opt->{show_filename} = $opt->{H} && !$opt->{h};+ }++ if ( my $output = $opt_output ) {+ $output =~ s{\\}{\\\\}g;+ $output =~ s{"}{\\"}g;+ $opt_output = qq{"$output"};+ }++ my $resources;+ if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x+ $resources = App::Ack::Resources->from_stdin( $opt );+ $opt_regex = shift @ARGV if not defined $opt_regex;+ $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );+ }+ else {+ if ( $opt_f || $opt_lines ) {+ if ( $opt_regex ) {+ App::Ack::warn( "regex ($opt_regex) specified with -f or --lines" );+ App::Ack::exit_from_ack( 0 ); # XXX the 0 is misleading+ }+ }+ else {+ $opt_regex = shift @ARGV if not defined $opt_regex;+ $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );+ }+ if ( $opt_regex && $opt_regex =~ /\n/ ) {+ App::Ack::exit_from_ack( 0 );+ }+ my @start;+ if ( not defined $opt->{files_from} ) {+ @start = @ARGV;+ }+ if ( !exists($opt->{show_filename}) ) {+ unless(@start == 1 && !(-d $start[0])) {+ $opt_show_filename = $opt->{show_filename} = 1;+ }+ }++ if ( defined $opt->{files_from} ) {+ $resources = App::Ack::Resources->from_file( $opt, $opt->{files_from} );+ exit 1 unless $resources;+ }+ else {+ @start = ('.') unless @start;+ foreach my $target (@start) {+ if ( !-e $target && $App::Ack::report_bad_filenames) {+ App::Ack::warn( "$target: No such file or directory" );+ }+ }++ $opt->{file_filter} = _compile_file_filter($opt, \@start);+ $opt->{descend_filter} = _compile_descend_filter($opt);++ $resources = App::Ack::Resources->from_argv( $opt, \@start );+ }+ }+ App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};++ my $ors = $opt_print0 ? "\0" : "\n";+ my $only_first = $opt->{1};++ my $nmatches = 0;+ my $total_count = 0;++ set_up_line_context();++RESOURCES:+ while ( my $resource = $resources->next ) {+ if ($is_tracking_context) {+ set_up_line_context_for_file();+ }++ if ( $opt_f ) {+ if ( $opt->{show_types} ) {+ show_types( $resource, $ors );+ }+ else {+ App::Ack::print( $resource->name, $ors );+ }+ ++$nmatches;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ elsif ( $opt_g ) {+ if ( $opt->{show_types} ) {+ show_types( $resource, $ors );+ }+ else {+ local $opt_show_filename = 0; # XXX Why is this local?++ print_line_with_options( '', $resource->name, 0, $ors );+ }+ ++$nmatches;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ elsif ( $opt_lines ) {+ my %line_numbers;+ foreach my $line ( @{ $opt_lines } ) {+ my @lines = split /,/, $line;+ @lines = map {+ /^(\d+)-(\d+)$/+ ? ( $1 .. $2 )+ : $_+ } @lines;+ @line_numbers{@lines} = (1) x @lines;+ }++ my $filename = $resource->name;++ local $opt_color = 0;++ iterate( $resource, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ print_line_with_context( $filename, $_, $. );+ }+ elsif ( $opt_passthru ) {+ print_line_with_options( $filename, $_, $., ':' );+ }+ elsif ( $is_tracking_context ) {+ print_line_if_context( $filename, $_, $., '-' );+ }+ return 1;+ });+ }+ elsif ( $opt_count ) {+ my $matches_for_this_file = count_matches_in_resource( $resource );++ if ( not $opt_show_filename ) {+ $total_count += $matches_for_this_file;+ next RESOURCES;+ }++ if ( !$opt_l || $matches_for_this_file > 0) {+ if ( $opt_show_filename ) {+ App::Ack::print( $resource->name, ':', $matches_for_this_file, $ors );+ }+ else {+ App::Ack::print( $matches_for_this_file, $ors );+ }+ }+ }+ elsif ( $opt_l || $opt_L ) {+ my $is_match = resource_has_match( $resource );++ if ( $opt_L ? !$is_match : $is_match ) {+ App::Ack::print( $resource->name, $ors );+ ++$nmatches;++ last RESOURCES if $only_first;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ }+ else {+ $nmatches += print_matches_in_resource( $resource, $opt );+ if ( $nmatches && $only_first ) {+ last RESOURCES;+ }+ }+ }++ if ( $opt_count && !$opt_show_filename ) {+ App::Ack::print( $total_count, "\n" );+ }++ close $App::Ack::fh;+ App::Ack::exit_from_ack( $nmatches );+}++=pod++=encoding UTF-8++=head1 NAME++ack - grep-like text finder++=head1 SYNOPSIS++ ack [options] PATTERN [FILE...]+ ack -f [options] [DIRECTORY...]++=head1 DESCRIPTION++ack is designed as an alternative to F<grep> for programmers.++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+of their behavior, please consult+L<http://perldoc.perl.org/perlreref.html|perlreref>. If you don't know+how to use regular expression but are interested in learning, you may+consult L<http://perldoc.perl.org/perlretut.html|perlretut>. If you do not+need or want ack to use regular expressions, please see the+C<-Q>/C<--literal> option.++Ack can also list files that would be searched, without actually+searching them, to let you take advantage of ack's file-type filtering+capabilities.++=head1 FILE SELECTION++If files are not specified for searching, either on the command+line or piped in with the C<-x> option, I<ack> delves into+subdirectories selecting files for searching.++I<ack> is intelligent about the files it searches. It knows about+certain file types, based on both the extension on the file and,+in some cases, the contents of the file. These selections can be+made with the B<--type> option.++With no file selection, I<ack> searches through regular files that+are not explicitly excluded by B<--ignore-dir> and B<--ignore-file>+options, either present in F<ackrc> files or on the command line.++The default options for I<ack> ignore certain files and directories. These+include:++=over 4++=item * Backup files: Files matching F<#*#> or ending with F<~>.++=item * Coredumps: Files matching F<core.\d+>++=item * Version control directories like F<.svn> and F<.git>.++=back++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<--ackrc>++Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=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>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> suppresses+the color. This is on by default unless the output is redirected.++On Windows, this option is off by default unless the+L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>+environment variable is used.++=item B<--color-filename=I<color>>++Sets the color to be used for filenames.++=item B<--color-match=I<color>>++Sets the color to be used for matches.++=item B<--color-lineno=I<color>>++Sets the color to be used for line numbers.++=item B<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are separated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were receiving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++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>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=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.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=item B<--cathy>++Chocolate, Chocolate, Chocolate!++=back++=head1 THE .ackrc FILE++The F<.ackrc> file contains command-line options that are prepended+to the command line before processing. Multiple options may live+on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>+might look like this:++ # Always sort the files+ --sort-files++ # Always color, even if piping to another program+ --color++ # Use "less -r" as my pager+ --pager=less -r++Note that arguments with spaces in them do not need to be quoted,+as they are not interpreted by the shell. Basically, each I<line>+in the F<.ackrc> file is interpreted as one element of C<@ARGV>.++F<ack> looks in several locations for F<.ackrc> files; the searching+process is detailed in L</"ACKRC LOCATION SEMANTICS">. These+files are not considered if B<--noenv> is specified on the command line.++=head1 Defining your own types++ack allows you to define your own types in addition to the predefined+types. This is done with command line options that are best put into+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,+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+files as well when searching for --perl files? I<ack --type-add perl:ext:xs --perl foo>+does this for you. B<--type-add> appends+additional extensions to an existing type.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --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>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 AVAILABLE COLORS++F<ack> uses the colors available in Perl's L<Term::ANSIColor> module, which+provides the following listed values. Note that case does not matter when using+these values.++=head2 Foreground colors++ black red green yellow blue magenta cyan white++ bright_black bright_red bright_green bright_yellow+ bright_blue bright_magenta bright_cyan bright_white++=head2 Background colors++ on_black on_red on_green on_yellow+ on_blue on_magenta on_cyan on_white++ on_bright_black on_bright_red on_bright_green on_bright_yellow+ on_bright_blue on_bright_magenta on_bright_cyan on_bright_white++=head1 ACK & OTHER TOOLS++=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>:++ set grepprg=ack\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++=head2 Editor integration++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++For greater compatibility with I<grep>, I<ack> in normal use returns+shell return or exit code of 0 only if something is found and 1 if+no match is found.++(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)++The I<grep> code 2 for errors is not used.++If C<-f> or C<-g> are specified, then 0 is returned if at least one+file is found. If no files are found, then 1 is returned.++=cut++=head1 DEBUGGING ACK PROBLEMS++If ack gives you output you're not expecting, start with a few simple steps.++=head2 Use B<--noenv>++Your environment variables and F<.ackrc> may be doing things you're+not expecting, or forgotten you specified. Use B<--noenv> to ignore+your environment and F<.ackrc>.++=head2 Use B<-f> to see what files have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=head1 COMMUNITY++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)?++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, 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?++No, ack will always be read-only. Perl has a perfectly good way+to do search & replace in files, using the C<-i>, C<-p> and C<-n>+switches.++You can certainly use ack to select your files to update. For+example, to change all "foo" to "bar" in all PHP files, you can do+this from the Unix shell:++ $ perl -i -p -e's/foo/bar/g' $(ack -f --php)++=head2 Can I make ack recognize F<.xyz> files?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?++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+specifies the sources Ack looks for configuration files; each one+that is found is loaded in the order specified here, and+each one overrides options set in any of the sources preceding+it. (For example, if I set --sort-files in my user ackrc, and+--nosort-files on the command line, the command line takes+precedence)++=over 4++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems.++Under Windows XP and earlier, the global ackrc is at+C<C:\Documents and Settings\All Users\Application Data\ackrc>++Under Windows Vista/7, the global ackrc is at+C<C:\ProgramData\ackrc>++The C<--noenv> option prevents all ackrc files from being loaded.++=item * User ackrc++Options are then loaded from the user's ackrc. This is located at+C<$HOME/.ackrc> on Unix-like systems.++Under Windows XP and earlier, the user's ackrc is at+C<C:\Documents and Settings\$USER\Application Data\ackrc>.++Under Windows Vista/7, the user's ackrc is at+C<C:\Users\$USER\AppData\Roaming\ackrc>.++If you want to load a different user-level ackrc, it may be specified+with the C<$ACKRC> environment variable.++The C<--noenv> option prevents all ackrc files from being loaded.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * --ackrc++The C<--ackrc> option may be included on the command line to specify an+ackrc file that can override all others. It is consulted even if C<--noenv>+is present.++=item * ACK_OPTIONS++Options are then loaded from the environment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by contrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/beyondgrep/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+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/beyondgrep/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<https://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/beyondgrep/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * MetaCPAN++L<http://metacpan.org/release/ack>++=item * Git source repository++L<https://github.com/beyondgrep/ack2>++=back++=head1 ACKNOWLEDGEMENTS++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,+Denis Howe,+RaE<uacute>l GundE<iacute>n,+James McCoy,+Daniel Perrett,+Steven Lee,+Jonathan Perret,+Fraser Tweedale,+RaE<aacute>l GundE<aacute>n,+Steffen Jaeckel,+Stephan Hohe,+Michael Beijen,+Alexandr Ciornii,+Christian Walde,+Charles Lee,+Joe McMahon,+John Warwick,+David Steinbrunner,+Kara Martens,+Volodymyr Medvid,+Ron Savage,+Konrad Borowski,+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++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.++See http://www.perlfoundation.org/artistic_license_2_0 or the LICENSE.md+file that comes with the ack distribution.++=cut+package App::Ack;++use warnings;+use strict;+++our $VERSION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.22';+ $COPYRIGHT = 'Copyright 2005-2017 Andy Lester.';+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin' || $^O eq 'msys');+ $is_windows = ($^O eq 'MSWin32');+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}++++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}++++sub warn {+ return CORE::warn( _my_program(), ': ', @_, "\n" );+}+++sub die {+ return CORE::die( _my_program(), ': ', @_, "\n" );+}++sub _my_program {+ require File::Basename;+ return File::Basename::basename( $0 );+}++++sub filetypes_supported {+ return keys %mappings;+}++sub thpppt {+ my $y = q{_ /|,\\'!.x',=(www)=, U };+ $y =~ tr/,x!w/\nOo_/;++ App::Ack::print( "$y ack $_[0]!\n" );+ exit 0;+}++sub ackbar {+ my $x;+ $x = <<'_BAR';+ 6?!I'7!I"?%+!+ 3~!I#7#I"7#I!?!+!="+"="+!:!+ 2?#I!7!I!?#I!7!I"+"=%+"=#+ 1?"+!?*+!=#~"=!+#?"="+!+ 0?"+!?"I"?&+!="~!=!~"=!+%="+"+ /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!+ .?%I"?%+%='?!=#~$="+ ,,!?%I"?(+$=$~!=#:"~$:!~!+ ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!+ +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"+ *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!+ *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"+ )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!+ (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!+ (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!+ %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"+ $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/+ $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!+ $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/+ %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!+ %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!+ %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!+ %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!+ &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!+ &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!+ &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!+ &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!+ &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"+ &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!+ %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&+ %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)+ $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!+ $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"+ "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!+ !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!+ !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*+7+:!,!~#,"=!7'I!?#I"7/+!7++77I!+!7!?!7!I"71+!7,+_BAR++ return _pic_decode($x);+}++sub cathy {+ my $x = <<'CATHY';+ 0+!--+!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! $A"C!K!!! $|!+ 0+!--+!+ 6\! 1:!,!.! !+ 7\! /.!M!~!Z!M!~!+ 8\! /~!D! "M! !+ 4.! $\! /M!~!.!8! +.!M# 4+ 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3+ /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4+ ..! !D!Z!.!Z!.! '\! 9=!M".! 6+ /.! !.!~!M".! '\! 8~! 9+ 4M!.! /.!7!N!M!.! F+ 4.! &:!M! !N"M# !M"N!M! #D!M&=! =+ :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;+ 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9+ (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0+ &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! ++ &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *+ 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! ++ 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4+ 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3+ /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1+ #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0+ $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '+ #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (+ #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %+ *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &+ )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '+ )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! ++ (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! ++ 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *+ %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )+ !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (+ !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &+ !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $+ !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $+ (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #+ '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %+ $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! ++ ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )+ +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (+ 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )+ 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /+ 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2+ 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %+ 0?!$! &N! )." .,! %."M! ":!M!.! 0+ 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0+CATHY+ return _pic_decode($x);+}++sub _pic_decode {+ my($compressed) = @_;+ $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;+ App::Ack::print( $compressed );+ exit 0;+}+++sub show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after+ matching lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before+ matching lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For+ example, --pager="less -R". Ignored if output+ is redirected.+ --nopager Do not send output through a pager. Cancels+ any setting in ~/.ackrc, ACK_PAGER or+ ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's+ results. (default: on when used interactively)+ --[no]break Print a break between results from different+ files. (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line+ numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without+ searching. The PATTERN must not be specified.+ -g Same as -f, but only select files matching+ PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/remove directory from list of ignored dirs+ --[no]ignore-directory=name Synonym for ignore-dir+ --ignore-file=filter Add filter for ignoring files+ -r, -R, --recurse Recurse into subdirectories (default: on)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files of types that ack recognizes.++ --type=X Include only X files, where X is a recognized+ filetype.+ --type=noX Exclude X files.+ See "ack --help-types" for supported filetypes.++File type specification:+ --type-set TYPE:FILTER:FILTERARGS+ Files with the given FILTERARGS applied to the+ given FILTER are recognized as being of type+ TYPE. This replaces an existing definition for+ type TYPE.+ --type-add TYPE:FILTER:FILTERARGS+ Files with the given FILTERARGS applied to the+ given FILTER are recognized as being type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc+ files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore default definitions included with ack.+ --create-ackrc Outputs a default ackrc for your customization+ to standard output.+ --help, -? This help+ --help-types Display all known types+ --dump Dump information on which options are loaded+ from which RC files+ --[no]filter Force ack to treat standard input as a pipe+ (--filter) or tty (--nofilter)+ --man Man page+ --version Display version & copyright+ --thpppt Bill the Cat+ --bar The warning admiral+ --cathy Chocolate! Chocolate! Chocolate!++Exit status is 0 if match, 1 if no match.++ack's home page is at https://beyondgrep.com/++The full ack manual is available by running "ack --man".++This is version $VERSION of ack. Run "ack --version" for full version info.+END_OF_HELP++ return;+ }++++sub show_help_types {+ App::Ack::print( <<'END_OF_HELP' );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ return <<"END_OF_VERSION";+ack ${VERSION}+Running under Perl $ver at $this_perl++$copyright++This program is free software. You may modify or distribute it+under the terms of the Artistic License v2.0.+END_OF_VERSION+}+++sub print_version_statement {+ App::Ack::print( get_version_statement() );++ return;+}+++sub get_copyright {+ return $COPYRIGHT;+}+++sub print { print {$fh} @_; return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';+++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 open {+ 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 {+ 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 {+ 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 ) = @_;++ 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;+package App::Ack::Resources;++++use Errno qw(EACCES);++use warnings;+use strict;++sub _generate_error_handler {+ my $opt = shift;++ if ( $opt->{dont_report_bad_filenames} ) {+ return sub {+ my $msg = shift;+ if ( $! == EACCES ) {+ return;+ }+ App::Ack::warn( $msg );+ };+ }+ else {+ return sub {+ my $msg = shift;+ App::Ack::warn( $msg );+ };+ }+}+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => _generate_error_handler($opt),+ warning_handler => sub {},+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => _generate_error_handler($opt),+ warning_handler => _generate_error_handler($opt),+ sort_files => $opt->{sort_files},+ }, $file ) or return undef;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource->new( $file );+}++1;+package App::Ack::ConfigDefault;++use warnings;+use strict;+++++sub options {+ return split( /\n/, _options_block() );+}+++sub options_clean {+ return grep { /./ && !/^#/ } options();+}+++sub _options_block {+ my $lines = <<'HERE';+# This is the default ackrc for ack version ==VERSION==.++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++### Directories to ignore++# Bazaar+# http://bazaar.canonical.com/+--ignore-directory=is:.bzr++# Codeville+# http://freecode.com/projects/codeville+--ignore-directory=is:.cdv++# Interface Builder (Xcode)+# http://en.wikipedia.org/wiki/Interface_Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# 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/+--ignore-directory=is:.hg++# quilt+# http://directory.fsf.org/wiki/Quilt+--ignore-directory=is:.pc++# Subversion+# http://subversion.tigris.org/+--ignore-directory=is:.svn++# Monotone+# http://www.monotone.ca/+--ignore-directory=is:_MTN++# CVS+# http://savannah.nongnu.org/projects/cvs+--ignore-directory=is:CVS++# RCS+# http://www.gnu.org/software/rcs/+--ignore-directory=is:RCS++# SCCS+# http://en.wikipedia.org/wiki/Source_Code_Control_System+--ignore-directory=is:SCCS++# darcs+# http://darcs.net/+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+# http://www.gnu.org/software/autoconf/+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+# https://metacpan.org/release/Devel-Cover+--ignore-directory=is:cover_db++# Node modules created by npm+--ignore-directory=is:node_modules++# CMake cache+# http://www.cmake.org/+--ignore-directory=is:CMakeFiles++# Eclipse workspace folder+# http://eclipse.org/+--ignore-directory=is:.metadata++# Cabal (Haskell) sandboxes+# http://www.haskell.org/cabal/users-guide/installing-packages.html+--ignore-directory=is:.cabal-sandbox++### Files to ignore++# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files http://vim.org/+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.-]min[.]js$/+--ignore-file=match:/[.]js[.]min$/++# minified CSS+--ignore-file=match:/[.]min[.]css$/+--ignore-file=match:/[.]css[.]min$/++# JS and CSS source maps+--ignore-file=match:/[.]js[.]map$/+--ignore-file=match:/[.]css[.]map$/++# PDFs, because they pass Perl's -T detection+--ignore-file=ext:pdf++# Common graphics, just as an optimization+--ignore-file=ext:gif,jpg,jpeg,png+++### Filetypes defined++# Perl+# http://perl.org/+--type-add=perl:ext:pl,pm,pod,t,psgi+--type-add=perl:firstlinematch:/^#!.*\bperl/++# Perl tests+--type-add=perltest:ext:t++# Makefiles+# http://www.gnu.org/s/make/+--type-add=make:ext:mk+--type-add=make:ext:mak+--type-add=make:is:makefile+--type-add=make:is:Makefile+--type-add=make:is:Makefile.Debug+--type-add=make:is:Makefile.Release++# Rakefiles+# http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake+# http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada+# http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP+# http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net+# http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion+# http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure+# http://clojure.org/+--type-add=clojure:ext:clj,cljs,edn,cljc++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# CoffeeScript+# http://coffeescript.org/+--type-add=coffeescript:ext:coffee++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C++ header files+--type-add=hpp:ext:hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS+# http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Dart+# http://www.dartlang.org/+--type-add=dart:ext:dart++# Delphi+# http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# Elixir+# http://elixir-lang.org/+--type-add=elixir:ext:ex,exs++# Emacs Lisp+# http://www.gnu.org/software/emacs+--type-add=elisp:ext:el++# Erlang+# http://www.erlang.org/+--type-add=erlang:ext:erl,hrl++# Fortran+# http://en.wikipedia.org/wiki/Fortran+--type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp++# Go+# http://golang.org/+--type-add=go:ext:go++# Groovy+# 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,xhtml++# Jade+# http://jade-lang.com/+--type-add=jade:ext:jade++# Java+# http://www.oracle.com/technetwork/java/index.html+--type-add=java:ext:java,properties++# JavaScript+--type-add=js:ext:js++# JSP+# http://www.oracle.com/technetwork/java/javaee/jsp/index.html+--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++# Common Lisp+# http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua+# http://www.lua.org/+--type-add=lua:ext:lua+--type-add=lua:firstlinematch:/^#!.*\blua(jit)?/++# Objective-C+--type-add=objc:ext:m,h++# Objective-C+++--type-add=objcpp:ext:mm,h++# OCaml+# http://caml.inria.fr/+--type-add=ocaml:ext:ml,mli,mll,mly++# Matlab+# http://en.wikipedia.org/wiki/MATLAB+--type-add=matlab:ext:m++# Parrot+# http://www.parrot.org/+--type-add=parrot:ext:pir,pasm,pmc,ops,pod,pg,tg++# PHP+# http://www.php.net/+--type-add=php:ext:php,phpt,php3,php4,php5,phtml+--type-add=php:firstlinematch:/^#!.*\bphp/++# Plone+# http://plone.org/+--type-add=plone:ext:pt,cpt,metadata,cpy,py++# Python+# http://www.python.org/+--type-add=python:ext:py+--type-add=python:firstlinematch:/^#!.*\bpython/++# R+# http://www.r-project.org/+--type-add=rr:ext:R++# reStructured Text+# http://docutils.sourceforge.net/rst.html+--type-add=rst:ext:rst++# Ruby+# http://www.ruby-lang.org/+--type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec+--type-add=ruby:is:Rakefile+--type-add=ruby:firstlinematch:/^#!.*\bruby/++# Rust+# http://www.rust-lang.org/+--type-add=rust:ext:rs++# Sass+# http://sass-lang.com+--type-add=sass:ext:sass,scss++# Scala+# http://www.scala-lang.org/+--type-add=scala:ext:scala++# Scheme+# http://groups.csail.mit.edu/mac/projects/scheme/+--type-add=scheme:ext:scm,ss++# Shell+--type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/++# Smalltalk+# http://www.smalltalk.org/+--type-add=smalltalk:ext:st++# Smarty+# http://www.smarty.net/+--type-add=smarty:ext:tpl++# SQL+# http://www.iso.org/iso/catalogue_detail.htm?csnumber=45498+--type-add=sql:ext:sql,ctl++# Stylus+# 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++# LaTeX+# http://www.latex-project.org/+--type-add=tex:ext:tex,cls,sty++# Template Toolkit (Perl)+# http://template-toolkit.org/+--type-add=tt:ext:tt,tt2,ttml++# Visual Basic+--type-add=vb:ext:bas,cls,frm,ctl,vb,resx++# Verilog+--type-add=verilog:ext:v,vh,sv++# VHDL+# http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome+--type-add=vhdl:ext:vhd,vhdl++# Vim+# http://www.vim.org/+--type-add=vim:ext:vim++# XML+# http://www.w3.org/TR/REC-xml/+--type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl+--type-add=xml:firstlinematch:/<[?]xml/++# YAML+# http://yaml.org/+--type-add=yaml:ext:yaml,yml+HERE+ $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;++ return $lines;+}++1;+package App::Ack::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++use if ($^O eq 'MSWin32'), 'Win32';+++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}+++sub _remove_redundancies {+ my @configs = @_;++ my %seen;+ foreach my $config (@configs) {+ my $key = $config->{path};+ if ( not $App::Ack::is_windows ) {+ # On Unix, uniquify on inode.+ my ($dev, $inode) = (stat $key)[0, 1];+ $key = "$dev:$inode" if defined $dev;+ }+ undef $config if $seen{$key}++;+ }+ return grep { defined } @configs;+}+++sub _check_for_ackrc {+ return unless defined $_[0];++ my @files = grep { -f }+ map { File::Spec->catfile(@_, $_) }+ qw(.ackrc _ackrc);++ die File::Spec->catdir(@_) . " contains both .ackrc and _ackrc.\n" .+ "Please remove one of those files.\n"+ if @files > 1;++ return wantarray ? @files : $files[0];+} # end _check_for_ackrc++++sub find_config_files {+ my @config_files;++ if ( $App::Ack::is_windows ) {+ push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (+ Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),+ Win32::GetFolderPath(Win32::CSIDL_APPDATA()),+ );+ }+ else {+ push @config_files, { path => '/etc/ackrc' };+ }+++ if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {+ push @config_files, { path => $ENV{'ACKRC'} };+ }+ else {+ push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});+ }++ 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 );+ while(@dirs) {+ my $ackrc = _check_for_ackrc(@dirs);+ if(defined $ackrc) {+ push @config_files, { project => 1, path => $ackrc };+ last;+ }+ pop @dirs;+ }++ # We only test for existence here, so if the file is deleted out from under us, this will fail later.+ return _remove_redundancies( @config_files );+}++++sub read_rcfile {+ my $file = shift;++ return unless defined $file && -e $file;++ my @lines;++ open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" );+ while ( my $line = <$fh> ) {+ chomp $line;+ $line =~ s/^\s+//;+ $line =~ s/\s+$//;++ next if $line eq '';+ next if $line =~ /^\s*#/;++ push( @lines, $line );+ }+ close $fh or App::Ack::die( "Unable to close $file: $!" );++ return @lines;+}++1;+package App::Ack::ConfigLoader;++use strict;+use warnings;++use Carp 1.04 ();+use Getopt::Long 2.38 ();+use Text::ParseWords 3.1 ();+++my @INVALID_COMBINATIONS;++BEGIN {+ my @context = qw( -A -B -C --after-context --before-context --context );+ my @pretty = qw( --heading --group --break );+ my @filename = qw( -h -H --with-filename --no-filename );++ @INVALID_COMBINATIONS = (+ # XXX normalize+ [qw(-l)] => [@context, @pretty, @filename, qw(-L -o --passthru --output --max-count --column -f -g --show-types)],+ [qw(-L)] => [@context, @pretty, @filename, qw(-l -o --passthru --output --max-count --column -f -g --show-types -c --count)],+ [qw(--line)] => [@context, @pretty, @filename, qw(-l --files-with-matches --files-without-matches -L -o --passthru --match -m --max-count -1 -c --count --column --print0 -f -g --show-types)],+ [qw(-o)] => [@context, qw(--output -c --count --column --column -f --show-types)],+ [qw(--passthru)] => [@context, qw(--output --column -m --max-count -1 -c --count -f -g)],+ [qw(--output)] => [@context, qw(-c --count -f -g)],+ [qw(--match)] => [qw(-f -g)],+ [qw(-m --max-count)] => [qw(-1 -f -g -c --count)],+ [qw(-h --no-filename)] => [qw(-H --with-filename -f -g --group --heading)],+ [qw(-H --with-filename)] => [qw(-h --no-filename -f -g)],+ [qw(-c --count)] => [@context, @pretty, qw(--column -f -g)],+ [qw(--column)] => [qw(-f -g)],+ [@context] => [qw(-f -g)],+ [qw(-f)] => [qw(-g), @pretty],+ [qw(-g)] => [qw(-f), @pretty],+ );+}++sub _generate_ignore_dir {+ my ( $option_name, $opt ) = @_;++ my $is_inverted = $option_name =~ /^--no/;++ return sub {+ my ( undef, $dir ) = @_;++ $dir = App::Ack::remove_dir_sep( $dir );+ if ( $dir !~ /:/ ) {+ $dir = 'is:' . $dir;+ }++ my ( $filter_type, $args ) = split /:/, $dir, 2;++ if ( $filter_type eq 'firstlinematch' ) {+ Carp::croak( qq{Invalid filter specification "$filter_type" for option '$option_name'} );+ }++ my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));+ my $collection;++ my $previous_inversion_matches = $opt->{idirs} && !($is_inverted xor $opt->{idirs}[-1]->is_inverted());++ if ( $previous_inversion_matches ) {+ $collection = $opt->{idirs}[-1];++ if ( $is_inverted ) {+ # XXX this relies on invert of an inverted filter+ # to return the original+ $collection = $collection->invert()+ }+ }+ else {+ $collection = App::Ack::Filter::Collection->new();++ if ( $is_inverted ) {+ push @{ $opt->{idirs} }, $collection->invert();+ }+ else {+ push @{ $opt->{idirs} }, $collection;+ }+ }++ $collection->add($filter);++ if ( $filter_type eq 'is' ) {+ $collection->add(App::Ack::Filter::IsPath->new($args));+ }+ };+}++sub process_filter_spec {+ my ( $spec ) = @_;++ if ( $spec =~ /^(\w+):(\w+):(.*)/ ) {+ my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 );++ return ( $type_name,+ App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) );+ }+ elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification.+ my ( $type_name, $extensions ) = ( $1, $2 );++ my @extensions = split(/,/, $extensions);+ foreach my $extension ( @extensions ) {+ $extension =~ s/^[.]//;+ }++ return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) );+ }+ else {+ Carp::croak "invalid filter specification '$spec'";+ }+}+++sub uninvert_filter {+ my ( $opt, @filters ) = @_;++ return unless defined $opt->{filters} && @filters;++ # Loop through all the registered filters. If we hit one that+ # matches this extension and it's inverted, we need to delete it from+ # the options.+ for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) {+ my $opt_filter = @{ $opt->{filters} }[$i];++ # XXX Do a real list comparison? This just checks string equivalence.+ if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) {+ splice @{ $opt->{filters} }, $i, 1;+ $i--;+ }+ }++ return;+}+++sub process_filetypes {+ my ( $opt, $arg_sources ) = @_;++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version'); # start with default options, minus some annoying ones+ Getopt::Long::Configure(+ 'no_ignore_case',+ 'no_auto_abbrev',+ 'pass_through',+ );+ my %additional_specs;++ my $add_spec = sub {+ my ( undef, $spec ) = @_;++ my ( $name, $filter ) = process_filter_spec($spec);++ push @{ $App::Ack::mappings{$name} }, $filter;++ $additional_specs{$name . '!'} = sub {+ my ( undef, $value ) = @_;++ my @filters = @{ $App::Ack::mappings{$name} };+ if ( not $value ) {+ @filters = map { $_->invert() } @filters;+ }+ else {+ uninvert_filter( $opt, @filters );+ }++ push @{ $opt->{'filters'} }, @filters;+ };+ };++ my $set_spec = sub {+ my ( undef, $spec ) = @_;++ my ( $name, $filter ) = process_filter_spec($spec);++ $App::Ack::mappings{$name} = [ $filter ];++ $additional_specs{$name . '!'} = sub {+ my ( undef, $value ) = @_;++ my @filters = @{ $App::Ack::mappings{$name} };+ if ( not $value ) {+ @filters = map { $_->invert() } @filters;+ }++ push @{ $opt->{'filters'} }, @filters;+ };+ };++ my $delete_spec = sub {+ my ( undef, $name ) = @_;++ delete $App::Ack::mappings{$name};+ delete $additional_specs{$name . '!'};+ };++ my %type_arg_specs = (+ 'type-add=s' => $add_spec,+ 'type-set=s' => $set_spec,+ 'type-del=s' => $delete_spec,+ );++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ if ( ref($args) ) {+ # $args are modified in place, so no need to munge $arg_sources+ local @ARGV = @{$args};+ Getopt::Long::GetOptions(%type_arg_specs);+ @{$args} = @ARGV;+ }+ else {+ ( undef, $source->{contents} ) =+ Getopt::Long::GetOptionsFromString($args, %type_arg_specs);+ }+ }++ $additional_specs{'k|known-types'} = sub {+ my ( undef, $value ) = @_;++ my @filters = map { @{$_} } values(%App::Ack::mappings);++ push @{ $opt->{'filters'} }, @filters;+ };++ return \%additional_specs;+}+++sub removed_option {+ my ( $option, $explanation ) = @_;++ $explanation ||= '';+ return sub {+ warn "Option '$option' is not valid in ack 2.\n$explanation";+ exit 1;+ };+}+++sub get_arg_spec {+ my ( $opt, $extra_specs ) = @_;++ my $dash_a_explanation = <<'EOT';+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:-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},+ c => \$opt->{count},+ 'color|colour!' => \$opt->{color},+ 'color-match=s' => \$ENV{ACK_COLOR_MATCH},+ 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},+ 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},+ 'column!' => \$opt->{column},+ count => \$opt->{count},+ 'create-ackrc' => sub { print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; },+ 'env!' => sub {+ my ( undef, $value ) = @_;++ if ( !$value ) {+ $opt->{noenv_seen} = 1;+ }+ },+ f => \$opt->{f},+ 'files-from=s' => \$opt->{files_from},+ 'filter!' => \$App::Ack::is_filter_mode,+ flush => \$opt->{flush},+ 'follow!' => \$opt->{follow},+ g => \$opt->{g},+ G => removed_option('-G'),+ 'group!' => sub { shift; $opt->{heading} = $opt->{break} = shift },+ 'heading!' => \$opt->{heading},+ 'h|no-filename' => \$opt->{h},+ 'H|with-filename' => \$opt->{H},+ 'i|ignore-case' => \$opt->{i},+ 'ignore-directory|ignore-dir=s' => _generate_ignore_dir('--ignore-dir', $opt),+ 'ignore-file=s' => sub {+ my ( undef, $file ) = @_;++ my ( $filter_type, $args ) = split /:/, $file, 2;++ my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));++ if ( !$opt->{ifiles} ) {+ $opt->{ifiles} = App::Ack::Filter::Collection->new();+ }+ $opt->{ifiles}->add($filter);+ },+ 'lines=s' => sub { shift; my $val = shift; push @{$opt->{lines}}, $val },+ 'l|files-with-matches'+ => \$opt->{l},+ 'L|files-without-matches'+ => \$opt->{L},+ 'm|max-count=i' => \$opt->{m},+ 'match=s' => \$opt->{regex},+ 'n|no-recurse' => \$opt->{n},+ o => sub { $opt->{output} = '$&' },+ 'output=s' => \$opt->{output},+ 'pager:s' => sub {+ my ( undef, $value ) = @_;++ $opt->{pager} = $value || $ENV{PAGER};+ },+ 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt),+ 'nopager' => sub { $opt->{pager} = undef },+ 'passthru' => \$opt->{passthru},+ 'print0' => \$opt->{print0},+ 'Q|literal' => \$opt->{Q},+ 'r|R|recurse' => sub { $opt->{n} = 0 },+ 's' => \$opt->{dont_report_bad_filenames},+ 'show-types' => \$opt->{show_types},+ 'smart-case!' => \$opt->{smart_case},+ 'sort-files' => \$opt->{sort_files},+ 'type=s' => sub {+ my ( $getopt, $value ) = @_;++ my $cb_value = 1;+ if ( $value =~ s/^no// ) {+ $cb_value = 0;+ }++ my $callback = $extra_specs->{ $value . '!' };++ if ( $callback ) {+ $callback->( $getopt, $cb_value );+ }+ else {+ Carp::croak( "Unknown type '$value'" );+ }+ },+ 'u' => removed_option('-u'),+ 'unrestricted' => removed_option('--unrestricted'),+ 'v|invert-match' => \$opt->{v},+ 'w|word-regexp' => \$opt->{w},+ 'x' => sub { $opt->{files_from} = '-' },++ 'version' => sub { App::Ack::print_version_statement(); exit; },+ 'help|?:s' => sub { shift; App::Ack::show_help(@_); exit; },+ 'help-types' => sub { App::Ack::show_help_types(); exit; },+ 'man' => sub { App::Ack::show_man(); exit; },+ $extra_specs ? %{$extra_specs} : (),+ }; # arg_specs+}+++sub process_other {+ my ( $opt, $extra_specs, $arg_sources ) = @_;++ # Start with default options, minus some annoying ones.+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure(+ 'bundling',+ 'no_ignore_case',+ );++ my $argv_source;+ my $is_help_types_active;++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ if ( $source_name eq 'ARGV' ) {+ $argv_source = $args;+ last;+ }+ }++ if ( $argv_source ) { # This *should* always be true, but you never know...+ my @copy = @{$argv_source};+ local @ARGV = @copy;++ Getopt::Long::Configure('pass_through');++ Getopt::Long::GetOptions(+ 'help-types' => \$is_help_types_active,+ );++ Getopt::Long::Configure('no_pass_through');+ }++ my $arg_specs = get_arg_spec($opt, $extra_specs);++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ my $args_for_source = $arg_specs;++ if ( $source->{project} ) {+ my $illegal = sub {+ die "Options --output, --pager and --match are forbidden in project .ackrc files.\n";+ };++ $args_for_source = {+ %{$args_for_source},+ 'output=s' => $illegal,+ 'pager:s' => $illegal,+ 'match=s' => $illegal,+ };+ }++ my $ret;+ if ( ref($args) ) {+ local @ARGV = @{$args};+ $ret = Getopt::Long::GetOptions( %{$args_for_source} );+ @{$args} = @ARGV;+ }+ else {+ ( $ret, $source->{contents} ) =+ Getopt::Long::GetOptionsFromString( $args, %{$args_for_source} );+ }+ if ( !$ret ) {+ if ( !$is_help_types_active ) {+ my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name";+ App::Ack::die( "Invalid option $where" );+ }+ }+ if ( $opt->{noenv_seen} ) {+ App::Ack::die( "--noenv found in $source_name" );+ }+ }++ # XXX We need to check on a -- in the middle of a non-ARGV source++ return;+}+++sub should_dump_options {+ my ( $sources ) = @_;++ foreach my $source (@{$sources}) {+ my ( $name, $options ) = @{$source}{qw/name contents/};++ if($name eq 'ARGV') {+ my $dump;+ local @ARGV = @{$options};+ Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');+ Getopt::Long::GetOptions(+ 'dump' => \$dump,+ );+ @{$options} = @ARGV;+ return $dump;+ }+ }+ return;+}+++sub explode_sources {+ my ( $sources ) = @_;++ my @new_sources;++ Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');++ my %opt;+ my $arg_spec = get_arg_spec(\%opt);++ my $add_type = sub {+ my ( undef, $arg ) = @_;++ # XXX refactor?+ if ( $arg =~ /(\w+)=/) {+ $arg_spec->{$1} = sub {};+ }+ else {+ ( $arg ) = split /:/, $arg;+ $arg_spec->{$arg} = sub {};+ }+ };++ my $del_type = sub {+ my ( undef, $arg ) = @_;++ delete $arg_spec->{$arg};+ };++ foreach my $source (@{$sources}) {+ my ( $name, $options ) = @{$source}{qw/name contents/};+ if ( ref($options) ne 'ARRAY' ) {+ $source->{contents} = $options =+ [ Text::ParseWords::shellwords($options) ];+ }++ for my $j ( 0 .. @{$options}-1 ) {+ next unless $options->[$j] =~ /^-/;+ my @chunk = ( $options->[$j] );+ push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/;+ $j--;++ my @copy = @chunk;+ local @ARGV = @chunk;+ Getopt::Long::GetOptions(+ 'type-add=s' => $add_type,+ 'type-set=s' => $add_type,+ 'type-del=s' => $del_type,+ );+ Getopt::Long::GetOptions( %{$arg_spec} );++ push @new_sources, {+ name => $name,+ contents => \@copy,+ };+ }+ }++ return \@new_sources;+}+++sub compare_opts {+ my ( $a, $b ) = @_;++ my $first_a = $a->[0];+ my $first_b = $b->[0];++ $first_a =~ s/^--?//;+ $first_b =~ s/^--?//;++ return $first_a cmp $first_b;+}+++sub dump_options {+ my ( $sources ) = @_;++ $sources = explode_sources($sources);++ my %opts_by_source;+ my @source_names;++ foreach my $source (@{$sources}) {+ my ( $name, $contents ) = @{$source}{qw/name contents/};+ if ( not $opts_by_source{$name} ) {+ $opts_by_source{$name} = [];+ push @source_names, $name;+ }+ push @{$opts_by_source{$name}}, $contents;+ }++ foreach my $name (@source_names) {+ my $contents = $opts_by_source{$name};++ print $name, "\n";+ print '=' x length($name), "\n";+ print ' ', join(' ', @{$_}), "\n" foreach sort { compare_opts($a, $b) } @{$contents};+ }++ return;+}+++sub remove_default_options_if_needed {+ my ( $sources ) = @_;++ my $default_index;++ foreach my $index ( 0 .. $#{$sources} ) {+ if ( $sources->[$index]{'name'} eq 'Defaults' ) {+ $default_index = $index;+ last;+ }+ }++ return $sources unless defined $default_index;++ my $should_remove = 0;++ # Start with default options, minus some annoying ones.+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure(+ 'no_ignore_case',+ 'no_auto_abbrev',+ 'pass_through',+ );++ foreach my $index ( $default_index + 1 .. $#{$sources} ) {+ my ( $name, $args ) = @{$sources->[$index]}{qw/name contents/};++ if (ref($args)) {+ local @ARGV = @{$args};+ Getopt::Long::GetOptions(+ 'ignore-ack-defaults' => \$should_remove,+ );+ @{$args} = @ARGV;+ }+ else {+ ( undef, $sources->[$index]{contents} ) = Getopt::Long::GetOptionsFromString($args,+ 'ignore-ack-defaults' => \$should_remove,+ );+ }+ }++ Getopt::Long::Configure('default');+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ return $sources unless $should_remove;++ my @copy = @{$sources};+ splice @copy, $default_index, 1;+ return \@copy;+}+++sub check_for_mutually_exclusive_options {+ my ( $arg_sources ) = @_;++ my %mutually_exclusive_with;+ my @copy = @{$arg_sources};++ for(my $i = 0; $i < @INVALID_COMBINATIONS; $i += 2) {+ my ( $lhs, $rhs ) = @INVALID_COMBINATIONS[ $i, $i + 1 ];++ foreach my $l_opt ( @{$lhs} ) {+ foreach my $r_opt ( @{$rhs} ) {+ push @{ $mutually_exclusive_with{ $l_opt } }, $r_opt;+ push @{ $mutually_exclusive_with{ $r_opt } }, $l_opt;+ }+ }+ }++ while( @copy ) {+ my %set_opts;++ my $source = shift @copy;+ my ( $source_name, $args ) = @{$source}{qw/name contents/};+ $args = ref($args) ? [ @{$args} ] : [ Text::ParseWords::shellwords($args) ];++ foreach my $opt ( @{$args} ) {+ next unless $opt =~ /^[-+]/;+ last if $opt eq '--';++ if( $opt =~ /^(.*)=/ ) {+ $opt = $1;+ }+ elsif ( $opt =~ /^(-[^-]).+/ ) {+ $opt = $1;+ }++ $set_opts{ $opt } = 1;++ my $mutex_opts = $mutually_exclusive_with{ $opt };++ next unless $mutex_opts;++ foreach my $mutex_opt ( @{$mutex_opts} ) {+ if($set_opts{ $mutex_opt }) {+ die "Options '$mutex_opt' and '$opt' are mutually exclusive\n";+ }+ }+ }+ }++ return;+}+++sub process_args {+ my $arg_sources = \@_;++ my %opt = (+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},+ );++ check_for_mutually_exclusive_options($arg_sources);++ $arg_sources = remove_default_options_if_needed($arg_sources);++ if ( should_dump_options($arg_sources) ) {+ dump_options($arg_sources);+ exit(0);+ }++ my $type_specs = process_filetypes(\%opt, $arg_sources);+ process_other(\%opt, $type_specs, $arg_sources);+ while ( @{$arg_sources} ) {+ my $source = shift @{$arg_sources};+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ # All of our sources should be transformed into an array ref+ if ( ref($args) ) {+ if ( $source_name eq 'ARGV' ) {+ @ARGV = @{$args};+ }+ elsif (@{$args}) {+ Carp::croak "source '$source_name' has extra arguments!";+ }+ }+ else {+ Carp::croak 'The impossible has occurred!';+ }+ }+ my $filters = ($opt{filters} ||= []);++ # Throw the default filter in if no others are selected.+ if ( not grep { !$_->is_inverted() } @{$filters} ) {+ push @{$filters}, App::Ack::Filter::Default->new();+ }+ return \%opt;+}+++sub retrieve_arg_sources {+ my @arg_sources;++ my $noenv;+ my $ackrc;++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure('pass_through');+ Getopt::Long::Configure('no_auto_abbrev');++ Getopt::Long::GetOptions(+ 'noenv' => \$noenv,+ 'ackrc=s' => \$ackrc,+ );++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ my @files;++ if ( !$noenv ) {+ my $finder = App::Ack::ConfigFinder->new;+ @files = $finder->find_config_files;+ }+ if ( $ackrc ) {+ # We explicitly use open so we get a nice error message.+ # XXX This is a potential race condition!.+ if(open my $fh, '<', $ackrc) {+ close $fh;+ }+ else {+ die "Unable to load ackrc '$ackrc': $!"+ }+ push( @files, { path => $ackrc } );+ }++ push @arg_sources, {+ name => 'Defaults',+ contents => [ App::Ack::ConfigDefault::options_clean() ],+ };++ foreach my $file ( @files) {+ my @lines = App::Ack::ConfigFinder::read_rcfile($file->{path});++ if(@lines) {+ push @arg_sources, {+ name => $file->{path},+ contents => \@lines,+ project => $file->{project},+ };+ }+ }++ if ( $ENV{ACK_OPTIONS} && !$noenv ) {+ push @arg_sources, {+ name => 'ACK_OPTIONS',+ contents => $ENV{ACK_OPTIONS},+ };+ }++ push @arg_sources, {+ name => 'ARGV',+ contents => [ @ARGV ],+ };++ return @arg_sources;+}++1; # End of App::Ack::ConfigLoader+package App::Ack::Filter;+++use strict;+use warnings;++use Carp 1.04 ();++my %filter_types;+++sub create_filter {+ my ( undef, $type, @args ) = @_;++ if ( my $package = $filter_types{$type} ) {+ return $package->new(@args);+ }+ Carp::croak "Unknown filter type '$type'";+}+++sub register_filter {+ my ( undef, $type, $package ) = @_;++ $filter_types{$type} = $package;++ return;+}+++sub invert {+ my ( $self ) = @_;++ return App::Ack::Filter::Inverse->new( $self );+}+++sub is_inverted {+ return 0;+}+++sub to_string {+ my ( $self ) = @_;++ return '(unimplemented to_string)';+}+++sub inspect {+ my ( $self ) = @_;++ return ref($self);+}++1;+package App::Ack::Filter::Extension;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}+++sub new {+ my ( $class, @extensions ) = @_;++ my $exts = join('|', map { "\Q$_\E"} @extensions);+ my $re = qr/[.](?:$exts)$/i;++ return bless {+ extensions => \@extensions,+ regex => $re,+ groupname => 'ExtensionGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::ExtensionGroup->new();+}++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{'regex'};++ return $resource->name =~ /$re/;+}++sub inspect {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ return ref($self) . " - $re";+}++sub to_string {+ my ( $self ) = @_;++ my $exts = $self->{'extensions'};++ return join(' ', map { ".$_" } @{$exts});+}++BEGIN {+ App::Ack::Filter->register_filter(ext => __PACKAGE__);+}++1;+package App::Ack::Filter::FirstLineMatch;++++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class, $re ) = @_;++ $re =~ s{^/|/$}{}g; # XXX validate?+ $re = qr{$re}i;++ return bless {+ regex => $re,+ }, $class;+}++# This test reads the first 250 characters of a file, then just uses the+# first line found in that. This prevents reading something like an entire+# .min.js file (which might be only one "line" long) into memory.++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{'regex'};++ my $line = $resource->firstliney;++ return $line =~ /$re/;+}++sub inspect {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ return ref($self) . " - $re";+}++sub to_string {+ my ( $self ) = @_;++ (my $re = $self->{regex}) =~ s{\([^:]*:(.*)\)$}{$1};++ return "first line matches /$re/";+}++BEGIN {+ App::Ack::Filter->register_filter(firstlinematch => __PACKAGE__);+}++1;+package App::Ack::Filter::Is;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++use File::Spec 3.00 ();++sub new {+ my ( $class, $filename ) = @_;++ return bless {+ filename => $filename,+ groupname => 'IsGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::IsGroup->new();+}++sub filter {+ my ( $self, $resource ) = @_;++ my $filename = $self->{'filename'};+ my $base = (File::Spec->splitpath($resource->name))[2];++ return $base eq $filename;+}++sub inspect {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return ref($self) . " - $filename";+}++sub to_string {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return $filename;+}++BEGIN {+ App::Ack::Filter->register_filter(is => __PACKAGE__);+}++1;+package App::Ack::Filter::Match;++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++use File::Spec 3.00;+++sub new {+ my ( $class, $re ) = @_;++ $re =~ s{^/|/$}{}g; # XXX validate?+ $re = qr/$re/i;++ return bless {+ regex => $re,+ groupname => 'MatchGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::MatchGroup->new;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{'regex'};++ return $resource->basename =~ /$re/;+}++sub inspect {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ print ref($self) . " - $re";++ return;+}++sub to_string {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ return "filename matches $re";+}++BEGIN {+ App::Ack::Filter->register_filter(match => __PACKAGE__);+}++1;+package App::Ack::Filter::Default;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}++sub filter {+ my ( $self, $resource ) = @_;++ return -T $resource->name;+}++1;+package App::Ack::Filter::Inverse;++++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class, $filter ) = @_;++ return bless {+ filter => $filter,+ }, $class;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $filter = $self->{'filter'};+ return !$filter->filter( $resource );+}++sub invert {+ my $self = shift;++ return $self->{'filter'};+}++sub is_inverted {+ return 1;+}++sub inspect {+ my ( $self ) = @_;++ my $filter = $self->{'filter'};++ return "!$filter";+}++1;+package App::Ack::Filter::Collection;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ groups => {},+ ungrouped => [],+ }, $class;+}++sub filter {+ my ( $self, $resource ) = @_;++ for my $group (values %{$self->{'groups'}}) {+ if ($group->filter($resource)) {+ return 1;+ }+ }++ for my $filter (@{$self->{'ungrouped'}}) {+ if ($filter->filter($resource)) {+ return 1;+ }+ }++ return 0;+}++sub add {+ my ( $self, $filter ) = @_;++ if (exists $filter->{'groupname'}) {+ my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group());+ $group->add($filter);+ }+ else {+ push @{$self->{'ungrouped'}}, $filter;+ }++ return;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ my $ungrouped = $self->{'ungrouped'};++ return join(', ', map { "($_)" } @{$ungrouped});+}++1;+package App::Ack::Filter::IsGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++use File::Spec 3.00 ();++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $data = $self->{'data'};+ my $base = $resource->basename;++ return exists $data->{$base};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;+package App::Ack::Filter::ExtensionGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ foreach my $ext (@{$filter->{extensions}}) {+ $self->{data}->{lc $ext} = 1;+ }++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ if ($resource->name =~ /[.]([^.]*)$/) {+ return exists $self->{'data'}->{lc $1};+ }++ return 0;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', map { ".$_" } sort keys %{$self->{data}});+}++1;+package App::Ack::Filter::MatchGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ matches => [],+ big_re => undef,+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ push @{ $self->{matches} }, $filter->{regex};++ my $re = join('|', map { "(?:$_)" } @{ $self->{matches} });+ $self->{big_re} = qr/$re/;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{big_re};++ return $resource->basename =~ /$re/;+}++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 {+ our @ISA = 'App::Ack::Filter';+}+++sub new {+ my ( $class, $filename ) = @_;++ return bless {+ filename => $filename,+ groupname => 'IsPathGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::IsPathGroup->new();+}++sub filter {+ my ( $self, $resource ) = @_;++ return $resource->name eq $self->{'filename'};+}++sub inspect {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return ref($self) . " - $filename";+}++sub to_string {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return $filename;+}++1;+package App::Ack::Filter::IsPathGroup;+++++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $data = $self->{'data'};++ return exists $data->{$resource->name};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.16';++++use File::Spec ();++our $name; # name of the current file+our $dir; # dir of the current file++our %files_defaults;+our %skip_dirs;++BEGIN {+ %files_defaults = (+ file_filter => undef,+ descend_filter => undef,+ error_handler => sub { CORE::die $_[0] },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );++ return sub {+ my $filter = $parms->{file_filter};+ while (@queue) {+ my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );+ if ( -f $fullpath || -p _ || $fullpath =~ m{^/dev/fd} ) {+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ }+ if ( -d _ ) {+ unshift( @queue, _candidate_files( $parms, $fullpath ) );+ }+ } # while++ return;+ }; # iterator+}+++++++sub from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{warning_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!", $! + 0 );+ return undef;+ }+ }++ return sub {+ my $filter = $parms->{file_filter};+ local $/ = $parms->{nul_separated} ? "\x00" : $/;+ while ( my $fullpath = <$fh> ) {+ chomp $fullpath;+ next unless $fullpath =~ /./;+ if ( not ( -f $fullpath || -p _ ) ) {+ $warn->( "$fullpath: No such file" );+ next;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!", $! + 0 );+ return;+ }++ my @newfiles;+ my $descend_filter = $parms->{descend_filter};+ my $follow_symlinks = $parms->{follow_symlinks};+ my $sort_sub = $parms->{sort_files};++ for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {+ my $has_stat;++ 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;+ local $_ = $file;+ next if not $descend_filter->();+ }+ }+ if ( $sort_sub ) {+ push( @newfiles, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next