.tmux.conf: vim style window splitting
[dotfiles.git] / bin / ack
diff --git a/bin/ack b/bin/ack
index bf687091e35387dd5ac1ca4222bc113c434503c4..66d807c6b2e83d671d7922c15e826cc0d1e7b6a9 100755 (executable)
--- a/bin/ack
+++ b/bin/ack
 # Please DO NOT EDIT or send patches for it.
 #
 # Please take a look at the source from
-# http://github.com/petdance/ack
+# http://github.com/petdance/ack2
 # and submit patches against the individual files
 # that build ack.
 #
 
-use warnings;
+package File::Next;
+
 use strict;
+use warnings;
 
-our $VERSION = '1.96';
-# Check http://betterthangrep.com/ for updates
 
-# These are all our globals.
+our $VERSION = '1.12';
 
 
-MAIN: {
-    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 ( @ARGV ) {
-        last if ( $_ eq '--' );
+use File::Spec ();
 
-        # Priorities! Get the --thpppt checking out of the way.
-        /^--th[pt]+t+$/ && App::Ack::_thpppt($_);
+our $name; # name of the current file
+our $dir;  # dir of the current file
 
-        # See if we want to ignore the environment. (Don't tell Al Gore.)
-        if ( /^--(no)?env$/ ) {
-            $env_is_usable = defined $1 ? 0 : 1;
-        }
-    }
-    if ( $env_is_usable ) {
-        unshift( @ARGV, App::Ack::read_ackrc() );
-    }
-    else {
-        my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
-        delete @ENV{@keys};
-    }
-    App::Ack::load_colors();
+our %files_defaults;
+our %skip_dirs;
 
-    if ( exists $ENV{ACK_SWITCHES} ) {
-        App::Ack::warn( 'ACK_SWITCHES is no longer supported.  Use ACK_OPTIONS.' );
-    }
+BEGIN {
+    %files_defaults = (
+        file_filter     => undef,
+        descend_filter  => undef,
+        error_handler   => sub { CORE::die @_ },
+        warning_handler => sub { CORE::warn @_ },
+        sort_files      => undef,
+        follow_symlinks => 1,
+        nul_separated   => 0,
+    );
+    %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
+}
 
-    if ( !@ARGV ) {
-        App::Ack::show_help();
-        exit 1;
-    }
 
-    main();
+sub files {
+    die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
+
+    my ($parms,@queue) = _setup( \%files_defaults, @_ );
+    my $filter = $parms->{file_filter};
+
+    return sub {
+        while (@queue) {
+            my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
+            if ( -f $fullpath || -p $fullpath || $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;
+            }
+            elsif ( -d _ ) {
+                unshift( @queue, _candidate_files( $parms, $fullpath ) );
+            }
+        } # while
+
+        return;
+    }; # iterator
 }
 
-sub main {
-    my $opt = App::Ack::get_command_line_options();
 
-    $| = 1 if $opt->{flush}; # Unbuffer the output if flush mode
 
-    if ( App::Ack::input_from_pipe() ) {
-        # We're going into filter mode
-        for ( qw( f g l ) ) {
-            $opt->{$_} and App::Ack::die( "Can't use -$_ when acting as a filter." );
-        }
-        $opt->{show_filename} = 0;
-        $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
-        if ( my $nargs = @ARGV ) {
-            my $s = $nargs == 1 ? '' : 's';
-            App::Ack::warn( "Ignoring $nargs argument$s on the command-line while acting as a filter." );
-        }
 
-        my $res = App::Ack::Resource::Basic->new( '-' );
-        my $nmatches;
-        if ( $opt->{count} ) {
-            $nmatches = App::Ack::search_and_list( $res, $opt );
-        }
-        else {
-            # normal searching
-            $nmatches = App::Ack::search_resource( $res, $opt );
-        }
-        $res->close();
-        App::Ack::exit_from_ack( $nmatches );
-    }
 
-    my $file_matching = $opt->{f} || $opt->{lines};
-    if ( $file_matching ) {
-        App::Ack::die( "Can't specify both a regex ($opt->{regex}) and use one of --line, -f or -g." ) if $opt->{regex};
-    }
-    else {
-        $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
-    }
 
-    # check that all regexes do compile fine
-    App::Ack::check_regex( $_ ) for ( $opt->{regex}, $opt->{G} );
+sub from_file {
+    die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
 
-    my $what = App::Ack::get_starting_points( \@ARGV, $opt );
-    my $iter = App::Ack::get_iterator( $what, $opt );
-    App::Ack::filetype_setup();
+    my ($parms,@queue) = _setup( \%files_defaults, @_ );
+    my $err  = $parms->{error_handler};
+    my $warn = $parms->{error_handler};
 
-    my $nmatches = 0;
+    my $filename = $queue[1];
 
-    App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
-    if ( $opt->{f} ) {
-        $nmatches = App::Ack::print_files( $iter, $opt );
+    if ( !defined($filename) ) {
+        $err->( 'Must pass a filename to from_file()' );
+        return undef;
     }
-    elsif ( $opt->{l} || $opt->{count} ) {
-        $nmatches = App::Ack::print_files_with_matches( $iter, $opt );
+
+    my $fh;
+    if ( $filename eq '-' ) {
+        $fh = \*STDIN;
     }
     else {
-        $nmatches = App::Ack::print_matches( $iter, $opt );
+        if ( !open( $fh, '<', $filename ) ) {
+            $err->( "Unable to open $filename: $!" );
+            return undef;
+        }
     }
-    close $App::Ack::fh;
-    App::Ack::exit_from_ack( $nmatches );
-}
+    my $filter = $parms->{file_filter};
 
-=head1 NAME
+    return sub {
+        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;
+            }
 
-ack - grep-like text finder
+            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;
 
-=head1 SYNOPSIS
+        return;
+    }; # iterator
+}
 
-    ack [options] PATTERN [FILE...]
-    ack -f [options] [DIRECTORY...]
+sub _bad_invocation {
+    my $good = (caller(1))[3];
+    my $bad  = $good;
+    $bad =~ s/(.+)::/$1->/;
+    return "$good must not be invoked as $bad";
+}
 
-=head1 DESCRIPTION
+sub sort_standard($$)   { return $_[0]->[1] cmp $_[1]->[1] }
+sub sort_reverse($$)    { return $_[1]->[1] cmp $_[0]->[1] }
 
-Ack is designed as a replacement for 99% of the uses of F<grep>.
+sub reslash {
+    my $path = shift;
 
-Ack searches the named input FILEs (or standard input if no files are
-named, or the file name - is given) for lines containing a match to the
-given PATTERN.  By default, ack prints the matching lines.
+    my @parts = split( /\//, $path );
 
-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.
+    return $path if @parts < 2;
 
-=head1 FILE SELECTION
+    return File::Spec->catfile( @parts );
+}
 
-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 selections, I<ack> only searches files of types that
-it recognizes.  If you have a file called F<foo.wango>, and I<ack>
-doesn't know what a .wango file is, I<ack> won't search it.
 
-The B<-a> option tells I<ack> to select all files, regardless of
-type.
+sub _setup {
+    my $defaults = shift;
+    my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
 
-Some files will never be selected by I<ack>, even with B<-a>,
-including:
+    my %passed_parms = %{$passed_parms};
 
-=over 4
+    my $parms = {};
+    for my $key ( keys %{$defaults} ) {
+        $parms->{$key} =
+            exists $passed_parms{$key}
+                ? delete $passed_parms{$key}
+                : $defaults->{$key};
+    }
 
-=item * Backup files: Files matching F<#*#> or ending with F<~>.
+    # 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" );
+    }
 
-=item * Coredumps: Files matching F<core.\d+>
+    # 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;
 
-=back
+    for ( @_ ) {
+        my $start = reslash( $_ );
+        if (-d $start) {
+            push @queue, ($start,undef,$start);
+        }
+        else {
+            push @queue, (undef,$start,$start);
+        }
+    }
 
-However, I<ack> always searches the files given on the command line,
-no matter what type. Furthermore, by specifying the B<-u> option all
-files will be searched.
+    return ($parms,@queue);
+}
 
-=head1 DIRECTORY SELECTION
 
-I<ack> descends through the directory tree of the starting directories
-specified.  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.
+sub _candidate_files {
+    my $parms   = shift;
+    my $dirname = shift;
 
-For a complete list of directories that do not get searched, run
-F<ack --help>.
+    my $dh;
+    if ( !opendir $dh, $dirname ) {
+        $parms->{error_handler}->( "$dirname: $!" );
+        return;
+    }
 
-=head1 WHEN TO USE GREP
+    my @newfiles;
+    my $descend_filter = $parms->{descend_filter};
+    my $follow_symlinks = $parms->{follow_symlinks};
+    my $sort_sub = $parms->{sort_files};
 
-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.
+    for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
+        my $has_stat;
 
-E.g., searching through huge files looking for regexes that can be
-expressed with I<grep> syntax should be quicker with I<grep>.
+        # Only do directory checking if we have a descend_filter
+        my $fullpath = File::Spec->catdir( $dirname, $file );
+        if ( !$follow_symlinks ) {
+            next if -l $fullpath;
+            $has_stat = 1;
+        }
 
-If your script or parent program uses I<grep> C<--quiet> or
-C<--silent> or needs exit 2 on IO error, use I<grep>.
+        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;
 
-=head1 OPTIONS
+    if ( $sort_sub ) {
+        return map { @{$_} } sort $sort_sub @newfiles;
+    }
 
-=over 4
+    return @newfiles;
+}
 
-=item B<-a>, B<--all>
 
-Operate on all files, regardless of type (but still skip directories
-like F<blib>, F<CVS>, etc.)
+1; # End of File::Next
+package App::Ack;
 
-=item B<-A I<NUM>>, B<--after-context=I<NUM>>
+use warnings;
+use strict;
 
-Print I<NUM> lines of trailing context after matching lines.
 
-=item B<-B I<NUM>>, B<--before-context=I<NUM>>
+our $VERSION;
+our $GIT_REVISION;
+our $COPYRIGHT;
+BEGIN {
+    $VERSION = '2.10';
+    $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';
+    $GIT_REVISION = q{af91cce};
+}
 
-Print I<NUM> lines of leading context before matching lines.
+our $fh;
 
-=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
+BEGIN {
+    $fh = *STDOUT;
+}
 
-Print I<NUM> lines (default 2) of context around matching lines.
 
-=item B<-c>, B<--count>
+our %types;
+our %type_wanted;
+our %mappings;
+our %ignore_dirs;
 
-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.
+our $is_filter_mode;
+our $output_to_pipe;
 
-If combined with B<-h> (B<--no-filename>) ack outputs only one total count.
+our $dir_sep_chars;
+our $is_cygwin;
+our $is_windows;
 
-=item B<--color>, B<--nocolor>
+use File::Spec 1.00015 ();
 
-B<--color> highlights the matching text.  B<--nocolor> supresses
-the color.  This is on by default unless the output is redirected.
+BEGIN {
+    # These have to be checked before any filehandle diddling.
+    $output_to_pipe  = not -t *STDOUT;
+    $is_filter_mode = -p STDIN;
 
-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.
+    $is_cygwin       = ($^O eq 'cygwin');
+    $is_windows      = ($^O eq 'MSWin32');
+    $dir_sep_chars   = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
+}
 
-=item B<--color-filename=I<color>>
 
-Sets the color to be used for filenames.
 
-=item B<--color-match=I<color>>
+sub remove_dir_sep {
+    my $path = shift;
+    $path =~ s/[$dir_sep_chars]$//;
 
-Sets the color to be used for matches.
+    return $path;
+}
 
-=item B<--color-lineno=I<color>>
 
-Sets the color to be used for line numbers.
 
-=item B<--column>
+sub warn {
+    return CORE::warn( _my_program(), ': ', @_, "\n" );
+}
 
-Show the column number of the first match.  This is helpful for editors
-that can place your cursor at a given position.
 
-=item B<--env>, B<--noenv>
+sub die {
+    return CORE::die( _my_program(), ': ', @_, "\n" );
+}
 
-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.
+sub _my_program {
+    require File::Basename;
+    return File::Basename::basename( $0 );
+}
 
-=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>
+sub filetypes_supported {
+    return keys %mappings;
+}
 
-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.
+sub _get_thpppt {
+    my $y = q{_   /|,\\'!.x',=(www)=,   U   };
+    $y =~ tr/,x!w/\nOo_/;
+    return $y;
+}
 
-=item B<--follow>, B<--nofollow>
+sub _thpppt {
+    my $y = _get_thpppt();
+    App::Ack::print( "$y ack $_[0]!\n" );
+    exit 0;
+}
 
-Follow or don't follow symlinks, other than whatever starting files
-or directories were specified on the command line.
+sub _bar {
+    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
+
+    App::Ack::__pic($x);
+}
 
-This is off by default.
+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
+    App::Ack::__pic($x);
+}
 
-=item B<-G I<REGEX>>
+sub __pic {
+    my($compressed) = @_;
+    $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;
+    App::Ack::print( $compressed );
+    exit 0;
+}
 
-Only paths matching I<REGEX> are included in the search.  The entire
-path and filename are matched against I<REGEX>, and I<REGEX> is a
-Perl regular expression, not a shell glob.
 
-The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
+sub show_help {
+    my $help_arg = shift || 0;
 
-=item B<-g I<REGEX>>
+    return show_help_types() if $help_arg =~ /^types?/;
 
-Print files where the relative path + filename matches I<REGEX>. This option is
-a convenience shortcut for B<-f> B<-G I<REGEX>>.
+    App::Ack::print( <<"END_OF_HELP" );
+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
 
-The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
+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 "-".
 
-=item B<--group>, B<--nogroup>
+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.
 
-B<--group> groups matches by file name with.  This is the default when
-used interactively.
+Example: ack -i select
 
-B<--nogroup> prints one result per line, like grep.  This is the default
-when output is redirected.
+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
 
-=item B<-H>, B<--with-filename>
+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.
 
-Print the filename for each match.
 
-=item B<-h>, B<--no-filename>
+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).
 
-Suppress the prefixing of filenames on output when multiple files are
-searched.
 
-=item B<--help>
+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.
 
-Print a short help statement.
+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.
 
-=item B<-i>, B<--ignore-case>
 
-Ignore case in the search strings.
+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!
 
-This applies only to the PATTERN, not to the regexes given for the B<-g>
-and B<-G> options.
+Exit status is 0 if match, 1 if no match.
 
-=item B<--[no]ignore-dir=I<DIRNAME>>
+This is version $VERSION of ack.
+END_OF_HELP
 
-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).
+    return;
+ }
 
-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<--line=I<NUM>>
 
-Only print line I<NUM> of each file. Multiple lines can be given with multiple
-B<--line> options or as a comma separated list (B<--line=3,5,7>). B<--line=4-7>
-also works. The lines are always output in ascending order, no matter the
-order given on the command line.
+sub show_help_types {
+    App::Ack::print( <<'END_OF_HELP' );
+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
 
-=item B<-l>, B<--files-with-matches>
+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.
 
-Only print the filenames of matching files, instead of the matching text.
+Note that some extensions may appear in multiple types.  For example,
+.pod files are both Perl and Parrot.
 
-=item B<-L>, B<--files-without-matches>
+END_OF_HELP
 
-Only print the filenames of files that do I<NOT> match. This is equivalent
-to specifying B<-l> and B<-v>.
+    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};
 
-=item B<--match I<REGEX>>
+        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 ) );
+    }
 
-Specify the I<REGEX> 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.
+    return;
+}
 
-    # search for foo and bar in given files
-    ack file1 t/file* --match foo
-    ack file1 t/file* --match bar
+sub show_man {
+    require Pod::Usage;
 
-=item B<-m=I<NUM>>, B<--max-count=I<NUM>>
+    Pod::Usage::pod2usage({
+        -input   => $App::Ack::orig_program_name,
+        -verbose => 2,
+        -exitval => 0,
+    });
 
-Stop reading a file after I<NUM> matches.
+    return;
+}
 
-=item B<--man>
 
-Print this manual page.
+sub get_version_statement {
+    require Config;
 
-=item B<-n>, B<--no-recurse>
+    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 );
 
-No descending into subdirectories.
+    my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';
 
-=item B<-o>
+    return <<"END_OF_VERSION";
+ack ${VERSION}${git_revision}
+Running under Perl $ver at $this_perl
 
-Show only the part of each line matching PATTERN (turns off text
-highlighting)
+$copyright
 
-=item B<--output=I<expr>>
+This program is free software.  You may modify or distribute it
+under the terms of the Artistic License v2.0.
+END_OF_VERSION
+}
 
-Output the evaluation of I<expr> for each line (turns off text
-highlighting)
 
-=item B<--pager=I<program>>
+sub print_version_statement {
+    App::Ack::print( get_version_statement() );
 
-Direct ack's output through I<program>.  This can also be specified
-via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.
+    return;
+}
 
-Using --pager does not suppress grouping and coloring like piping
-output on the command-line does.
 
-=item B<--passthru>
+sub get_copyright {
+    return $COPYRIGHT;
+}
 
-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
+# print subs added in order to make it easy for a third party
+# module (such as App::Wack) to redefine the display methods
+# and show the results in a different way.
+sub print                   { print {$fh} @_; return; }
+sub print_first_filename    { App::Ack::print( $_[0], "\n" ); return; }
+sub print_blank_line        { App::Ack::print( "\n" ); return; }
+sub print_separator         { App::Ack::print( "--\n" ); return; }
+sub print_filename          { App::Ack::print( $_[0], $_[1] ); return; }
+sub print_line_no           { App::Ack::print( $_[0], $_[1] ); return; }
+sub print_column_no         { App::Ack::print( $_[0], $_[1] ); return; }
+sub print_count {
+    my $filename = shift;
+    my $nmatches = shift;
+    my $ors = shift;
+    my $count = shift;
+    my $show_filename = shift;
 
-=item B<--print0>
+    if ($show_filename) {
+        App::Ack::print( $filename );
+        App::Ack::print( ':', $nmatches ) if $count;
+    }
+    else {
+        App::Ack::print( $nmatches ) if $count;
+    }
+    App::Ack::print( $ors );
 
-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.
+    return;
+}
 
-    # remove all files of type html
-    ack -f --html --print0 | xargs -0 rm -f
+sub print_count0 {
+    my $filename = shift;
+    my $ors = shift;
+    my $show_filename = shift;
 
-=item B<-Q>, B<--literal>
+    if ($show_filename) {
+        App::Ack::print( $filename, ':0', $ors );
+    }
+    else {
+        App::Ack::print( '0', $ors );
+    }
 
-Quote all metacharacters in PATTERN, it is treated as a literal.
+    return;
+}
 
-This applies only to the PATTERN, not to the regexes given for the B<-g>
-and B<-G> options.
+sub set_up_pager {
+    my $command = shift;
 
-=item B<-r>, B<-R>, B<--recurse>
+    return if App::Ack::output_to_pipe();
 
-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.
+    my $pager;
+    if ( not open( $pager, '|-', $command ) ) {
+        App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
+    }
+    $fh = $pager;
 
-=item B<--smart-case>, B<--no-smart-case>
+    return;
+}
 
-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.
 
-B<-i> always overrides this option.
+sub output_to_pipe {
+    return $output_to_pipe;
+}
 
-This applies only to the PATTERN, not to the regexes given for the
-B<-g> and B<-G> options.
 
-=item B<--sort-files>
+sub exit_from_ack {
+    my $nmatches = shift;
 
-Sorts the found files lexically.  Use this if you want your file
-listings to be deterministic between runs of I<ack>.
+    my $rc = $nmatches ? 0 : 1;
+    exit $rc;
+}
 
-=item B<--show-types>
 
-Outputs the filetypes that ack associates with each file.
 
-Works with B<-f> and B<-g> options.
+1; # End of App::Ack
+package App::Ack::Resource;
 
-=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.
+use warnings;
+use strict;
+use overload
+    '""' => 'name';
 
-=item B<--type=TYPE>, B<--type=noTYPE>
+sub FAIL {
+    require Carp;
+    Carp::confess( 'Must be overloaded' );
+}
 
-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.
+sub new {
+    return FAIL();
+}
 
-Type specifications can be repeated and are ORed together.
 
-See I<ack --help=types> for a list of valid types.
+sub name {
+    return FAIL();
+}
 
-=item B<--type-add I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
 
-Files with the given EXTENSION(s) are recognized as being of (the
-existing) type TYPE. See also L</"Defining your own types">.
+sub is_binary {
+    return FAIL();
+}
 
 
-=item B<--type-set I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
+sub open {
+    return FAIL();
+}
 
-Files with the given EXTENSION(s) are recognized as being of type
-TYPE. This replaces an existing definition for type TYPE.  See also
-L</"Defining your own types">.
 
-=item B<-u>, B<--unrestricted>
+sub needs_line_scan {
+    return FAIL();
+}
 
-All files and directories (including blib/, core.*, ...) are searched,
-nothing is skipped. When both B<-u> and B<--ignore-dir> are used, the
-B<--ignore-dir> option has no effect.
 
-=item B<-v>, B<--invert-match>
+sub reset {
+    return FAIL();
+}
 
-Invert match: select non-matching lines
 
-This applies only to the PATTERN, not to the regexes given for the B<-g>
-and B<-G> options.
+sub close {
+    return FAIL();
+}
 
-=item B<--version>
 
-Display version and copyright information.
+sub clone {
+    return FAIL();
+}
 
-=item B<-w>, B<--word-regexp>
 
-Force PATTERN to match only whole words.  The PATTERN is wrapped with
-C<\b> metacharacters.
+sub firstliney {
+    return FAIL();
+}
 
-This applies only to the PATTERN, not to the regexes given for the B<-g>
-and B<-G> options.
+1;
+package App::Ack::Resources;
 
-=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.
 
-=back
+use warnings;
+use strict;
 
-=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:
+sub from_argv {
+    my $class = shift;
+    my $opt   = shift;
+    my $start = shift;
 
-    # Always sort the files
-    --sort-files
+    my $self = bless {}, $class;
 
-    # Always color, even if piping to a another program
-    --color
+    my $file_filter    = undef;
+    my $descend_filter = $opt->{descend_filter};
 
-    # Use "less -r" as my pager
-    --pager=less -r
+    if( $opt->{n} ) {
+        $descend_filter = sub {
+            return 0;
+        };
+    }
 
-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>.
+    $self->{iter} =
+        File::Next::files( {
+            file_filter     => $opt->{file_filter},
+            descend_filter  => $descend_filter,
+            error_handler   => sub { my $msg = shift; App::Ack::warn( $msg ) },
+            sort_files      => $opt->{sort_files},
+            follow_symlinks => $opt->{follow},
+        }, @{$start} );
 
-F<ack> looks in your home directory for the F<.ackrc>.  You can
-specify another location with the F<ACKRC> variable, below.
+    return $self;
+}
 
-If B<--noenv> is specified on the command line, the F<.ackrc> file
-is ignored.
 
-=head1 Defining your own types
+sub from_file {
+    my $class = shift;
+    my $opt   = shift;
+    my $file  = shift;
 
-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.
+    my $iter =
+        File::Next::from_file( {
+            error_handler   => sub { my $msg = shift; App::Ack::warn( $msg ) },
+            warning_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },
+            sort_files      => $opt->{sort_files},
+        }, $file ) or return undef;
 
-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=.xs --perl foo>
-does this for you. B<--type-add> appends
-additional extensions to an existing type.
+    return bless {
+        iter => $iter,
+    }, $class;
+}
 
-If you want to define a new type, or completely redefine an existing
-type, then use B<--type-set>. I<ack --type-set
-eiffel=.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=.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=.c,.h>
-and I<.xs> files no longer belong to the type I<cc>.
+# This is for reading input lines from STDIN, not the list of files from STDIN
+sub from_stdin {
+    my $class = shift;
+    my $opt   = shift;
 
-When defining your own types in the F<.ackrc> file you have to use
-the following:
+    my $self  = bless {}, $class;
 
-  --type-set=eiffel=.e,.eiffel
+    my $has_been_called = 0;
 
-or writing on separate lines
+    $self->{iter} = sub {
+        if ( !$has_been_called ) {
+            $has_been_called = 1;
+            return '-';
+        }
+        return;
+    };
 
-  --type-set
-  eiffel=.e,.eiffel
+    return $self;
+}
 
-The following does B<NOT> work in the F<.ackrc> file:
+sub next {
+    my $self = shift;
 
-  --type-set eiffel=.e,.eiffel
+    my $file = $self->{iter}->() or return;
 
+    return App::Ack::Resource::Basic->new( $file );
+}
 
-In order to see all currently defined types, use I<--help types>, e.g.
-I<ack --type-set backup=.bak --type-add perl=.perl --help types>
+1;
+package App::Ack::Resource::Basic;
 
-Restrictions:
 
-=over 4
+use warnings;
+use strict;
 
-=item
+use Fcntl ();
 
-The types 'skipped', 'make', 'binary' and 'text' are considered "builtin" and
-cannot be altered.
+BEGIN {
+    our @ISA = 'App::Ack::Resource';
+}
 
-=item
 
-The shebang line recognition of the types 'perl', 'ruby', 'php', 'python',
-'shell' and 'xml' cannot be redefined by I<--type-set>, it is always
-active. However, the shebang line is only examined for files where the
-extension is not recognised. Therefore it is possible to say
-I<ack --type-set perl=.perl --type-set foo=.pl,.pm,.pod,.t --perl --nofoo> and
-only find your shiny new I<.perl> files (and all files with unrecognized extension
-and perl on the shebang line).
+sub new {
+    my $class    = shift;
+    my $filename = shift;
 
-=back
+    my $self = bless {
+        filename => $filename,
+        fh       => undef,
+        opened   => 0,
+    }, $class;
 
-=head1 ENVIRONMENT VARIABLES
+    if ( $self->{filename} eq '-' ) {
+        $self->{fh}     = *STDIN;
+        $self->{opened} = 1;
+    }
 
-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.
+    return $self;
+}
 
-=over 4
 
-=item ACKRC
+sub name {
+    return $_[0]->{filename};
+}
 
-Specifies the location of the 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.
+sub needs_line_scan {
+    my $self  = shift;
+    my $opt   = shift;
 
-=item ACK_COLOR_FILENAME
+    return 1 if $opt->{v};
 
-Specifies the color of the filename when it's printed in B<--group>
-mode.  By default, it's "bold green".
+    my $size = -s $self->{fh};
+    if ( $size == 0 ) {
+        return 0;
+    }
+    elsif ( $size > 100_000 ) {
+        return 1;
+    }
 
-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.
+    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 );
 
-This option can also be set with B<--color-filename>.
+    my $regex = $opt->{regex};
+    return $buffer =~ /$regex/m;
+}
 
-=item ACK_COLOR_MATCH
 
-Specifies the color of the matching text when printed in B<--color>
-mode.  By default, it's "black on_yellow".
+sub reset {
+    my $self = shift;
 
-This option can also be set with B<--color-match>.
+    # return if we haven't opened the file yet
+    if ( !defined($self->{fh}) ) {
+        return;
+    }
 
-See B<ACK_COLOR_FILENAME> for the color specifications.
+    if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
+        App::Ack::warn( "$self->{filename}: $!" );
+    }
 
-=item ACK_COLOR_LINENO
+    return;
+}
 
-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>.
+sub close {
+    my $self = shift;
 
-See B<ACK_COLOR_FILENAME> for the color specifications.
+    # return if we haven't opened the file yet
+    if ( !defined($self->{fh}) ) {
+        return;
+    }
 
-=item ACK_PAGER
+    if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
+        App::Ack::warn( $self->name() . ": $!" );
+    }
 
-Specifies a pager program, such as C<more>, C<less> or C<most>, to which
-ack will send its output.
+    $self->{opened} = 0;
 
-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.
+    return;
+}
 
-C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
 
-=item ACK_PAGER_COLOR
+sub clone {
+    my ( $self ) = @_;
 
-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.
+    return __PACKAGE__->new($self->name);
+}
 
-If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
+sub firstliney {
+    my ( $self ) = @_;
 
-=back
+    my $fh = $self->open();
 
-=head1 ACK & OTHER TOOLS
+    unless(exists $self->{firstliney}) {
+        my $buffer = '';
+        my $rc     = sysread( $fh, $buffer, 250 );
+        unless($rc) { # XXX handle this better?
+            $buffer = '';
+        }
+        $buffer =~ s/[\r\n].*//s;
+        $self->{firstliney} = $buffer;
+        $self->reset;
+    }
 
-=head2 Vim integration
+    $self->close;
 
-F<ack> integrates easily with the Vim text editor. Set this in your
-F<.vimrc> to use F<ack> instead of F<grep>:
+    return $self->{firstliney};
+}
 
-    set grepprg=ack\ -a
+sub open {
+    my ( $self ) = @_;
 
-That examples uses C<-a> to search through all files, but you may
-use other default flags. Now you can search with F<ack> and easily
-step through the results in Vim:
+    return $self->{fh} if $self->{opened};
 
-  :grep Dumper perllib
+    unless ( open $self->{fh}, '<', $self->{filename} ) {
+        return;
+    }
 
-=head2 Emacs integration
+    $self->{opened} = 1;
 
-Phil Jackson put together an F<ack.el> extension that "provides a
-simple compilation mode ... has the ability to guess what files you
-want to search for based on the major-mode."
+    return $self->{fh};
+}
 
-L<http://www.shellarchive.co.uk/content/emacs.html>
+1;
+package App::Ack::ConfigDefault;
 
-=head2 TextMate integration
+use warnings;
+use strict;
 
-Pedro Melo is a TextMate user who writes "I spend my day mostly
-inside TextMate, and the built-in find-in-project sucks with large
-projects.  So I hacked a TextMate command that was using find +
-grep to use ack.  The result is the Search in Project with ack, and
-you can find it here:
-L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"
+sub options {
+    my @options = split( /\n/, _options_block() );
+    @options = grep { /./ && !/^#/ } @options;
 
-=head2 Shell and Return Code
+    return @options;
+}
 
-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.
+sub _options_block {
+    return <<'HERE';
+# This is the default ackrc for ack 2.0
 
-(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
+# 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.
 
-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.
+### Directories to ignore
 
-=cut
+# Bazaar
+--ignore-directory=is:.bzr
 
-=head1 DEBUGGING ACK PROBLEMS
+# Codeville
+--ignore-directory=is:.cdv
 
-If ack gives you output you're not expecting, start with a few simple steps.
+# Interface Builder
+--ignore-directory=is:~.dep
+--ignore-directory=is:~.dot
+--ignore-directory=is:~.nib
+--ignore-directory=is:~.plst
 
-=head2 Use B<--noenv>
+# Git
+--ignore-directory=is:.git
 
-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>.
+# Mercurial
+--ignore-directory=is:.hg
 
-=head2 Use B<-f> to see what files you're scanning
+# quilt
+--ignore-directory=is:.pc
 
-The reason I created B<-f> in the first place was as a debugging
-tool.  If ack is not finding matches you think it should find, run
-F<ack -f> to see what files are being checked.
+# Subversion
+--ignore-directory=is:.svn
 
-=head1 TIPS
+# Monotone
+--ignore-directory=is:_MTN
 
-=head2 Use the F<.ackrc> file.
+# CVS
+--ignore-directory=is:CVS
 
-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.
+# RCS
+--ignore-directory=is:RCS
 
-=head2 Use F<-f> for working with big codesets
+# SCCS
+--ignore-directory=is:SCCS
 
-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:
+# darcs
+--ignore-directory=is:_darcs
 
-    # Change all "this" to "that" in all Perl files in a tree.
-    ack -f --perl | xargs perl -p -i -e's/this/that/g'
+# Vault/Fortress
+--ignore-directory=is:_sgbak
 
-or if you prefer:
+# autoconf
+--ignore-directory=is:autom4te.cache
 
-    perl -p -i -e's/this/thatg/' $(ack -f --perl)
+# Perl module building
+--ignore-directory=is:blib
+--ignore-directory=is:_build
 
-=head2 Use F<-Q> when in doubt about metacharacters
+# Perl Devel::Cover module's output directory
+--ignore-directory=is:cover_db
 
-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...
+# Node modules created by npm
+--ignore-directory=is:node_modules
 
-=head2 Use ack to watch log files
+# CMake cache
+--ignore-directory=is:CMakeFiles
 
-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.
+### Files to ignore
 
-    ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif
+# Backup files
+--ignore-file=ext:bak
+--ignore-file=match:/~$/
 
-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.
+# Emacs swap files
+--ignore-file=match:/^#.+#$/
 
-=head2 Share your knowledge
+# vi/vim swap files
+--ignore-file=match:/[._].*\.swp$/
 
-Join the ack-users mailing list.  Send me your tips and I may add
-them here.
+# core dumps
+--ignore-file=match:/core\.\d+$/
 
-=head1 FAQ
+# minified Javascript
+--ignore-file=match:/[.-]min[.]js$/
+--ignore-file=match:/[.]js[.]min$/
 
-=head2 Why isn't ack finding a match in (some file)?
+# minified CSS
+--ignore-file=match:/[.]min[.]css$/
+--ignore-file=match:/[.]css[.]min$/
 
-Probably because it's of a type that ack doesn't recognize.  ack's
-searching behavior is driven by filetype.  B<If ack doesn't know
-what kind of file it is, ack ignores the file.>
+# PDFs, because they pass Perl's -T detection
+--ignore-file=ext:pdf
 
-Use the C<-f> switch to see a list of files that ack will search
-for you.
+# Common graphics, just as an optimization
+--ignore-file=ext:gif,jpg,jpeg,png
 
-If you want ack to search files that it doesn't recognize, use the
-C<-a> switch.
 
-If you want ack to search every file, even ones that it always
-ignores like coredumps and backup files, use the C<-u> switch.
+### Filetypes defined
 
-=head2 Why does ack ignore unknown files by default?
+# Perl http://perl.org/
+--type-add=perl:ext:pl,pm,pod,t,psgi
+--type-add=perl:firstlinematch:/^#!.*\bperl/
 
-ack is designed by a programmer, for programmers, for searching
-large trees of code.  Most codebases have a lot files in them which
-aren't source files (like compiled object files, source control
-metadata, etc), and grep wastes a lot of time searching through all
-of those as well and returning matches from those files.
+# Perl tests
+--type-add=perltest:ext:t
 
-That's why ack's behavior of not searching things it doesn't recognize
-is one of its greatest strengths: the speed you get from only
-searching the things that you want to be looking at.
+# 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:GNUmakefile
 
-=head2 Wouldn't it be great if F<ack> did search & replace?
+# Rakefiles http://rake.rubyforge.org/
+--type-add=rake:is:Rakefile
 
-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.
+# CMake http://www.cmake.org/
+--type-add=cmake:is:CMakeLists.txt
+--type-add=cmake:ext:cmake
 
-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:
+# Actionscript
+--type-add=actionscript:ext:as,mxml
 
-    $ perl -i -p -e's/foo/bar/g' $(ack -f --php)
+# Ada http://www.adaic.org/
+--type-add=ada:ext:ada,adb,ads
 
-=head2 Can you make ack recognize F<.xyz> files?
+# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx
+--type-add=asp:ext:asp
 
-That's an enhancement.  Please see the section in the manual about
-enhancements.
+# ASP.Net http://www.asp.net/
+--type-add=aspx:ext:master,ascx,asmx,aspx,svc
 
-=head2 There's already a program/package called ack.
+# Assembly
+--type-add=asm:ext:asm,s
 
-Yes, I know.
+# Batch
+--type-add=batch:ext:bat,cmd
 
-=head2 Why is it called ack if it's called ack-grep?
+# ColdFusion http://en.wikipedia.org/wiki/ColdFusion
+--type-add=cfmx:ext:cfc,cfm,cfml
 
-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.
+# Clojure http://clojure.org/
+--type-add=clojure:ext:clj
 
-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.
+# C
+# .xs are Perl C files
+--type-add=cc:ext:c,h,xs
 
-To do that, run this with F<sudo> or as root:
+# C header files
+--type-add=hh:ext:h
 
-   ln -s /usr/bin/ack-grep /usr/bin/ack
+# CoffeeScript http://coffeescript.org/
+--type-add=coffeescript:ext:coffee
 
-=head2 What does F<ack> mean?
+# C++
+--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx
 
-Nothing.  I wanted a name that was easy to type and that you could
-pronounce as a single syllable.
+# C#
+--type-add=csharp:ext:cs
 
-=head2 Can I do multi-line regexes?
+# CSS http://www.w3.org/Style/CSS/
+--type-add=css:ext:css
 
-No, ack does not support regexes that match multiple lines.  Doing
-so would require reading in the entire file at a time.
+# 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
+
+# Google Go http://golang.org/
+--type-add=go:ext:go
+
+# Groovy http://groovy.codehaus.org/
+--type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
+
+# Haskell http://www.haskell.org/
+--type-add=haskell:ext:hs,lhs
+
+# HTML
+--type-add=html:ext:htm,html
+
+# 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,jhtm,jhtml
+
+# JSON http://www.json.org/
+--type-add=json:ext:json
+
+# 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
+
+# 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
+
+# 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
+
+# SQL http://www.iso.org/iso/catalogue_detail.htm?csnumber=45498
+--type-add=sql:ext:sql,ctl
+
+# Tcl http://www.tcl.tk/
+--type-add=tcl:ext:tcl,itcl,itk
+
+# LaTeX http://www.latex-project.org/
+--type-add=tex:ext:tex,cls,sty
+
+# Template Toolkit http://template-toolkit.org/
+--type-add=tt:ext:tt,tt2,ttml
+
+# Visual Basic
+--type-add=vb:ext:bas,cls,frm,ctl,vb,resx
+
+# Verilog
+--type-add=verilog:ext:v,vh,sv
+
+# VHDL http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome
+--type-add=vhdl:ext:vhd,vhdl
+
+# Vim http://www.vim.org/
+--type-add=vim:ext:vim
+
+# XML http://www.w3.org/TR/REC-xml/
+--type-add=xml:ext:xml,dtd,xsl,xslt,ent
+--type-add=xml:firstlinematch:/<[?]xml/
+
+# YAML http://yaml.org/
+--type-add=yaml:ext:yaml,yml
+HERE
+}
+
+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 ) = @_;
+
+    if ( $App::Ack::is_windows ) {
+        # inode stat always returns 0 on windows, so just check filenames.
+        my (%seen, @uniq);
+
+        foreach my $path (@configs) {
+            push @uniq, $path unless $seen{$path};
+            $seen{$path} = 1;
+        }
+
+        return @uniq;
+    }
+
+    else {
+
+        my %dev_and_inode_seen;
+
+        foreach my $path ( @configs ) {
+            my ( $dev, $inode ) = (stat $path)[0, 1];
+
+            if( defined($dev) ) {
+                if( $dev_and_inode_seen{"$dev:$inode"} ) {
+                    undef $path;
+                }
+                else {
+                    $dev_and_inode_seen{"$dev:$inode"} = 1;
+                }
+            }
+        }
+
+        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 { File::Spec->catfile($_, 'ackrc') } (
+            Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),
+            Win32::GetFolderPath(Win32::CSIDL_APPDATA()),
+        );
+    }
+    else {
+        push @config_files, '/etc/ackrc';
+    }
+
+
+    if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {
+        push @config_files, $ENV{'ACKRC'};
+    }
+    else {
+        push @config_files, _check_for_ackrc($ENV{'HOME'});
+    }
+
+    my @dirs = File::Spec->splitdir(Cwd::getcwd());
+    while(@dirs) {
+        my $ackrc = _check_for_ackrc(@dirs);
+        if(defined $ackrc) {
+            push @config_files, $ackrc;
+            last;
+        }
+        pop @dirs;
+    }
+
+    # XXX 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 =~ /^#/;
+
+        push( @lines, $line );
+    }
+    close $fh;
+
+    return @lines;
+}
+
+1;
+package App::Ack::ConfigLoader;
+
+use strict;
+use warnings;
+
+use Carp 1.04 ();
+use Getopt::Long 2.35 ();
+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 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--;
+        }
+    }
+}
+
+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,
+    );
+
+    for ( my $i = 0; $i < @{$arg_sources}; $i += 2) {
+        my ( $source_name, $args ) = @{$arg_sources}[ $i, $i + 1];
+
+        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, $arg_sources->[$i + 1] ) =
+                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;
+This is because we now have -k/--known-types which makes it only select files
+of known types, rather than any text file (which is the behavior of ack 1.x).
+You may have options in a .ackrc, or in the ACKRC_OPTIONS environment variable.
+Try using the --dump flag.
+EOT
+
+    return {
+        1                   => sub { $opt->{1} = $opt->{m} = 1 },
+        'A|after-context=i' => \$opt->{after_context},
+        'B|before-context=i'
+                            => \$opt->{before_context},
+        'C|context:i'       => sub { shift; my $val = shift; $opt->{before_context} = $opt->{after_context} = ($val || 2) },
+        'a'                 => 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' # XXX Combine this version with the negated version below
+                            => sub {
+                                my ( undef, $dir ) = @_;
+
+                                $dir = App::Ack::remove_dir_sep( $dir );
+                                if ( $dir !~ /^(?:is|match):/ ) {
+                                    $dir = 'is:' . $dir;
+                                }
+                                push @{ $opt->{idirs} }, $dir;
+                               },
+        'ignore-file=s'    => sub {
+                                    my ( undef, $file ) = @_;
+                                    push @{ $opt->{ifiles} }, $file;
+                               },
+        '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'
+                            => sub {
+                                my ( undef, $dir ) = @_;
+
+                                # XXX can you do --noignore-dir=match,...?
+                                $dir = App::Ack::remove_dir_sep( $dir );
+                                if ( $dir !~ /^(?:is|match):/ ) {
+                                    $dir = 'is:' . $dir;
+                                }
+                                if ( $dir !~ /^(?:is|match):/ ) {
+                                    Carp::croak("invalid noignore-directory argument: '$dir'");
+                                }
+
+                                @{ $opt->{idirs} } = grep {
+                                    $_ ne $dir
+                                } @{ $opt->{idirs} };
+
+                                push @{ $opt->{no_ignore_dirs} }, $dir;
+                               },
+        '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 ) = @_;
+
+    Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version'); # start with default options, minus some annoying ones
+    Getopt::Long::Configure(
+        'bundling',
+        'no_ignore_case',
+    );
+
+    my $argv_source;
+    my $is_help_types_active;
+
+    for ( my $i = 0; $i < @{$arg_sources}; $i += 2 ) {
+        my ( $source_name, $args ) = @{$arg_sources}[ $i, $i + 1 ];
+
+        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);
+
+    for ( my $i = 0; $i < @{$arg_sources}; $i += 2) {
+        my ($source_name, $args) = @{$arg_sources}[$i, $i + 1];
+
+        my $ret;
+        if ( ref($args) ) {
+            local @ARGV = @{$args};
+            $ret = Getopt::Long::GetOptions( %{$arg_specs} );
+            @{$args} = @ARGV;
+        }
+        else {
+            ( $ret, $arg_sources->[$i + 1] ) =
+                Getopt::Long::GetOptionsFromString( $args, %{$arg_specs} );
+        }
+        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 ) = @_;
+
+    for(my $i = 0; $i < @{$sources}; $i += 2) {
+        my ( $name, $options ) = @{$sources}[$i, $i + 1];
+        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};
+    };
+
+    for(my $i = 0; $i < @{$sources}; $i += 2) {
+        my ( $name, $options ) = @{$sources}[$i, $i + 1];
+        if ( ref($options) ne 'ARRAY' ) {
+            $sources->[$i + 1] = $options =
+                [ Text::ParseWords::shellwords($options) ];
+        }
+        for ( my $j = 0; $j < @{$options}; $j++ ) {
+            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, \@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;
+
+    for(my $i = 0; $i < @{$sources}; $i += 2) {
+        my ( $name, $contents ) = @{$sources}[$i, $i + 1];
+        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] 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 + 2 .. $#$sources ) {
+        next if $index % 2 != 0;
+
+        my ( $name, $args ) = @{$sources}[ $index, $index + 1 ];
+
+        if (ref($args)) {
+            local @ARGV = @{$args};
+            Getopt::Long::GetOptions(
+                'ignore-ack-defaults' => \$should_remove,
+            );
+            @{$args} = @ARGV;
+        }
+        else {
+            ( undef, $sources->[$index + 1] ) = 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, 2;
+    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_name, $args ) = splice @copy, 0, 2;
+        $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";
+                }
+            }
+        }
+    }
+}
+
+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_name, $args ) = splice( @{$arg_sources}, 0, 2 );
+
+        # 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, $ackrc );
+    }
+
+    push @arg_sources, Defaults => [ App::Ack::ConfigDefault::options() ];
+
+    foreach my $file ( @files) {
+        my @lines = App::Ack::ConfigFinder::read_rcfile($file);
+        push ( @arg_sources, $file, \@lines ) if @lines;
+    }
+
+    if ( $ENV{ACK_OPTIONS} && !$noenv ) {
+        push( @arg_sources, 'ACK_OPTIONS' => $ENV{ACK_OPTIONS} );
+    }
+
+    push( @arg_sources, 'ARGV' => [ @ARGV ] );
+
+    return @arg_sources;
+}
+
+1; # End of App::Ack::ConfigLoader
+package App::Ack::Filter;
+
+use strict;
+use warnings;
+use overload
+    '""' => 'to_string';
+
+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,
+    }, $class;
+}
+
+sub filter {
+    my ( $self, $resource ) = @_;
+
+    my $re   = $self->{'regex'};
+    my $base = (File::Spec->splitpath($resource->name))[2];
+
+    return $base =~ /$re/;
+}
+
+sub inspect {
+    my ( $self ) = @_;
+
+    my $re = $self->{'regex'};
+
+    print ref($self) . " - $re";
+}
+
+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';
+}
+
+use File::Spec 3.00 ();
+
+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 $groups = $self->{'groups'};
+        my $group_name = $filter->{'groupname'};
+
+        my $group;
+        if (exists $groups->{$group_name}) {
+            $group = $groups->{$group_name};
+        }
+        else {
+            $group = $groups->{$group_name} = $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;
+}
+
+sub filter {
+    my ( $self, $resource ) = @_;
+
+    my $data = $self->{'data'};
+    my $base = (File::Spec->splitpath($resource->name))[2];
+
+    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';
+}
+
+use File::Spec 3.00 ();
+
+sub new {
+    my ( $class ) = @_;
+
+    return bless {
+        data => {},
+    }, $class;
+}
+
+sub add {
+    my ( $self, $filter ) = @_;
+
+    my $data = $self->{'data'};
+    my $extensions = $filter->{'extensions'};
+
+    foreach my $ext (@{$extensions}) {
+        $data->{lc $ext} = 1;
+    }
+}
+
+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 ) = @_;
+
+    my $data = $self->{'data'};
+
+    return join(' ', map { ".$_" } (keys %$data));
+}
+
+1;
+package main;
+
+use strict;
+use warnings;
+
+use 5.008008;
+
+
+# XXX Don't make this so brute force
+# See also: https://github.com/petdance/ack2/issues/89
+
+use Getopt::Long 2.35 ();
+
+use Carp 1.04 ();
+
+our $VERSION = '2.10';
+# Check http://beyondgrep.com/ for updates
+
+# 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::_bar();
+        $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            = $opt->{idirs};
+    my $dont_ignore_dirs = $opt->{no_ignore_dirs};
+
+    # 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};
+
+    my %ignore_dirs;
+
+    foreach my $idir (@{$idirs}) {
+        if ( $idir =~ /^(\w+):(.*)/ ) {
+            if ( $1 eq 'is') {
+                $ignore_dirs{$2} = 1;
+            }
+            else {
+                Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
+            }
+        }
+        else {
+            Carp::croak( qq{Invalid filter specification "$idir"} );
+        }
+    }
+
+    return sub {
+        return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
+    };
+}
+
+sub _compile_file_filter {
+    my ( $opt, $start ) = @_;
+
+    my $ifiles = $opt->{ifiles};
+    $ifiles  ||= [];
+
+    my $ifiles_filters = App::Ack::Filter::Collection->new();
+    
+    foreach my $filter_spec (@{$ifiles}) {
+        if ( $filter_spec =~ /^(\w+):(.+)/ ) {
+            my ($how,$what) = ($1,$2);
+            my $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));
+            $ifiles_filters->add($filter);
+        }
+        else {
+            Carp::croak( qq{Invalid filter specification "$filter_spec"} );
+        }
+    }
+
+    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_list      = $opt->{idirs};
+    my $dont_ignore_dir_list = $opt->{no_ignore_dirs};
+
+    my %ignore_dir_set;
+    my %dont_ignore_dir_set;
+
+    foreach my $filter (@{ $ignore_dir_list }) {
+        if ( $filter =~ /^(\w+):(.*)/ ) {
+            if ( $1 eq 'is' ) {
+                $ignore_dir_set{ $2 } = 1;
+            } else {
+                Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
+            }
+        } else {
+            Carp::croak( qq{Invalid filter specification "$filter"} );
+        }
+    }
+    foreach my $filter (@{ $dont_ignore_dir_list }) {
+        if ( $filter =~ /^(\w+):(.*)/ ) {
+            if ( $1 eq 'is' ) {
+                $dont_ignore_dir_set{ $2 } = 1;
+            } else {
+                Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
+            }
+        } else {
+            Carp::croak( qq{Invalid filter specification "$filter"} );
+        }
+    }
+
+    return sub {
+        # 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_list ) {
+            my ( undef, $dirname ) = File::Spec->splitpath($File::Next::name);
+            my @dirs               = File::Spec->splitdir($dirname);
+
+            my $is_ignoring = 0;
+
+            foreach my $dir ( @dirs ) {
+                if ( $ignore_dir_set{ $dir } ) {
+                    $is_ignoring = 1;
+                }
+                elsif ( $dont_ignore_dir_set{ $dir } ) {
+                    $is_ignoring = 0;
+                }
+            }
+            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
+        unless ( -r _ ) {
+            if ( $App::Ack::report_bad_filenames ) {
+                App::Ack::warn( "${File::Next::name}: cannot open file for reading" );
+            }
+            return 0;
+        }
+
+        my $resource = App::Ack::Resource::Basic->new($File::Next::name);
+        return 0 if ! $resource;
+        if ( $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} ) {
+        $str = "\\b$str" if $str =~ /^\w/;
+        $str = "$str\\b" if $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/ };
+    if ( !$re ) {
+        die "Invalid regex '$str':\n  $@";
+    }
+
+    return $re;
+
+}
+
+{
+
+my @before_ctx_lines;
+my @after_ctx_lines;
+my $is_iterating;
+
+my $has_printed_something;
+
+BEGIN {
+    $has_printed_something = 0;
+}
+
+sub print_matches_in_resource {
+    my ( $resource, $opt ) = @_;
+
+    my $passthru       = $opt->{passthru};
+    my $max_count      = $opt->{m} || -1;
+    my $nmatches       = 0;
+    my $filename       = $resource->name;
+    my $break          = $opt->{break};
+    my $heading        = $opt->{heading};
+    my $ors            = $opt->{print0} ? "\0" : "\n";
+    my $color          = $opt->{color};
+    my $print_filename = $opt->{show_filename};
+
+    my $has_printed_for_this_resource = 0;
+
+    $is_iterating = 1;
+
+    local $opt->{before_context} = $opt->{output} ? 0 : $opt->{before_context};
+    local $opt->{after_context}  = $opt->{output} ? 0 : $opt->{after_context};
+
+    my $n_before_ctx_lines = $opt->{before_context} || 0;
+    my $n_after_ctx_lines  = $opt->{after_context}  || 0;
+
+    @after_ctx_lines = @before_ctx_lines = ();
+
+    my $fh = $resource->open();
+    if ( !$fh ) {
+        if ( $App::Ack::report_bad_filenames ) {
+            App::Ack::warn( "$filename: $!" );
+        }
+        return 0;
+    }
+
+    my $display_filename = $filename;
+    if ( $print_filename && $heading && $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 ( $n_before_ctx_lines || $n_after_ctx_lines ) {
+        my $current_line = <$fh>; # prime the first line of input
+
+        while ( defined $current_line ) {
+            while ( (@after_ctx_lines < $n_after_ctx_lines) && defined($_ = <$fh>) ) {
+                push @after_ctx_lines, $_;
+            }
+
+            local $_ = $current_line;
+            my $former_dot_period = $.;
+            $. -= @after_ctx_lines;
+
+            if ( does_match($opt, $_) ) {
+                if ( !$has_printed_for_this_resource ) {
+                    if ( $break && $has_printed_something ) {
+                        App::Ack::print_blank_line();
+                    }
+                    if ( $print_filename && $heading ) {
+                        App::Ack::print_filename( $display_filename, $ors );
+                    }
+                }
+                print_line_with_context($opt, $filename, $_, $.);
+                $has_printed_for_this_resource = 1;
+                $nmatches++;
+                $max_count--;
+            }
+            elsif ( $passthru ) {
+                chomp; # XXX proper newline handling?
+                # XXX inline this call?
+                if ( $break && !$has_printed_for_this_resource && $has_printed_something ) {
+                    App::Ack::print_blank_line();
+                }
+                print_line_with_options($opt, $filename, $_, $., ':');
+                $has_printed_for_this_resource = 1;
+            }
+            last unless $max_count != 0;
+
+            # I tried doing this with local(), but for some reason,
+            # $. continued to have its new value after the exit of the
+            # enclosing block.  I'm guessing that $. has some extra
+            # magic associated with it or something.  If someone can
+            # tell me why this happened, I would love to know!
+            $. = $former_dot_period; # XXX this won't happen on an exception
+
+            if ( $n_before_ctx_lines ) {
+                push @before_ctx_lines, $current_line;
+                shift @before_ctx_lines while @before_ctx_lines > $n_before_ctx_lines;
+            }
+            if ( $n_after_ctx_lines ) {
+                $current_line = shift @after_ctx_lines;
+            }
+            else {
+                $current_line = <$fh>;
+            }
+        }
+    }
+    else {
+        local $_;
+
+        while ( <$fh> ) {
+            if ( does_match($opt, $_) ) {
+                if ( !$has_printed_for_this_resource ) {
+                    if ( $break && $has_printed_something ) {
+                        App::Ack::print_blank_line();
+                    }
+                    if ( $print_filename && $heading ) {
+                        App::Ack::print_filename( $display_filename, $ors );
+                    }
+                }
+                print_line_with_context($opt, $filename, $_, $.);
+                $has_printed_for_this_resource = 1;
+                $nmatches++;
+                $max_count--;
+            }
+            elsif ( $passthru ) {
+                chomp; # XXX proper newline handling?
+                if ( $break && !$has_printed_for_this_resource && $has_printed_something ) {
+                    App::Ack::print_blank_line();
+                }
+                print_line_with_options($opt, $filename, $_, $., ':');
+                $has_printed_for_this_resource = 1;
+            }
+            last unless $max_count != 0;
+        }
+    }
+
+    $is_iterating = 0; # XXX this won't happen on an exception
+                       #     then again, do we care? ack doesn't really
+                       #     handle exceptions anyway.
+
+    return $nmatches;
+}
+
+sub print_line_with_options {
+    my ( $opt, $filename, $line, $line_no, $separator ) = @_;
+
+    $has_printed_something = 1;
+
+    my $print_filename = $opt->{show_filename};
+    my $print_column   = $opt->{column};
+    my $ors            = $opt->{print0} ? "\0" : "\n";
+    my $heading        = $opt->{heading};
+    my $output_expr    = $opt->{output};
+    my $color          = $opt->{color};
+
+    my @line_parts;
+
+    if( $color ) {
+        $filename = Term::ANSIColor::colored($filename,
+            $ENV{ACK_COLOR_FILENAME});
+        $line_no  = Term::ANSIColor::colored($line_no,
+            $ENV{ACK_COLOR_LINENO});
+    }
+
+    if($print_filename) {
+        if( $heading ) {
+            push @line_parts, $line_no;
+        }
+        else {
+            push @line_parts, $filename, $line_no;
+        }
+
+        if( $print_column ) {
+            push @line_parts, get_match_column();
+        }
+    }
+    if( $output_expr ) {
+        while ( $line =~ /$opt->{regex}/og ) {
+            my $output = eval $output_expr;
+            App::Ack::print( join( $separator, @line_parts, $output ), $ors );
+        }
+    }
+    else {
+        if ( $color ) {
+            $line =~ /$opt->{regex}/o; # this match is redundant, but we need
+                                       # to perfom it in order to get if
+                                       # capture groups are set
+
+            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;
+
+                    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; # flag; 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);
+
+                    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, $opt, $cb ) = @_;
+
+    $is_iterating = 1;
+
+    local $opt->{before_context} = $opt->{output} ? 0 : $opt->{before_context};
+    local $opt->{after_context}  = $opt->{output} ? 0 : $opt->{after_context};
+
+    my $n_before_ctx_lines = $opt->{before_context} || 0;
+    my $n_after_ctx_lines  = $opt->{after_context}  || 0;
+
+    @after_ctx_lines = @before_ctx_lines = ();
+
+    my $fh = $resource->open();
+    if ( !$fh ) {
+        if ( $App::Ack::report_bad_filenames ) {
+            # XXX direct access to filename
+            App::Ack::warn( "$resource->{filename}: $!" );
+        }
+        return;
+    }
+
+    # check for context before the main loop, so we don't
+    # pay for it if we don't need it
+    if ( $n_before_ctx_lines || $n_after_ctx_lines ) {
+        my $current_line = <$fh>; # prime the first line of input
+
+        while ( defined $current_line ) {
+            while ( (@after_ctx_lines < $n_after_ctx_lines) && defined($_ = <$fh>) ) {
+                push @after_ctx_lines, $_;
+            }
+
+            local $_ = $current_line;
+            my $former_dot_period = $.;
+            $. -= @after_ctx_lines;
+
+            last unless $cb->();
+
+            # I tried doing this with local(), but for some reason,
+            # $. continued to have its new value after the exit of the
+            # enclosing block.  I'm guessing that $. has some extra
+            # magic associated with it or something.  If someone can
+            # tell me why this happened, I would love to know!
+            $. = $former_dot_period; # XXX this won't happen on an exception
+
+            if ( $n_before_ctx_lines ) {
+                push @before_ctx_lines, $current_line;
+                shift @before_ctx_lines while @before_ctx_lines > $n_before_ctx_lines;
+            }
+            if ( $n_after_ctx_lines ) {
+                $current_line = shift @after_ctx_lines;
+            }
+            else {
+                $current_line = <$fh>;
+            }
+        }
+    }
+    else {
+        local $_;
+
+        while ( <$fh> ) {
+            last unless $cb->();
+        }
+    }
+
+    $is_iterating = 0; # XXX this won't happen on an exception
+                       #     then again, do we care? ack doesn't really
+                       #     handle exceptions anyway.
+
+    return;
+}
+
+sub get_context {
+    if ( not $is_iterating ) {
+        Carp::croak( 'get_context() called outside of iterate()' );
+    }
+
+    return (
+        scalar(@before_ctx_lines) ? \@before_ctx_lines : undef,
+        scalar(@after_ctx_lines)  ? \@after_ctx_lines  : undef,
+    );
+}
+
+}
+
+{
+
+my $is_first_match;
+my $previous_file_processed;
+my $previous_line_printed;
+
+BEGIN {
+    $is_first_match        = 1;
+    $previous_line_printed = -1;
+}
+
+sub print_line_with_context {
+    my ( $opt, $filename, $matching_line, $line_no ) = @_;
+
+    my $heading = $opt->{heading};
+
+    if( !defined($previous_file_processed) ||
+      $previous_file_processed ne $filename ) {
+        $previous_file_processed = $filename;
+        $previous_line_printed   = -1;
+
+        if( $heading ) {
+            $is_first_match = 1;
+        }
+    }
 
-If you want to see lines near your match, use the C<--A>, C<--B>
-and C<--C> switches for displaying context.
+    my $ors                 = $opt->{print0} ? "\0" : "\n";
+    my $match_word          = $opt->{w};
+    my $is_tracking_context = $opt->{after_context} || $opt->{before_context};
+    my $output_expr         = $opt->{output};
 
-=head1 AUTHOR
+    $matching_line =~ s/[\r\n]+$//g;
 
-Andy Lester, C<< <andy at petdance.com> >>
+    my ( $before_context, $after_context ) = get_context();
 
-=head1 BUGS
+    if ( $before_context ) {
+        my $first_line = $. - @{$before_context};
 
-Please report any bugs or feature requests to the issues list at
-Github: L<http://github.com/petdance/ack/issues>
+        if ( $first_line <= $previous_line_printed ) {
+            splice @{$before_context}, 0, $previous_line_printed - $first_line + 1;
+            $first_line = $. - @{$before_context};
+        }
+        if ( @{$before_context} ) {
+            my $offset = @{$before_context};
 
-=head1 ENHANCEMENTS
+            if( !$is_first_match && $previous_line_printed != $first_line - 1 ) {
+                App::Ack::print('--', $ors);
+            }
+            foreach my $line (@{$before_context}) {
+                my $context_line_no = $. - $offset;
+                if ( $context_line_no <= $previous_line_printed ) {
+                    next;
+                }
 
-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.
+                chomp $line;
+                print_line_with_options($opt, $filename, $line, $context_line_no, '-');
+                $previous_line_printed = $context_line_no;
+                $offset--;
+            }
+        }
+    }
 
-There is a list of enhancements I want to make to F<ack> in the ack
-issues list at Github: L<http://github.com/petdance/ack/issues>
+    if ( $. > $previous_line_printed ) {
+        if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {
+            App::Ack::print('--', $ors);
+        }
 
-Patches are always welcome, but patches with tests get the most
-attention.
+        print_line_with_options($opt, $filename, $matching_line, $line_no, ':');
+        $previous_line_printed = $.;
+    }
 
-=head1 SUPPORT
+    if($after_context) {
+        my $offset = 1;
+        foreach my $line (@{$after_context}) {
+            # XXX improve this!
+            if ( $previous_line_printed >= $. + $offset ) {
+                $offset++;
+                next;
+            }
+            chomp $line;
+            my $separator = ($opt->{regex} && does_match( $opt, $line )) ? ':' : '-';
+            print_line_with_options($opt, $filename, $line, $. + $offset, $separator);
+            $previous_line_printed = $. + $offset;
+            $offset++;
+        }
+    }
 
-Support for and information about F<ack> can be found at:
+    $is_first_match = 0;
 
-=over 4
+    return;
+}
 
-=item * The ack homepage
+}
 
-L<http://betterthangrep.com/>
+{
 
-=item * The ack issues list at Github
+my $match_column_number;
 
-L<http://github.com/petdance/ack/issues>
+# does_match() MUST have an $opt->{regex} set.
 
-=item * AnnoCPAN: Annotated CPAN documentation
+sub does_match {
+    my ( $opt, $line ) = @_;
 
-L<http://annocpan.org/dist/ack>
+    $match_column_number = undef;
 
-=item * CPAN Ratings
+    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;
+        }
+    }
+}
 
-L<http://cpanratings.perl.org/d/ack>
+sub get_match_column {
+    return $match_column_number;
+}
 
-=item * Search CPAN
+}
 
-L<http://search.cpan.org/dist/ack>
+sub resource_has_match {
+    my ( $resource, $opt ) = @_;
 
-=item * Git source repository
+    my $has_match = 0;
+    my $fh = $resource->open();
+    if ( !$fh ) {
+        if ( $App::Ack::report_bad_filenames ) {
+            # XXX direct access to filename
+            App::Ack::warn( "$resource->{filename}: $!" );
+        }
+    }
+    else {
+        my $opt_v = $opt->{v};
+        my $re    = $opt->{regex};
+        while ( <$fh> ) {
+            if (/$re/o xor $opt_v) {
+                $has_match = 1;
+                last;
+            }
+        }
+        close $fh;
+    }
+
+    return $has_match;
+}
+
+sub count_matches_in_resource {
+    my ( $resource, $opt ) = @_;
+
+    my $nmatches = 0;
+    my $fh = $resource->open();
+    if ( !$fh ) {
+        if ( $App::Ack::report_bad_filenames ) {
+            # XXX direct access to filename
+            App::Ack::warn( "$resource->{filename}: $!" );
+        }
+    }
+    else {
+        my $opt_v = $opt->{v};
+        my $re    = $opt->{regex};
+        while ( <$fh> ) {
+            ++$nmatches if (/$re/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 );
+
+    $App::Ack::report_bad_filenames = !$opt->{dont_report_bad_filenames};
+
+    if ( $opt->{flush} ) {
+        $| = 1;
+    }
+
+    if ( not defined $opt->{color} ) {
+        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} = !App::Ack::output_to_pipe();
+    }
+
+    if ( defined($opt->{H}) || defined($opt->{h}) ) {
+        $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 );
+        my $regex = $opt->{regex};
+        $regex = shift @ARGV if not defined $regex;
+        $opt->{regex} = build_regex( $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 {
+            my $regex = $opt->{regex};
+            $regex = shift @ARGV if not defined $regex;
+            $opt->{regex} = build_regex( $regex, $opt );
+        }
+        my @start;
+        if ( not defined $opt->{files_from} ) {
+            @start = @ARGV;
+        }
+        if ( !exists($opt->{show_filename}) ) {
+            unless(@start == 1 && !(-d $start[0])) {
+                $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 $print_filenames = $opt->{show_filename};
+    my $max_count       = $opt->{m};
+    my $ors             = $opt->{print0} ? "\0" : "\n";
+    my $only_first      = $opt->{1};
+
+    my $nmatches    = 0;
+    my $total_count = 0;
+RESOURCES:
+    while ( my $resource = $resources->next ) {
+        # XXX this variable name combined with what we're trying
+        # to do makes no sense.
+
+        # XXX Combine the -f and -g functions
+        if ( $opt->{f} ) {
+            # XXX printing should probably happen inside of App::Ack
+            if ( $opt->{show_types} ) {
+                show_types( $resource, $ors );
+            }
+            else {
+                App::Ack::print( $resource->name, $ors );
+            }
+            ++$nmatches;
+            last RESOURCES if defined($max_count) && $nmatches >= $max_count;
+        }
+        elsif ( $opt->{g} ) {
+            my $is_match = ( $resource->name =~ /$opt->{regex}/o );
+            if ( $opt->{v} ? !$is_match : $is_match ) {
+                if ( $opt->{show_types} ) {
+                    show_types( $resource, $ors );
+                }
+                else {
+                    App::Ack::print( $resource->name, $ors );
+                }
+                ++$nmatches;
+                last RESOURCES if defined($max_count) && $nmatches >= $max_count;
+            }
+        }
+        elsif ( $opt->{lines} ) {
+            my $print_filename = $opt->{show_filename};
+            my $passthru       = $opt->{passthru};
+
+            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, $opt, sub {
+                chomp;
+
+                if ( $line_numbers{$.} ) {
+                    print_line_with_context($opt, $filename, $_, $.);
+                }
+                elsif ( $passthru ) {
+                    print_line_with_options($opt, $filename, $_, $., ':');
+                }
+                return 1;
+            });
+        }
+        elsif ( $opt->{count} ) {
+            my $matches_for_this_file = count_matches_in_resource( $resource, $opt );
 
-L<http://github.com/petdance/ack>
+            unless ( $opt->{show_filename} ) {
+                $total_count += $matches_for_this_file;
+                next RESOURCES;
+            }
+
+            if ( !$opt->{l} || $matches_for_this_file > 0) {
+                if ( $print_filenames ) {
+                    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, $opt );
+
+            if ( $opt->{L} ? !$is_match : $is_match ) {
+                App::Ack::print( $resource->name, $ors );
+                ++$nmatches;
+
+                last RESOURCES if $only_first;
+                last RESOURCES if defined($max_count) && $nmatches >= $max_count;
+            }
+        }
+        else {
+            $nmatches += print_matches_in_resource( $resource, $opt );
+            if ( $nmatches && $only_first ) {
+                last RESOURCES;
+            }
+        }
+    }
+
+    if ( $opt->{count} && !$opt->{show_filename} ) {
+        App::Ack::print( $total_count, "\n" );
+    }
+
+    close $App::Ack::fh;
+    App::Ack::exit_from_ack( $nmatches );
+}
+
+
+
+=head1 NAME
+
+ack - grep-like text finder
+
+=head1 SYNOPSIS
+
+    ack [options] PATTERN [FILE...]
+    ack -f [options] [DIRECTORY...]
+
+=head1 DESCRIPTION
+
+Ack is designed as a replacement for 99% of the uses of F<grep>.
+
+Ack searches the named input FILEs (or standard input if no files
+are named, or the file name - is given) for lines containing a match
+to the given PATTERN.  By default, ack prints the matching lines.
+
+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
 
-=head1 ACKNOWLEDGEMENTS
+Run I<ack> with the C<--dump> option to see what settings are set.
 
-How appropriate to have I<ack>nowledgements!
+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.
 
-Thanks to everyone who has contributed to ack in any way, including
-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
-and Pete Krawczyk.
+=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.
 
-=head1 COPYRIGHT & LICENSE
+For a complete list of directories that do not get searched, run
+C<ack --dump>.
 
-Copyright 2005-2011 Andy Lester.
+=head1 WHEN TO USE GREP
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the Artistic License v2.0.
+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.
 
-=cut
-package File::Next;
+E.g., searching through huge files looking for regexes that can be
+expressed with I<grep> syntax should be quicker with I<grep>.
 
-use strict;
-use warnings;
+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
 
-our $VERSION = '1.06';
+=over 4
 
+=item B<--ackrc>
 
+Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.
 
-use File::Spec ();
+=item B<-A I<NUM>>, B<--after-context=I<NUM>>
 
+Print I<NUM> lines of trailing context after matching lines.
 
-our $name; # name of the current file
-our $dir;  # dir of the current file
+=item B<-B I<NUM>>, B<--before-context=I<NUM>>
 
-our %files_defaults;
-our %skip_dirs;
+Print I<NUM> lines of leading context before matching lines.
 
-BEGIN {
-    %files_defaults = (
-        file_filter     => undef,
-        descend_filter  => undef,
-        error_handler   => sub { CORE::die @_ },
-        sort_files      => undef,
-        follow_symlinks => 1,
-    );
-    %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
-}
+=item B<--[no]break>
 
+Print a break between results from different files. On by default
+when used interactively.
 
-sub files {
-    ($_[0] eq __PACKAGE__) && die 'File::Next::files must not be invoked as File::Next->files';
+=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
 
-    my ($parms,@queue) = _setup( \%files_defaults, @_ );
-    my $filter = $parms->{file_filter};
+Print I<NUM> lines (default 2) of context around matching lines.
 
-    return sub {
-        while (@queue) {
-            my ($dir,$file,$fullpath) = splice( @queue, 0, 3 );
-            if ( -f $fullpath ) {
-                if ( $filter ) {
-                    local $_ = $file;
-                    local $File::Next::dir = $dir;
-                    local $File::Next::name = $fullpath;
-                    next if not $filter->();
-                }
-                return wantarray ? ($dir,$file,$fullpath) : $fullpath;
-            }
-            elsif ( -d _ ) {
-                unshift( @queue, _candidate_files( $parms, $fullpath ) );
-            }
-        } # while
+=item B<-c>, B<--count>
 
-        return;
-    }; # iterator
-}
+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.
 
-sub sort_standard($$)   { return $_[0]->[1] cmp $_[1]->[1] }
-sub sort_reverse($$)    { return $_[1]->[1] cmp $_[0]->[1] }
+=item B<--color-match=I<color>>
 
-sub reslash {
-    my $path = shift;
+Sets the color to be used for matches.
 
-    my @parts = split( /\//, $path );
+=item B<--color-lineno=I<color>>
 
-    return $path if @parts < 2;
+Sets the color to be used for line numbers.
 
-    return File::Spec->catfile( @parts );
-}
+=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>
 
-sub _setup {
-    my $defaults = shift;
-    my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
+Dumps the default ack options to standard output.  This is useful for
+when you want to customize the defaults.
 
-    my %passed_parms = %{$passed_parms};
+=item B<--dump>
 
-    my $parms = {};
-    for my $key ( keys %{$defaults} ) {
-        $parms->{$key} =
-            exists $passed_parms{$key}
-                ? delete $passed_parms{$key}
-                : $defaults->{$key};
-    }
+Writes the list of options loaded and where they came from to standard
+output.  Handy for debugging.
 
-    # 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" );
-    }
+=item B<--[no]env>
 
-    # 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;
+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.
 
-    for ( @_ ) {
-        my $start = reslash( $_ );
-        if (-d $start) {
-            push @queue, ($start,undef,$start);
-        }
-        else {
-            push @queue, (undef,$start,$start);
-        }
-    }
+=item B<--flush>
 
-    return ($parms,@queue);
-}
+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>
 
-sub _candidate_files {
-    my $parms = shift;
-    my $dir = shift;
+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.
 
-    my $dh;
-    if ( !opendir $dh, $dir ) {
-        $parms->{error_handler}->( "$dir: $!" );
-        return;
-    }
+=item B<--files-from=I<FILE>>
 
-    my @newfiles;
-    my $descend_filter = $parms->{descend_filter};
-    my $follow_symlinks = $parms->{follow_symlinks};
-    my $sort_sub = $parms->{sort_files};
+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.
 
-    for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
-        my $has_stat;
+=item B<--[no]filter>
 
-        # Only do directory checking if we have a descend_filter
-        my $fullpath = File::Spec->catdir( $dir, $file );
-        if ( !$follow_symlinks ) {
-            next if -l $fullpath;
-            $has_stat = 1;
-        }
+Forces ack to act as if it were receiving input via a pipe.
 
-        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, [ $dir, $file, $fullpath ] );
-        }
-        else {
-            push( @newfiles, $dir, $file, $fullpath );
-        }
-    }
-    closedir $dh;
+=item B<--[no]follow>
 
-    if ( $sort_sub ) {
-        return map { @{$_} } sort $sort_sub @newfiles;
-    }
+Follow or don't follow symlinks, other than whatever starting files
+or directories were specified on the command line.
 
-    return @newfiles;
-}
+This is off by default.
 
+=item B<-g I<PATTERN>>
 
-1; # End of File::Next
-package App::Ack;
+Print files where the relative path + filename matches I<PATTERN>.
 
-use warnings;
-use strict;
+=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>
 
-our $VERSION;
-our $COPYRIGHT;
-BEGIN {
-    $VERSION = '1.96';
-    $COPYRIGHT = 'Copyright 2005-2011 Andy Lester.';
-}
+Print the filename for each match. This is the default unless searching
+a single explicitly specified file.
 
-our $fh;
+=item B<-h>, B<--no-filename>
 
-BEGIN {
-    $fh = *STDOUT;
-}
+Suppress the prefixing of filenames on output when multiple files are
+searched.
 
+=item B<--[no]heading>
 
-our %types;
-our %type_wanted;
-our %mappings;
-our %ignore_dirs;
+Print a filename heading above each file's results.  This is the default
+when used interactively.
 
-our $input_from_pipe;
-our $output_to_pipe;
+=item B<--help>, B<-?>
 
-our $dir_sep_chars;
-our $is_cygwin;
-our $is_windows;
+Print a short help statement.
 
-use File::Spec ();
-use File::Glob ':glob';
-use Getopt::Long ();
+=item B<--help-types>, B<--help=types>
 
-BEGIN {
-    %ignore_dirs = (
-        '.bzr'              => 'Bazaar',
-        '.cdv'              => 'Codeville',
-        '~.dep'             => 'Interface Builder',
-        '~.dot'             => 'Interface Builder',
-        '~.nib'             => 'Interface Builder',
-        '~.plst'            => 'Interface Builder',
-        '.git'              => 'Git',
-        '.hg'               => 'Mercurial',
-        '.pc'               => 'quilt',
-        '.svn'              => 'Subversion',
-        _MTN                => 'Monotone',
-        blib                => 'Perl module building',
-        CVS                 => 'CVS',
-        RCS                 => 'RCS',
-        SCCS                => 'SCCS',
-        _darcs              => 'darcs',
-        _sgbak              => 'Vault/Fortress',
-        'autom4te.cache'    => 'autoconf',
-        'cover_db'          => 'Devel::Cover',
-        _build              => 'Module::Build',
-    );
+Print all known types.
 
-    %mappings = (
-        actionscript => [qw( as mxml )],
-        ada         => [qw( ada adb ads )],
-        asm         => [qw( asm s )],
-        batch       => [qw( bat cmd )],
-        binary      => q{Binary files, as defined by Perl's -B op (default: off)},
-        cc          => [qw( c h xs )],
-        cfmx        => [qw( cfc cfm cfml )],
-        clojure     => [qw( clj )],
-        cpp         => [qw( cpp cc cxx m hpp hh h hxx )],
-        csharp      => [qw( cs )],
-        css         => [qw( css )],
-        delphi      => [qw( pas int dfm nfm dof dpk dproj groupproj bdsgroup bdsproj )],
-        elisp       => [qw( el )],
-        erlang      => [qw( erl hrl )],
-        fortran     => [qw( f f77 f90 f95 f03 for ftn fpp )],
-        go          => [qw( go )],
-        groovy      => [qw( groovy gtmpl gpp grunit )],
-        haskell     => [qw( hs lhs )],
-        hh          => [qw( h )],
-        html        => [qw( htm html shtml xhtml )],
-        java        => [qw( java properties )],
-        js          => [qw( js )],
-        jsp         => [qw( jsp jspx jhtm jhtml )],
-        lisp        => [qw( lisp lsp )],
-        lua         => [qw( lua )],
-        make        => q{Makefiles (including *.mk and *.mak)},
-        mason       => [qw( mas mhtml mpl mtxt )],
-        objc        => [qw( m h )],
-        objcpp      => [qw( mm h )],
-        ocaml       => [qw( ml mli )],
-        parrot      => [qw( pir pasm pmc ops pod pg tg )],
-        perl        => [qw( pl pm pm6 pod t )],
-        php         => [qw( php phpt php3 php4 php5 phtml)],
-        plone       => [qw( pt cpt metadata cpy py )],
-        python      => [qw( py )],
-        rake        => q{Rakefiles},
-        ruby        => [qw( rb rhtml rjs rxml erb rake spec )],
-        scala       => [qw( scala )],
-        scheme      => [qw( scm ss )],
-        shell       => [qw( sh bash csh tcsh ksh zsh )],
-        skipped     => q{Files, but not directories, normally skipped by ack (default: off)},
-        smalltalk   => [qw( st )],
-        sql         => [qw( sql ctl )],
-        tcl         => [qw( tcl itcl itk )],
-        tex         => [qw( tex cls sty )],
-        text        => q{Text files, as defined by Perl's -T op (default: off)},
-        tt          => [qw( tt tt2 ttml )],
-        vb          => [qw( bas cls frm ctl vb resx )],
-        verilog     => [qw( v vh sv )],
-        vhdl        => [qw( vhd vhdl )],
-        vim         => [qw( vim )],
-        yaml        => [qw( yaml yml )],
-        xml         => [qw( xml dtd xsl xslt ent )],
-    );
+=item B<-i>, B<--ignore-case>
 
-    while ( my ($type,$exts) = each %mappings ) {
-        if ( ref $exts ) {
-            for my $ext ( @{$exts} ) {
-                push( @{$types{$ext}}, $type );
-            }
-        }
-    }
-    # add manually Makefile extensions
-    push @{$types{$_}}, 'make' for qw{ mk mak };
+Ignore case distinctions in PATTERN
 
-    # These have to be checked before any filehandle diddling.
-    $output_to_pipe  = not -t *STDOUT;
-    $input_from_pipe = -p STDIN;
+=item B<--ignore-ack-defaults>
 
-    $is_cygwin       = ($^O eq 'cygwin');
-    $is_windows      = ($^O =~ /MSWin32/);
-    $dir_sep_chars   = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
-}
+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>>
 
-sub read_ackrc {
-    my @files = ( $ENV{ACKRC} );
-    my @dirs =
-        $is_windows
-            ? ( $ENV{HOME}, $ENV{USERPROFILE} )
-            : ( '~', $ENV{HOME} );
-    for my $dir ( grep { defined } @dirs ) {
-        for my $file ( '.ackrc', '_ackrc' ) {
-            push( @files, bsd_glob( "$dir/$file", GLOB_TILDE ) );
-        }
-    }
-    for my $filename ( @files ) {
-        if ( defined $filename && -e $filename ) {
-            open( my $fh, '<', $filename ) or App::Ack::die( "$filename: $!\n" );
-            my @lines = grep { /./ && !/^\s*#/ } <$fh>;
-            chomp @lines;
-            close $fh or App::Ack::die( "$filename: $!\n" );
+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).
 
-            # get rid of leading and trailing whitespaces
-            for ( @lines ) {
-               s/^\s+//;
-               s/\s+$//;
-            }
+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.
 
-            return @lines;
-        }
-    }
+=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>
 
-    return;
-}
+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>
 
-sub get_command_line_options {
-    my %opt = (
-        pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
-    );
+Limit selected files to those with types that ack knows about.  This is
+equivalent to the default behavior found in ack 1.
 
-    my $getopt_specs = {
-        1                       => sub { $opt{1} = $opt{m} = 1 },
-        'A|after-context=i'     => \$opt{after_context},
-        'B|before-context=i'    => \$opt{before_context},
-        'C|context:i'           => sub { shift; my $val = shift; $opt{before_context} = $opt{after_context} = ($val || 2) },
-        'a|all-types'           => \$opt{all},
-        '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},
-        'env!'                  => sub { }, # ignore this option, it is handled beforehand
-        f                       => \$opt{f},
-        flush                   => \$opt{flush},
-        'follow!'               => \$opt{follow},
-        'g=s'                   => sub { shift; $opt{G} = shift; $opt{f} = 1 },
-        'G=s'                   => \$opt{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},
-        'invert-file-match'     => \$opt{invert_file_match},
-        'lines=s'               => sub { shift; my $val = shift; push @{$opt{lines}}, $val },
-        'l|files-with-matches'  => \$opt{l},
-        'L|files-without-matches' => sub { $opt{l} = $opt{v} = 1 },
-        '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'               => \$opt{pager},
-        'nopager'               => sub { $opt{pager} = undef },
-        'passthru'              => \$opt{passthru},
-        'print0'                => \$opt{print0},
-        'Q|literal'             => \$opt{Q},
-        'r|R|recurse'           => sub { $opt{n} = 0 },
-        'show-types'            => \$opt{show_types},
-        'smart-case!'           => \$opt{smart_case},
-        'sort-files'            => \$opt{sort_files},
-        'u|unrestricted'        => \$opt{u},
-        'v|invert-match'        => \$opt{v},
-        'w|word-regexp'         => \$opt{w},
-
-        'ignore-dirs=s'         => sub { shift; my $dir = remove_dir_sep( shift ); $ignore_dirs{$dir} = '--ignore-dirs' },
-        'noignore-dirs=s'       => sub { shift; my $dir = remove_dir_sep( shift ); delete $ignore_dirs{$dir} },
-
-        'version'   => sub { print_version_statement(); exit; },
-        'help|?:s'  => sub { shift; show_help(@_); exit; },
-        'help-types'=> sub { show_help_types(); exit; },
-        'man'       => sub {
-            require Pod::Usage;
-            Pod::Usage::pod2usage({
-                -verbose => 2,
-                -exitval => 0,
-            });
-        },
+=item B<--lines=I<NUM>>
 
-        'type=s'    => sub {
-            # Whatever --type=xxx they specify, set it manually in the hash
-            my $dummy = shift;
-            my $type = shift;
-            my $wanted = ($type =~ s/^no//) ? 0 : 1; # must not be undef later
+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.
 
-            if ( exists $type_wanted{ $type } ) {
-                $type_wanted{ $type } = $wanted;
-            }
-            else {
-                App::Ack::die( qq{Unknown --type "$type"} );
-            }
-        }, # type sub
-    };
+=item B<-l>, B<--files-with-matches>
 
-    # Stick any default switches at the beginning, so they can be overridden
-    # by the command line switches.
-    unshift @ARGV, split( ' ', $ENV{ACK_OPTIONS} ) if defined $ENV{ACK_OPTIONS};
+Only print the filenames of matching files, instead of the matching text.
 
-    # first pass through options, looking for type definitions
-    def_types_from_ARGV();
+=item B<-L>, B<--files-without-matches>
 
-    for my $i ( filetypes_supported() ) {
-        $getopt_specs->{ "$i!" } = \$type_wanted{ $i };
-    }
+Only print the filenames of files that do I<NOT> match.
 
+=item B<--match I<PATTERN>>
 
-    my $parser = Getopt::Long::Parser->new();
-    $parser->configure( 'bundling', 'no_ignore_case', );
-    $parser->getoptions( %{$getopt_specs} ) or
-        App::Ack::die( 'See ack --help, ack --help-types or ack --man for options.' );
+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.
 
-    my $to_screen = not output_to_pipe();
-    my %defaults = (
-        all            => 0,
-        color          => $to_screen,
-        follow         => 0,
-        break          => $to_screen,
-        heading        => $to_screen,
-        before_context => 0,
-        after_context  => 0,
-    );
-    if ( $is_windows && $defaults{color} && not $ENV{ACK_PAGER_COLOR} ) {
-        if ( $ENV{ACK_PAGER} || not eval { require Win32::Console::ANSI } ) {
-            $defaults{color} = 0;
-        }
-    }
-    if ( $to_screen && $ENV{ACK_PAGER_COLOR} ) {
-        $defaults{color} = 1;
-    }
+    # search for foo and bar in given files
+    ack file1 t/file* --match foo
+    ack file1 t/file* --match bar
 
-    while ( my ($key,$value) = each %defaults ) {
-        if ( not defined $opt{$key} ) {
-            $opt{$key} = $value;
-        }
-    }
+=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:
 
-    if ( defined $opt{m} && $opt{m} <= 0 ) {
-        App::Ack::die( '-m must be greater than zero' );
-    }
+    # Watch a log file, and highlight a certain IP address
+    $ tail -f ~/access.log | ack --passthru 123.45.67.89
 
-    for ( qw( before_context after_context ) ) {
-        if ( defined $opt{$_} && $opt{$_} < 0 ) {
-            App::Ack::die( "--$_ may not be negative" );
-        }
-    }
+=item B<--print0>
 
-    if ( defined( my $val = $opt{output} ) ) {
-        $opt{output} = eval qq[ sub { "$val" } ];
-    }
-    if ( defined( my $l = $opt{lines} ) ) {
-        # --line=1 --line=5 is equivalent to --line=1,5
-        my @lines = split( /,/, join( ',', @{$l} ) );
+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.
 
-        # --line=1-3 is equivalent to --line=1,2,3
-        @lines = map {
-            my @ret;
-            if ( /-/ ) {
-                my ($from, $to) = split /-/, $_;
-                if ( $from > $to ) {
-                    App::Ack::warn( "ignoring --line=$from-$to" );
-                    @ret = ();
-                }
-                else {
-                    @ret = ( $from .. $to );
-                }
-            }
-            else {
-                @ret = ( $_ );
-            };
-            @ret
-        } @lines;
+    # remove all files of type html
+    ack -f --html --print0 | xargs -0 rm -f
 
-        if ( @lines ) {
-            my %uniq;
-            @uniq{ @lines } = ();
-            $opt{lines} = [ sort { $a <=> $b } keys %uniq ];   # numerical sort and each line occurs only once!
-        }
-        else {
-            # happens if there are only ignored --line directives
-            App::Ack::die( 'All --line options are invalid.' );
-        }
-    }
+=item B<-Q>, B<--literal>
 
-    return \%opt;
-}
+Quote all metacharacters in PATTERN, it is treated as a literal.
 
+=item B<-r>, B<-R>, B<--recurse>
 
-sub def_types_from_ARGV {
-    my @typedef;
+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.
 
-    my $parser = Getopt::Long::Parser->new();
-        # pass_through   => leave unrecognized command line arguments alone
-        # no_auto_abbrev => otherwise -c is expanded and not left alone
-    $parser->configure( 'no_ignore_case', 'pass_through', 'no_auto_abbrev' );
-    $parser->getoptions(
-        'type-set=s' => sub { shift; push @typedef, ['c', shift] },
-        'type-add=s' => sub { shift; push @typedef, ['a', shift] },
-    ) or App::Ack::die( 'See ack --help or ack --man for options.' );
+=item B<-s>
 
-    for my $td (@typedef) {
-        my ($type, $ext) = split /=/, $td->[1];
+Suppress error messages about nonexistent or unreadable files.  This is taken
+from fgrep.
 
-        if ( $td->[0] eq 'c' ) {
-            # type-set
-            if ( exists $mappings{$type} ) {
-                # can't redefine types 'make', 'skipped', 'text' and 'binary'
-                App::Ack::die( qq{--type-set: Builtin type "$type" cannot be changed.} )
-                    if ref $mappings{$type} ne 'ARRAY';
+=item B<--[no]smart-case>, B<--no-smart-case>
 
-                delete_type($type);
-            }
-        }
-        else {
-            # type-add
+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.
 
-            # can't append to types 'make', 'skipped', 'text' and 'binary'
-            App::Ack::die( qq{--type-add: Builtin type "$type" cannot be changed.} )
-                if exists $mappings{$type} && ref $mappings{$type} ne 'ARRAY';
+B<-i> always overrides this option.
 
-            App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} )
-                unless exists $mappings{$type};
-        }
+=item B<--sort-files>
 
-        my @exts = split /,/, $ext;
-        s/^\.// for @exts;
+Sorts the found files lexicographically.  Use this if you want your file
+listings to be deterministic between runs of I<ack>.
 
-        if ( !exists $mappings{$type} || ref($mappings{$type}) eq 'ARRAY' ) {
-            push @{$mappings{$type}}, @exts;
-            for my $e ( @exts ) {
-                push @{$types{$e}}, $type;
-            }
-        }
-        else {
-            App::Ack::die( qq{Cannot append to type "$type".} );
-        }
-    }
+=item B<--show-types>
 
-    return;
-}
+Outputs the filetypes that ack associates with each file.
 
+Works with B<-f> and B<-g> options.
 
-sub delete_type {
-    my $type = shift;
+=item B<--type=[no]TYPE>
 
-    App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} )
-        unless ref $mappings{$type} eq 'ARRAY';
+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>.
 
-    delete $mappings{$type};
-    delete $type_wanted{$type};
-    for my $ext ( keys %types ) {
-        $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ];
-    }
-}
+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.
 
-sub ignoredir_filter {
-    return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
-}
+See I<ack --help=types> for a list of valid types.
 
+=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>
 
-sub remove_dir_sep {
-    my $path = shift;
-    $path =~ s/[$dir_sep_chars]$//;
+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">.
 
-    return $path;
-}
 
+=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>
 
-use constant TEXT => 'text';
+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">.
 
-sub filetypes {
-    my $filename = shift;
+=item B<--type-del I<TYPE>>
 
-    my $basename = $filename;
-    $basename =~ s{.*[$dir_sep_chars]}{};
+The filters associated with TYPE are removed from Ack, and are no longer considered
+for searches.
 
-    return 'skipped' unless is_searchable( $basename );
+=item B<-v>, B<--invert-match>
 
-    my $lc_basename = lc $basename;
-    return ('make',TEXT)        if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile';
-    return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile';
+Invert match: select non-matching lines
 
-    # If there's an extension, look it up
-    if ( $filename =~ m{\.([^\.$dir_sep_chars]+)$}o ) {
-        my $ref = $types{lc $1};
-        return (@{$ref},TEXT) if $ref;
-    }
+=item B<--version>
 
-    # At this point, we can't tell from just the name.  Now we have to
-    # open it and look inside.
+Display version and copyright information.
 
-    return unless -e $filename;
-    # From Elliot Shank:
-    #     I can't see any reason that -r would fail on these-- the ACLs look
-    #     fine, and no program has any of them open, so the busted Windows
-    #     file locking model isn't getting in there.  If I comment the if
-    #     statement out, everything works fine
-    # So, for cygwin, don't bother trying to check for readability.
-    if ( !$is_cygwin ) {
-        if ( !-r $filename ) {
-            App::Ack::warn( "$filename: Permission denied" );
-            return;
-        }
-    }
+=item B<-w>, B<--word-regexp>
 
-    return 'binary' if -B $filename;
+Force PATTERN to match only whole words.  The PATTERN is wrapped with
+C<\b> metacharacters.
 
-    # If there's no extension, or we don't recognize it, check the shebang line
-    my $fh;
-    if ( !open( $fh, '<', $filename ) ) {
-        App::Ack::warn( "$filename: $!" );
-        return;
-    }
-    my $header = <$fh>;
-    close $fh;
+=item B<-x>
 
-    if ( $header =~ /^#!/ ) {
-        return ($1,TEXT)       if $header =~ /\b(ruby|lua|p(?:erl|hp|ython))-?(\d[\d.]*)?\b/;
-        return ('shell',TEXT)  if $header =~ /\b(?:ba|t?c|k|z)?sh\b/;
-    }
-    else {
-        return ('xml',TEXT)    if $header =~ /\Q<?xml /i;
-    }
+An abbreviation for B<--files-from=->; the list of files to search are read
+from standard input, with one line per file.
 
-    return (TEXT);
-}
+=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.
 
-sub is_searchable {
-    my $filename = shift;
+=item B<--thpppt>
 
-    # If these are updated, update the --help message
-    return if $filename =~ /[.]bak$/;
-    return if $filename =~ /~$/;
-    return if $filename =~ m{^#.*#$}o;
-    return if $filename =~ m{^core\.\d+$}o;
-    return if $filename =~ m{[._].*\.swp$}o;
-    return if $filename =~ /[.-]min\.js$/;
+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.
 
-    return 1;
-}
+=item B<--bar>
 
+Check with the admiral for traps.
 
-sub build_regex {
-    my $str = shift;
-    my $opt = shift;
+=item B<--cathy>
 
-    defined $str or App::Ack::die( 'No regular expression found.' );
+Chocolate, Chocolate, Chocolate!
 
-    $str = quotemeta( $str ) if $opt->{Q};
-    if ( $opt->{w} ) {
-        $str = "\\b$str" if $str =~ /^\w/;
-        $str = "$str\\b" if $str =~ /\w$/;
-    }
+=back
 
-    my $regex_is_lc = $str eq lc $str;
-    if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
-        $str = "(?i)$str";
-    }
+=head1 THE .ackrc FILE
 
-    return $str;
-}
+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
 
-sub check_regex {
-    my $regex = shift;
+    # Always color, even if piping to a another program
+    --color
 
-    return unless defined $regex;
+    # Use "less -r" as my pager
+    --pager=less -r
 
-    eval { qr/$regex/ };
-    if ($@) {
-        (my $error = $@) =~ s/ at \S+ line \d+.*//;
-        chomp($error);
-        App::Ack::die( "Invalid regex '$regex':\n  $error" );
-    }
+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>.
 
-    return;
-}
+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.
 
+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.
 
-sub warn {
-    return CORE::warn( _my_program(), ': ', @_, "\n" );
-}
+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:
 
-sub die {
-    return CORE::die( _my_program(), ': ', @_, "\n" );
-}
+  --type-set=eiffel:ext:e,eiffel
 
-sub _my_program {
-    require File::Basename;
-    return File::Basename::basename( $0 );
-}
+or writing on separate lines
 
+  --type-set
+  eiffel:ext:e,eiffel
 
+The following does B<NOT> work in the F<.ackrc> file:
 
-sub filetypes_supported {
-    return keys %mappings;
-}
+  --type-set eiffel:ext:e,eiffel
 
-sub _get_thpppt {
-    my $y = q{_   /|,\\'!.x',=(www)=,   U   };
-    $y =~ tr/,x!w/\nOo_/;
-    return $y;
-}
 
-sub _thpppt {
-    my $y = _get_thpppt();
-    App::Ack::print( "$y ack $_[0]!\n" );
-    exit 0;
-}
+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>
 
-sub _key {
-    my $str = lc shift;
-    $str =~ s/[^a-z]//g;
+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>.
 
-    return $str;
-}
+=over 4
 
+=item is:I<FILENAME>
 
-sub show_help {
-    my $help_arg = shift || 0;
+I<is> filters match the target filename exactly.  It takes exactly one
+argument, which is the name of the file to match.
 
-    return show_help_types() if $help_arg =~ /^types?/;
+Example:
 
-    my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs );
+    --type-set make:is:Makefile
 
-    App::Ack::print( <<"END_OF_HELP" );
-Usage: ack [OPTION]... PATTERN [FILE]
+=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]
 
-Search for PATTERN in each source file in the tree from cwd on down.
-If [FILES] is specified, then only those files/directories are checked.
-ack may also search STDIN, but only if no FILE are specified, or if
-one of FILES is "-".
+I<ext> filters match the extension of the target file against a list
+of extensions.  No leading dot is needed for the extensions.
 
-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:
 
-Example: ack -i select
+    --type-set perl:ext:pl,pm,t
 
-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
+=item match:I<PATTERN>
 
-Search output:
-  --line=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
-  -o                    Show only the part of a line matching PATTERN
-                        (turns off text highlighting)
-  --passthru            Print all lines, whether matching or not
-  --output=expr         Output the evaluation of expr for each line
-                        (turns off text highlighting)
-  --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
-  -h, --no-filename     Suppress the prefixing filename on output
-  -c, --count           Show number of lines matching per file
-  --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.
+I<match> filters match the target filename against a regular expression.
+The regular expression is made case insensitive for the search.
 
-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).
+Example:
 
-File finding:
-  -f                    Only print the files found, without searching.
-                        The PATTERN must not be specified.
-  -g REGEX              Same as -f, but only print files matching REGEX.
-  --sort-files          Sort the found files lexically.
-  --invert-file-match   Print/search handle files that do not match -g/-G.
-  --show-types          Show which types each file has.
+    --type-set make:match:/(gnu)?makefile/
 
-File inclusion/exclusion:
-  -a, --all-types       All file types searched;
-                        Ignores CVS, .svn and other ignored directories
-  -u, --unrestricted    All files and directories searched
-  --[no]ignore-dir=name Add/Remove directory from the list of ignored dirs
-  -r, -R, --recurse     Recurse into subdirectories (ack's default behavior)
-  -n, --no-recurse      No descending into subdirectories
-  -G REGEX              Only search files that match REGEX
-
-  --perl                Include only Perl files.
-  --type=perl           Include only Perl files.
-  --noperl              Exclude Perl files.
-  --type=noperl         Exclude Perl files.
-                        See "ack --help type" for supported filetypes.
-
-  --type-set TYPE=.EXTENSION[,.EXT2[,...]]
-                        Files with the given EXTENSION(s) are recognized as
-                        being of type TYPE. This replaces an existing
-                        definition for type TYPE.
-  --type-add TYPE=.EXTENSION[,.EXT2[,...]]
-                        Files with the given EXTENSION(s) are recognized as
-                        being of (the existing) type TYPE
-
-  --[no]follow          Follow symlinks.  Default is off.
-
-  Directories ignored by default:
-    $ignore_dirs
-
-  Files not checked for type:
-    /~\$/            - Unix backup files
-    /#.+#\$/         - Emacs swap files
-    /[._].*\\.swp\$/ - Vi(m) swap files
-    /core\\.\\d+\$/  - core dumps
-    /[.-]min\\.js\$/  - Minified javascript files
+=item firstlinematch:I<PATTERN>
 
-Miscellaneous:
-  --noenv               Ignore environment variables and ~/.ackrc
-  --help                This help
-  --man                 Man page
-  --version             Display version & copyright
-  --thpppt              Bill the Cat
+I<firstlinematch> matches the first line of the target file against a
+regular expression.  Like I<match>, the regular expression is made
+case insensitive.
 
-Exit status is 0 if match, 1 if no match.
+Example:
 
-This is version $VERSION of ack.
-END_OF_HELP
+    --type-add perl:firstlinematch:/perl/
 
-    return;
- }
+=back
 
+More filter types may be made available in the future.
 
+=head1 ENVIRONMENT VARIABLES
 
-sub show_help_types {
-    App::Ack::print( <<'END_OF_HELP' );
-Usage: ack [OPTION]... PATTERN [FILES]
+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.
 
-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.
+=over 4
 
-Note that some extensions may appear in multiple types.  For example,
-.pod files are both Perl and Parrot.
+=item ACKRC
 
-END_OF_HELP
+Specifies the location of the user's F<.ackrc> file.  If this file doesn't
+exist, F<ack> looks in the default location.
 
-    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};
+=item ACK_OPTIONS
 
-        if ( ref $ext_list ) {
-            $ext_list = join( ' ', map { ".$_" } @{$ext_list} );
-        }
-        App::Ack::print( sprintf( "    --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
-    }
+This variable specifies default options to be placed in front of
+any explicit options on the command line.
 
-    return;
-}
+=item ACK_COLOR_FILENAME
 
-sub _listify {
-    my @whats = @_;
+Specifies the color of the filename when it's printed in B<--group>
+mode.  By default, it's "bold green".
 
-    return '' if !@whats;
+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.
 
-    my $end = pop @whats;
-    my $str = @whats ? join( ', ', @whats ) . " and $end" : $end;
+This option can also be set with B<--color-filename>.
 
-    no warnings 'once';
-    require Text::Wrap;
-    $Text::Wrap::columns = 75;
-    return Text::Wrap::wrap( '', '    ', $str );
-}
+=item ACK_COLOR_MATCH
 
+Specifies the color of the matching text when printed in B<--color>
+mode.  By default, it's "black on_yellow".
 
-sub get_version_statement {
-    require Config;
+This option can also be set with B<--color-match>.
 
-    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 );
+See B<ACK_COLOR_FILENAME> for the color specifications.
 
-    return <<"END_OF_VERSION";
-ack $VERSION
-Running under Perl $ver at $this_perl
+=item ACK_COLOR_LINENO
 
-$copyright
+Specifies the color of the line number when printed in B<--color>
+mode.  By default, it's "bold yellow".
 
-This program is free software.  You may modify or distribute it
-under the terms of the Artistic License v2.0.
-END_OF_VERSION
-}
+This option can also be set with B<--color-lineno>.
 
+See B<ACK_COLOR_FILENAME> for the color specifications.
 
-sub print_version_statement {
-    App::Ack::print( get_version_statement() );
+=item ACK_PAGER
 
-    return;
-}
+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.
 
-sub get_copyright {
-    return $COPYRIGHT;
-}
+C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
 
+=item ACK_PAGER_COLOR
 
-sub load_colors {
-    eval 'use Term::ANSIColor ()';
+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.
 
-    $ENV{ACK_COLOR_MATCH}    ||= 'black on_yellow';
-    $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
-    $ENV{ACK_COLOR_LINENO}   ||= 'bold yellow';
+If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
 
-    return;
-}
+=back
 
+=head1 ACK & OTHER TOOLS
 
-sub is_interesting {
-    return if /^\./;
+=head2 Vim integration
 
-    my $include;
+F<ack> integrates easily with the Vim text editor. Set this in your
+F<.vimrc> to use F<ack> instead of F<grep>:
 
-    for my $type ( filetypes( $File::Next::name ) ) {
-        if ( defined $type_wanted{$type} ) {
-            if ( $type_wanted{$type} ) {
-                $include = 1;
-            }
-            else {
-                return;
-            }
-        }
-    }
+    set grepprg=ack\ -k
 
-    return $include;
-}
+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
 
+Miles Sterrett has written a Vim plugin for F<ack> which allows you to use
+C<:Ack> instead of C<:grep>, as well as several other advanced features.
 
-# print subs added in order to make it easy for a third party
-# module (such as App::Wack) to redefine the display methods
-# and show the results in a different way.
-sub print                   { print {$fh} @_ }
-sub print_first_filename    { App::Ack::print( $_[0], "\n" ) }
-sub print_blank_line        { App::Ack::print( "\n" ) }
-sub print_separator         { App::Ack::print( "--\n" ) }
-sub print_filename          { App::Ack::print( $_[0], $_[1] ) }
-sub print_line_no           { App::Ack::print( $_[0], $_[1] ) }
-sub print_column_no         { App::Ack::print( $_[0], $_[1] ) }
-sub print_count {
-    my $filename = shift;
-    my $nmatches = shift;
-    my $ors = shift;
-    my $count = shift;
-    my $show_filename = shift;
+L<https://github.com/mileszs/ack.vim>
 
-    if ($show_filename) {
-        App::Ack::print( $filename );
-        App::Ack::print( ':', $nmatches ) if $count;
-    }
-    else {
-        App::Ack::print( $nmatches ) if $count;
-    }
-    App::Ack::print( $ors );
-}
+=head2 Emacs integration
 
-sub print_count0 {
-    my $filename = shift;
-    my $ors = shift;
-    my $show_filename = shift;
+Phil Jackson put together an F<ack.el> extension that "provides a
+simple compilation mode ... has the ability to guess what files you
+want to search for based on the major-mode."
 
-    if ($show_filename) {
-        App::Ack::print( $filename, ':0', $ors );
-    }
-    else {
-        App::Ack::print( '0', $ors );
-    }
-}
+L<http://www.shellarchive.co.uk/content/emacs.html>
 
+=head2 TextMate integration
 
+Pedro Melo is a TextMate user who writes "I spend my day mostly
+inside TextMate, and the built-in find-in-project sucks with large
+projects.  So I hacked a TextMate command that was using find +
+grep to use ack.  The result is the Search in Project with ack, and
+you can find it here:
+L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"
 
-{
-    my $filename;
-    my $regex;
-    my $display_filename;
+=head2 Shell and Return Code
 
-    my $keep_context;
+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.
 
-    my $last_output_line;             # number of the last line that has been output
-    my $any_output;                   # has there been any output for the current file yet
-    my $context_overall_output_count; # has there been any output at all
+(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
 
-sub search_resource {
-    my $res = shift;
-    my $opt = shift;
+The I<grep> code 2 for errors is not used.
 
-    $filename = $res->name();
+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.
 
-    my $v = $opt->{v};
-    my $passthru = $opt->{passthru};
-    my $max = $opt->{m};
-    my $nmatches = 0;
+=cut
 
-    $display_filename = undef;
+=head1 DEBUGGING ACK PROBLEMS
 
-    # for --line processing
-    my $has_lines = 0;
-    my @lines;
-    if ( defined $opt->{lines} ) {
-        $has_lines = 1;
-        @lines = ( @{$opt->{lines}}, -1 );
-        undef $regex; # Don't match when printing matching line
-    }
-    else {
-        $regex = qr/$opt->{regex}/;
-    }
+If ack gives you output you're not expecting, start with a few simple steps.
 
-    # for context processing
-    $last_output_line = -1;
-    $any_output = 0;
-    my $before_context = $opt->{before_context};
-    my $after_context  = $opt->{after_context};
+=head2 Use B<--noenv>
 
-    $keep_context = ($before_context || $after_context) && !$passthru;
+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>.
 
-    my @before;
-    my $before_starts_at_line;
-    my $after = 0; # number of lines still to print after a match
+=head2 Use B<-f> to see what files have been selected
 
-    while ( $res->next_text ) {
-        # XXX Optimize away the case when there are no more @lines to find.
-        # XXX $has_lines, $passthru and $v never change.  Optimize.
-        if ( $has_lines
-               ? $. != $lines[0]  # $lines[0] should be a scalar
-               : $v ? m/$regex/ : !m/$regex/ ) {
-            if ( $passthru ) {
-                App::Ack::print( $_ );
-                next;
-            }
+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.
 
-            if ( $keep_context ) {
-                if ( $after ) {
-                    print_match_or_context( $opt, 0, $., $-[0], $+[0], $_ );
-                    $after--;
-                }
-                elsif ( $before_context ) {
-                    if ( @before ) {
-                        if ( @before >= $before_context ) {
-                            shift @before;
-                            ++$before_starts_at_line;
-                        }
-                    }
-                    else {
-                        $before_starts_at_line = $.;
-                    }
-                    push @before, $_;
-                }
-                last if $max && ( $nmatches >= $max ) && !$after;
-            }
-            next;
-        } # not a match
+=head2 Use B<--dump>
 
-        ++$nmatches;
+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.
 
-        # print an empty line as a divider before first line in each file (not before the first file)
-        if ( !$any_output && $opt->{show_filename} && $opt->{break} && defined( $context_overall_output_count ) ) {
-            App::Ack::print_blank_line();
-        }
+=head1 TIPS
 
-        shift @lines if $has_lines;
+=head2 Use the F<.ackrc> file.
 
-        if ( $res->is_binary ) {
-            App::Ack::print( "Binary file $filename matches\n" );
-            last;
-        }
-        if ( $keep_context ) {
-            if ( @before ) {
-                print_match_or_context( $opt, 0, $before_starts_at_line, $-[0], $+[0], @before );
-                @before = ();
-                $before_starts_at_line = 0;
-            }
-            if ( $max && $nmatches > $max ) {
-                --$after;
-            }
-            else {
-                $after = $after_context;
-            }
-        }
-        print_match_or_context( $opt, 1, $., $-[0], $+[0], $_ );
+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.
 
-        last if $max && ( $nmatches >= $max ) && !$after;
-    } # while
+=head2 Use F<-f> for working with big codesets
 
-    return $nmatches;
-}   # search_resource()
-
-
-
-sub print_match_or_context {
-    my $opt         = shift; # opts array
-    my $is_match    = shift; # is there a match on the line?
-    my $line_no     = shift;
-    my $match_start = shift;
-    my $match_end   = shift;
-
-    my $color         = $opt->{color};
-    my $heading       = $opt->{heading};
-    my $show_filename = $opt->{show_filename};
-    my $show_column   = $opt->{column};
-
-    if ( $show_filename ) {
-        if ( not defined $display_filename ) {
-            $display_filename =
-                $color
-                    ? Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} )
-                    : $filename;
-            if ( $heading && !$any_output ) {
-                App::Ack::print_first_filename($display_filename);
-            }
-        }
-    }
+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:
 
-    my $sep = $is_match ? ':' : '-';
-    my $output_func = $opt->{output};
-    for ( @_ ) {
-        if ( $keep_context && !$output_func ) {
-            if ( ( $last_output_line != $line_no - 1 ) &&
-                ( $any_output || ( !$heading && defined( $context_overall_output_count ) ) ) ) {
-                App::Ack::print_separator();
-            }
-            # to ensure separators between different files when --noheading
+    # Change all "this" to "that" in all Perl files in a tree.
+    ack -f --perl | xargs perl -p -i -e's/this/that/g'
 
-            $last_output_line = $line_no;
-        }
+or if you prefer:
 
-        if ( $show_filename ) {
-            App::Ack::print_filename($display_filename, $sep) if not $heading;
-            my $display_line_no =
-                $color
-                    ? Term::ANSIColor::colored( $line_no, $ENV{ACK_COLOR_LINENO} )
-                    : $line_no;
-            App::Ack::print_line_no($display_line_no, $sep);
-        }
+    perl -p -i -e's/this/that/g' $(ack -f --perl)
 
-        if ( $output_func ) {
-            while ( /$regex/go ) {
-                App::Ack::print( $output_func->() . "\n" );
-            }
-        }
-        else {
-            if ( $color && $is_match && $regex &&
-                 s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) {
-                # At the end of the line reset the color and remove newline
-                s/[\r\n]*\z/\e[0m\e[K/;
-            }
-            else {
-                # remove any kind of newline at the end of the line
-                s/[\r\n]*\z//;
-            }
-            if ( $show_column ) {
-                App::Ack::print_column_no( $match_start+1, $sep );
-            }
-            App::Ack::print($_ . "\n");
-        }
-        $any_output = 1;
-        ++$context_overall_output_count;
-        ++$line_no;
-    }
+=head2 Use F<-Q> when in doubt about metacharacters
 
-    return;
-} # print_match_or_context()
+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...
 
-} # scope around search_resource() and print_match_or_context()
+=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.
 
-TOTAL_COUNT_SCOPE: {
-my $total_count;
+    ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif
 
-sub get_total_count {
-    return $total_count;
-}
+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.
 
-sub reset_total_count {
-    $total_count = 0;
-}
+=head2 Examples of F<--output>
 
+Following variables are useful in the expansion string:
 
-sub search_and_list {
-    my $res = shift;
-    my $opt = shift;
+=over 4
 
-    my $nmatches = 0;
-    my $count = $opt->{count};
-    my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator
-    my $show_filename = $opt->{show_filename};
+=item C<$&>
 
-    my $regex = qr/$opt->{regex}/;
+The whole string matched by PATTERN.
 
-    if ( $opt->{v} ) {
-        while ( $res->next_text ) {
-            if ( /$regex/ ) {
-                return 0 unless $count;
-            }
-            else {
-                ++$nmatches;
-            }
-        }
-    }
-    else {
-        while ( $res->next_text ) {
-            if ( /$regex/ ) {
-                ++$nmatches;
-                last unless $count;
-            }
-        }
-    }
+=item C<$1>, C<$2>, ...
 
-    if ( $opt->{show_total} ) {
-        $total_count += $nmatches;
-    }
-    else {
-        if ( $nmatches ) {
-            App::Ack::print_count( $res->name, $nmatches, $ors, $count, $show_filename );
-        }
-        elsif ( $count && !$opt->{l} ) {
-            App::Ack::print_count0( $res->name, $ors, $show_filename );
-        }
-    }
+The contents of the 1st, 2nd ... bracketed group in PATTERN.
 
-    return $nmatches ? 1 : 0;
-}   # search_and_list()
+=item C<$`>
 
-} # scope around $total_count
+The string before the match.
 
+=item C<$'>
 
+The string after the match.
 
-sub filetypes_supported_set {
-    return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported();
-}
+=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")
 
-sub print_files {
-    my $iter = shift;
-    my $opt = shift;
+    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
 
-    my $ors = $opt->{print0} ? "\0" : "\n";
+This shows how to pick out particular parts of a match using ( ) within regular expression.
 
-    my $nmatches = 0;
-    while ( defined ( my $file = $iter->() ) ) {
-        App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (),  $ors;
-        $nmatches++;
-        last if $opt->{1};
-    }
+  ack '=head(\d+)\s+(.*)' --output=' $1 : $2'
+  input file contains "=head1 NAME"
+  output  "1 : NAME"
 
-    return $nmatches;
-}
+=head2 Share your knowledge
 
+Join the ack-users mailing list.  Send me your tips and I may add
+them here.
 
-sub print_files_with_matches {
-    my $iter = shift;
-    my $opt = shift;
+=head1 FAQ
 
-    # if we have -l and only 1 file given on command line (this means
-    # show_filename is set to 0), we want to see the filename nevertheless
-    $opt->{show_filename} = 1 if $opt->{l};
+=head2 Why isn't ack finding a match in (some file)?
 
-    $opt->{show_filename} = 0 if $opt->{h};
-    $opt->{show_filename} = 1 if $opt->{H};
+Probably because it's of a type that ack doesn't recognize.  ack's
+searching behavior is driven by filetype.  B<If ack doesn't know
+what kind of file it is, ack ignores the file.>
 
-    # abuse options to hand in the show_total parameter to search_and_list
-    $opt->{show_total} = $opt->{count} && !$opt->{show_filename};
-    reset_total_count();
+Use the C<-f> switch to see a list of files that ack will search
+for you.  You can use the C<--show-types> switch to show which type
+ack thinks each file is.
 
-    my $nmatches = 0;
-    while ( defined ( my $filename = $iter->() ) ) {
-        my $repo = App::Ack::Repository::Basic->new( $filename );
-        my $res;
-        while ( $res = $repo->next_resource() ) {
-            $nmatches += search_and_list( $res, $opt );
-            $res->close();
-            last if $nmatches && $opt->{1};
-        }
-        $repo->close();
-    }
+=head2 Wouldn't it be great if F<ack> did search & replace?
 
-    if ( $nmatches && $opt->{show_total} ) {
-        App::Ack::print_count('', get_total_count(), "\n", 1, 0  )
-    }
+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.
 
-    return $nmatches;
-}
+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)
 
-sub print_matches {
-    my $iter = shift;
-    my $opt = shift;
+=head2 Can I make ack recognize F<.xyz> files?
 
-    $opt->{show_filename} = 0 if $opt->{h};
-    $opt->{show_filename} = 1 if $opt->{H};
+Yes!  Please see L</"Defining your own types">.  If you think
+that F<ack> should recognize a type by default, please see
+L</"ENHANCEMENTS">.
 
-    my $nmatches = 0;
-    while ( defined ( my $filename = $iter->() ) ) {
-        my $repo;
-        my $tarballs_work = 0;
-        if ( $tarballs_work && $filename =~ /\.tar\.gz$/ ) {
-            App::Ack::die( 'Not working here yet' );
-            require App::Ack::Repository::Tar; # XXX Error checking
-            $repo = App::Ack::Repository::Tar->new( $filename );
-        }
-        else {
-            $repo = App::Ack::Repository::Basic->new( $filename );
-        }
-        $repo or next;
+=head2 There's already a program/package called ack.
 
-        while ( my $res = $repo->next_resource() ) {
-            my $needs_line_scan;
-            if ( $opt->{regex} && !$opt->{passthru} ) {
-                $needs_line_scan = $res->needs_line_scan( $opt );
-                if ( $needs_line_scan ) {
-                    $res->reset();
-                }
-            }
-            else {
-                $needs_line_scan = 1;
-            }
-            if ( $needs_line_scan ) {
-                $nmatches += search_resource( $res, $opt );
-            }
-            $res->close();
-        }
-        last if $nmatches && $opt->{1};
-        $repo->close();
-    }
-    return  $nmatches;
-}
+Yes, I know.
 
+=head2 Why is it called ack if it's called ack-grep?
 
-sub filetype_setup {
-    my $filetypes_supported_set = filetypes_supported_set();
-    # If anyone says --no-whatever, we assume all other types must be on.
-    if ( !$filetypes_supported_set ) {
-        for my $i ( keys %type_wanted ) {
-            $type_wanted{$i} = 1 unless ( defined( $type_wanted{$i} ) || $i eq 'binary' || $i eq 'text' || $i eq 'skipped' );
-        }
-    }
-    return;
-}
+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.
 
-EXPAND_FILENAMES_SCOPE: {
-    my $filter;
+To do that, run this with F<sudo> or as root:
 
-    sub expand_filenames {
-        my $argv = shift;
+   ln -s /usr/bin/ack-grep /usr/bin/ack
 
-        my $attr;
-        my @files;
+Alternatively, you could use a shell alias:
 
-        foreach my $pattern ( @{$argv} ) {
-            my @results = bsd_glob( $pattern );
+    # bash/zsh
+    alias ack=ack-grep
 
-            if (@results == 0) {
-                @results = $pattern; # Glob didn't match, pass it thru unchanged
-            }
-            elsif ( (@results > 1) or ($results[0] ne $pattern) ) {
-                if (not defined $filter) {
-                    eval 'require Win32::File;';
-                    if ($@) {
-                        $filter = 0;
-                    }
-                    else {
-                        $filter = Win32::File::HIDDEN()|Win32::File::SYSTEM();
-                    }
-                } # end unless we've tried to load Win32::File
-                if ( $filter ) {
-                    # Filter out hidden and system files:
-                    @results = grep { not(Win32::File::GetAttributes($_, $attr) and $attr & $filter) } @results;
-                    App::Ack::warn( "$pattern: Matched only hidden files" ) unless @results;
-                } # end if we can filter by file attributes
-            } # end elsif this pattern got expanded
+    # csh
+    alias ack ack-grep
 
-            push @files, @results;
-        } # end foreach pattern
+=head2 What does F<ack> mean?
 
-        return \@files;
-    } # end expand_filenames
-} # EXPAND_FILENAMES_SCOPE
+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.
 
-sub get_starting_points {
-    my $argv = shift;
-    my $opt = shift;
+If you want to see lines near your match, use the C<--A>, C<--B>
+and C<--C> switches for displaying context.
 
-    my @what;
+=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?
 
-    if ( @{$argv} ) {
-        @what = @{ $is_windows ? expand_filenames($argv) : $argv };
-        $_ = File::Next::reslash( $_ ) for @what;
+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!)
 
-        # Show filenames unless we've specified one single file
-        $opt->{show_filename} = (@what > 1) || (!-f $what[0]);
-    }
-    else {
-        @what = '.'; # Assume current directory
-        $opt->{show_filename} = 1;
-    }
+=head2 Why does C<"ack '.{40000,}'"> fail?  Isn't that a valid regex?
 
-    for my $start_point (@what) {
-        App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point;
-    }
-    return \@what;
-}
+The Perl language limits the repetition quanitifier to 32K.  You
+can search for C<.{32767}> but not C<.{32768}>.
 
-sub _match {
-    my ( $target, $expression, $invert_flag ) = @_;
+=head1 ACKRC LOCATION SEMANTICS
 
-    if ( $invert_flag ) {
-        return $target !~ $expression;
-    }
-    else {
-        return $target =~ $expression;
-    }
-}
+Ack can load its configuration from many sources.  This list
+specifies the sources Ack looks for configuration; 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
 
-sub get_iterator {
-    my $what = shift;
-    my $opt  = shift;
+=item *
 
-    # Starting points are always searched, no matter what
-    my %starting_point = map { ($_ => 1) } @{$what};
+Defaults are loaded from App::Ack::ConfigDefaults.  This can be omitted
+using C<--ignore-ack-defaults>.
 
-    my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef;
-    my $file_filter;
+=item * Global ackrc
 
-    if ( $g_regex ) {
-        $file_filter
-            = $opt->{u}   ? sub { _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) }    # XXX Maybe this should be a 1, no?
-            : $opt->{all} ? sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_searchable( $_ ) ) }
-            :               sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_interesting( @ _) ) }
-            ;
-    }
-    else {
-        $file_filter
-            = $opt->{u}   ? sub {1}
-            : $opt->{all} ? sub { $starting_point{ $File::Next::name } || is_searchable( $_ ) }
-            :               sub { $starting_point{ $File::Next::name } || is_interesting( @_ ) }
-            ;
-    }
+Options are then loaded from the global ackrc.  This is located at
+C</etc/ackrc> on Unix-like systems, and
+C<C:\Documents and Settings\All Users\Application Data\ackrc> on Windows.
+This can be omitted using C<--noenv>.
 
-    my $descend_filter
-        = $opt->{n} ? sub {0}
-        : $opt->{u} ? sub {1}
-        : \&ignoredir_filter;
+=item * User ackrc
 
-    my $iter =
-        File::Next::files( {
-            file_filter     => $file_filter,
-            descend_filter  => $descend_filter,
-            error_handler   => sub { my $msg = shift; App::Ack::warn( $msg ) },
-            sort_files      => $opt->{sort_files},
-            follow_symlinks => $opt->{follow},
-        }, @{$what} );
-    return $iter;
-}
+Options are then loaded from the user's ackrc.  This is located at
+C<$HOME/.ackrc> on Unix-like systems, and
+C<C:\Documents and Settings\$USER\Application Data\ackrc>.  If a different
+ackrc is desired, it may be overridden with the C<$ACKRC> environment
+variable.
+This can be omitted using C<--noenv>.
 
+=item * Project ackrc
 
-sub set_up_pager {
-    my $command = shift;
+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>.
 
-    return if App::Ack::output_to_pipe();
+=item * --ackrc
 
-    my $pager;
-    if ( not open( $pager, '|-', $command ) ) {
-        App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
-    }
-    $fh = $pager;
+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.
 
-    return;
-}
+=item * ACK_OPTIONS
 
+Options are then loaded from the environment variable C<ACK_OPTIONS>.  This can
+be omitted using C<--noenv>.
 
-sub input_from_pipe {
-    return $input_from_pipe;
-}
+=item * Command line
 
+Options are then loaded from the command line.
 
+=back
 
-sub output_to_pipe {
-    return $output_to_pipe;
-}
+=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.
 
-sub exit_from_ack {
-    my $nmatches = shift;
+=head2 GENERAL CHANGES
 
-    my $rc = $nmatches ? 0 : 1;
-    exit $rc;
-}
+=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.
 
-1; # End of App::Ack
-package App::Ack::Repository;
+=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">.
 
-use warnings;
-use strict;
+=item *
 
-sub FAIL {
-    require Carp;
-    Carp::confess( 'Must be overloaded' );
-}
+ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for
+details.
 
+=item *
 
-sub new {
-    FAIL();
-}
+ack's default filter definitions aren't special; you may tell ack to
+completely disregard them if you don't like them.
 
+=back
 
-sub next_resource {
-    FAIL();
-}
+=head2 REMOVED OPTIONS
 
+=over 4
 
-sub close {
-    FAIL();
-}
+=item *
 
-1;
-package App::Ack::Resource;
+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 *
 
-use warnings;
-use strict;
+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.
 
-sub FAIL {
-    require Carp;
-    Carp::confess( 'Must be overloaded' );
-}
+=item *
 
+The B<--binary> option has been removed.
 
-sub new {
-    FAIL();
-}
+=item *
 
+The B<--skipped> option has been removed.
 
-sub name {
-    FAIL();
-}
+=item *
 
+The B<--text> option has been removed.
 
-sub is_binary {
-    FAIL();
-}
+=item *
 
+The B<--invert-file-match> option has been removed.  Instead, you may
+use B<-v> with B<-g>.
 
+=back
 
-sub needs_line_scan {
-    FAIL();
-}
+=head2 CHANGED OPTIONS
 
+=over 4
 
-sub reset {
-    FAIL();
-}
+=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>.
 
-sub next_text {
-    FAIL();
-}
+=back
 
+=head2 ADDED OPTIONS
 
-sub close {
-    FAIL();
-}
+=over 4
 
-1;
-package App::Ack::Plugin::Basic;
+=item *
 
+B<--files-from> was added so that a user may submit a list of filenames as
+a list of files to search.
 
+=item *
 
-package App::Ack::Resource::Basic;
+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 *
 
-use warnings;
-use strict;
+B<-s> was added to tell ack to suppress error messages about non-existent or
+unreadable files.
 
+=item *
 
-our @ISA = qw( App::Ack::Resource );
+B<--ignore-directory> and B<--noignore-directory> were added as aliases for
+B<--ignore-dir> and B<--noignore-dir> respectively.
 
+=item *
 
-sub new {
-    my $class    = shift;
-    my $filename = shift;
+B<--ignore-file> was added so that users may specify patterns of files to
+ignore (ex. /.*~$/).
 
-    my $self = bless {
-        filename        => $filename,
-        fh              => undef,
-        could_be_binary => undef,
-        opened          => undef,
-        id              => undef,
-    }, $class;
+=item *
 
-    if ( $self->{filename} eq '-' ) {
-        $self->{fh} = *STDIN;
-        $self->{could_be_binary} = 0;
-    }
-    else {
-        if ( !open( $self->{fh}, '<', $self->{filename} ) ) {
-            App::Ack::warn( "$self->{filename}: $!" );
-            return;
-        }
-        $self->{could_be_binary} = 1;
-    }
+B<--dump> was added to allow users to easily find out which options are
+set where.
 
-    return $self;
-}
+=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.
 
-sub name {
-    my $self = shift;
+=item *
 
-    return $self->{filename};
-}
+B<--type-del> was added to selectively remove file type definitions.
 
+=item *
 
-sub is_binary {
-    my $self = shift;
+B<--ignore-ack-defaults> was added so that users may ignore ack's default
+options in favor of their own.
 
-    if ( $self->{could_be_binary} ) {
-        return -B $self->{filename};
-    }
+=item *
 
-    return 0;
-}
+B<--bar> was added so ack users may consult Admiral Ackbar.
 
+=back
 
+=head1 AUTHOR
 
-sub needs_line_scan {
-    my $self  = shift;
-    my $opt   = shift;
+Andy Lester, C<< <andy at petdance.com> >>
 
-    return 1 if $opt->{v};
+=head1 BUGS
 
-    my $size = -s $self->{fh};
-    if ( $size == 0 ) {
-        return 0;
-    }
-    elsif ( $size > 100_000 ) {
-        return 1;
-    }
+Please report any bugs or feature requests to the issues list at
+Github: L<https://github.com/petdance/ack2/issues>
 
-    my $buffer;
-    my $rc = sysread( $self->{fh}, $buffer, $size );
-    if ( not defined $rc ) {
-        App::Ack::warn( "$self->{filename}: $!" );
-        return 1;
-    }
-    return 0 unless $rc && ( $rc == $size );
+=head1 ENHANCEMENTS
 
-    my $regex = $opt->{regex};
-    return $buffer =~ /$regex/m;
-}
+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/petdance/ack2/issues>
 
-sub reset {
-    my $self = shift;
+Patches are always welcome, but patches with tests get the most
+attention.
 
-    seek( $self->{fh}, 0, 0 )
-        or App::Ack::warn( "$self->{filename}: $!" );
+=head1 SUPPORT
 
-    return;
-}
+Support for and information about F<ack> can be found at:
 
+=over 4
 
-sub next_text {
-    if ( defined ($_ = readline $_[0]->{fh}) ) {
-        $. = ++$_[0]->{line};
-        return 1;
-    }
+=item * The ack homepage
 
-    return;
-}
+L<http://beyondgrep.com/>
 
+=item * The ack-users mailing list
 
-sub close {
-    my $self = shift;
+L<http://groups.google.com/group/ack-users>
 
-    if ( not close $self->{fh} ) {
-        App::Ack::warn( $self->name() . ": $!" );
-    }
+=item * The ack issues list at Github
 
-    return;
-}
+L<https://github.com/petdance/ack2/issues>
 
-package App::Ack::Repository::Basic;
+=item * AnnoCPAN: Annotated CPAN documentation
 
+L<http://annocpan.org/dist/ack>
 
-our @ISA = qw( App::Ack::Repository );
+=item * CPAN Ratings
 
+L<http://cpanratings.perl.org/d/ack>
 
-use warnings;
-use strict;
+=item * Search CPAN
 
-sub new {
-    my $class    = shift;
-    my $filename = shift;
+L<http://search.cpan.org/dist/ack>
 
-    my $self = bless {
-        filename => $filename,
-        nexted   => 0,
-    }, $class;
+=item * Git source repository
 
-    return $self;
-}
+L<https://github.com/petdance/ack2>
 
+=back
 
-sub next_resource {
-    my $self = shift;
+=head1 ACKNOWLEDGEMENTS
 
-    return if $self->{nexted};
-    $self->{nexted} = 1;
+How appropriate to have I<ack>nowledgements!
 
-    return App::Ack::Resource::Basic->new( $self->{filename} );
-}
+Thanks to everyone who has contributed to ack in any way, including
+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
 
-sub close {
-}
+Copyright 2005-2013 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.
 
-1;
+=cut