@ 11d698f

180621.1 · 6ef2cfa95760cda7a214ce6d0c321b561bd56ce3

Add 2.24 to the garage

reading declarations

garage/ack-2.24

new file mode 100755@@ -0,0 +1,5368 @@+#!/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.24'; # 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 ( defined(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 {+            my $needs_line_scan = 1;+            if ( $opt->{regex} && !$opt->{passthru} ) {+                if ( $resource->open ) {+                    $needs_line_scan = $resource->needs_line_scan( $opt );+                    if ( $needs_line_scan ) {+                        $resource->reset();+                    }+                }+            }+            if ( $needs_line_scan ) {+                $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>++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-2018 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.24';+    $COPYRIGHT = 'Copyright 2005-2018 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;++    # Can't tell if it does NOT exist in the file.+    return 1 if $opt->{v};++    return 1 unless -f $self->{fh};++    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}/mo;+}++++sub reset {+    my $self = shift;++    # Return if we haven't opened the file yet.+    if ( !defined($self->{fh}) ) {+        return;+    }++    if ( !-f $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}->();++    return unless defined($file);++    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++# TeX & 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++# Typescript+# http://www.typescriptlang.org/+--type-add=ts:ext:ts,tsx++# 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