170325.2 · d0cd00029c71977f587e362856582ddd42f48e77
Bringing over 2.18 from the ack2 repo
reading declarations
Changes to garage/ack-1.96. All files
@@ -0,0 +1,2844 @@+#!/usr/bin/env perl+#+# This file, ack, is generated code.+# Please DO NOT EDIT or send patches for it.+#+# Please take a look at the source from+# https://github.com/petdance/ack+# and submit patches against the individual files+# that build ack.+#++use warnings;+use strict;++our $VERSION = '1.96';+# Check http://betterthangrep.com/ for updates++# These are all our globals.+++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 '--' );++ # Priorities! Get the --thpppt and --bar checking out of the way.+ /^--th[pt]+t+$/ && App::Ack::_thpppt($_);+ /^--bar$/ && App::Ack::_bar();++ # 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();++ if ( exists $ENV{ACK_SWITCHES} ) {+ App::Ack::warn( 'ACK_SWITCHES is no longer supported. Use ACK_OPTIONS.' );+ }++ if ( !@ARGV ) {+ App::Ack::show_help();+ exit 1;+ }++ main();+}++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} );++ my $what = App::Ack::get_starting_points( \@ARGV, $opt );+ my $iter = App::Ack::get_iterator( $what, $opt );+ App::Ack::filetype_setup();++ my $nmatches = 0;++ App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};+ if ( $opt->{f} ) {+ $nmatches = App::Ack::print_files( $iter, $opt );+ }+ elsif ( $opt->{l} || $opt->{count} ) {+ $nmatches = App::Ack::print_files_with_matches( $iter, $opt );+ }+ else {+ $nmatches = App::Ack::print_matches( $iter, $opt );+ }+ 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.++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++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.++Some files will never be selected by I<ack>, even with B<-a>,+including:++=over 4++=item * Backup files: Files matching F<#*#> or ending with F<~>.++=item * Coredumps: Files matching F<core.\d+>++=back++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.++=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.++For a complete list of directories that do not get searched, run+F<ack --help>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or+C<--silent> or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-a>, B<--all>++Operate on all files, regardless of type (but still skip directories+like F<blib>, F<CVS>, etc.)++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total count.++=item B<--color>, B<--nocolor>++B<--color> highlights the matching text. B<--nocolor> supresses+the color. This is on by default unless the output is redirected.++On Windows, this option is off by default unless the+L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>+environment variable is used.++=item B<--color-filename=I<color>>++Sets the color to be used for filenames.++=item B<--color-match=I<color>>++Sets the color to be used for matches.++=item B<--color-lineno=I<color>>++Sets the color to be used for line numbers.++=item B<--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<--env>, B<--noenv>++B<--noenv> disables all environment processing. No F<.ackrc> is read+and all environment variables are ignored. By default, F<ack> considers+F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe+or file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken as+a path to search.++=item B<--follow>, B<--nofollow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-G I<REGEX>>++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>.++=item B<-g I<REGEX>>++Print files where the relative path + filename matches I<REGEX>. This option is+a convenience shortcut for B<-f> B<-G I<REGEX>>.++The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.++=item B<--group>, B<--nogroup>++B<--group> groups matches by file name with. This is the default when+used interactively.++B<--nogroup> prints one result per line, like grep. This is the default+when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--help>++Print a short help statement.++=item B<-i>, B<--ignore-case>++Ignore case in the search strings.++This applies only to the PATTERN, not to the regexes given for the B<-g>+and B<-G> options.++=item B<--[no]ignore-dir=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used multiple times+to ignore multiple directories. For example, mason users may wish to include+B<--ignore-dir=data>. The B<--noignore-dir> option allows users to search+directories which would normally be ignored (perhaps to research the contents+of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested directories like+F<foo/bar> are NOT supported. You would need to specify B<--ignore-dir=foo> and+then no files from any foo directory are taken into account by ack unless given+explicitly on the command line.++=item B<--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.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match. This is equivalent+to specifying B<-l> and B<-v>.++=item B<--match I<REGEX>>++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.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)++=item B<--pager=I<program>>++Direct 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.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++This applies only to the PATTERN, not to the regexes given for the B<-g>+and B<-G> options.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<--smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default.++B<-i> always overrides this option.++This applies only to the PATTERN, not to the regexes given for the+B<-g> and B<-G> options.++=item B<--sort-files>++Sorts the found files lexically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=item B<--type=TYPE>, B<--type=noTYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>=I<.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">.+++=item B<--type-set I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>++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>++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>++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.++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++This applies only to the PATTERN, not to the regexes given for the B<-g>+and B<-G> options.++=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++=head1 THE .ackrc FILE++The F<.ackrc> file contains command-line options that are prepended+to the command line before processing. Multiple options may live+on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>+might look like this:++ # Always sort the files+ --sort-files++ # Always color, even if piping to a another program+ --color++ # Use "less -r" as my pager+ --pager=less -r++Note that arguments with spaces in them do not need to be quoted,+as they are not interpreted by the shell. Basically, each I<line>+in the F<.ackrc> file is interpreted as one element of C<@ARGV>.++F<ack> looks in your home directory for the F<.ackrc>. You can+specify another location with the F<ACKRC> variable, below.++If B<--noenv> is specified on the command line, the F<.ackrc> file+is ignored.++=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=.xs --perl foo>+does this for you. B<--type-add> appends+additional extensions to an existing type.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set+eiffel=.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>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel=.e,.eiffel++or writing on separate lines++ --type-set+ eiffel=.e,.eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel=.e,.eiffel+++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>++Restrictions:++=over 4++=item++The types 'skipped', 'make', 'binary' and 'text' are considered "builtin" and+cannot be altered.++=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).++=back++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life much easier.+These variables are ignored if B<--noenv> is specified on the command line.++=over 4++=item ACKRC++Specifies the location of the F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 ACK & OTHER TOOLS++=head2 Vim integration++F<ack> integrates easily with the Vim text editor. Set this in your+F<.vimrc> to use F<ack> instead of F<grep>:++ set grepprg=ack\ -a++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:++ :grep Dumper perllib++=head2 Emacs integration++Phil Jackson put together an F<ack.el> extension that "provides a+simple compilation mode ... has the ability to guess what files you+want to search for based on the major-mode."++L<http://www.shellarchive.co.uk/content/emacs.html>++=head2 TextMate integration++Pedro Melo is a TextMate user who writes "I spend my day mostly+inside TextMate, and the built-in find-in-project sucks with large+projects. So I hacked a TextMate command that was using find ++grep to use ack. The result is the Search in Project with ack, and+you can find it here:+L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"++=head2 Shell and Return Code++For greater compatibility with I<grep>, I<ack> in normal use returns+shell return or exit code of 0 only if something is found and 1 if+no match is found.++(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)++The I<grep> code 2 for errors is not used.++If C<-f> or C<-g> are specified, then 0 is returned if at least one+file is found. If no files are found, then 1 is returned.++=cut++=head1 DEBUGGING ACK PROBLEMS++If ack gives you output you're not expecting, start with a few simple steps.++=head2 Use B<--noenv>++Your environment variables and F<.ackrc> may be doing things you're+not expecting, or forgotten you specified. Use B<--noenv> to ignore+your environment and F<.ackrc>.++=head2 Use B<-f> to see what files you're scanning++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.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Share your knowledge++Join the ack-users mailing list. Send me your tips and I may add+them here.++=head1 FAQ++=head2 Why isn't ack finding a match in (some file)?++Probably because it's of a type that ack doesn't recognize. ack's+searching behavior is driven by filetype. B<If ack doesn't know+what kind of file it is, ack ignores the file.>++Use the C<-f> switch to see a list of files that ack will search+for you.++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.++=head2 Why does ack ignore unknown files by default?++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.++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.++=head2 Wouldn't it be great if F<ack> did search & replace?++No, ack will always be read-only. Perl has a perfectly good way+to do search & replace in files, using the C<-i>, C<-p> and C<-n>+switches.++You can certainly use ack to select your files to update. For+example, to change all "foo" to "bar" in all PHP files, you can do+this from the Unix shell:++ $ perl -i -p -e's/foo/bar/g' $(ack -f --php)++=head2 Can you make ack recognize F<.xyz> files?++That's an enhancement. Please see the section in the manual about+enhancements.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://betterthangrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+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+and Pete Krawczyk.++=head1 COPYRIGHT & LICENSE++Copyright 2005-2012 Andy Lester.++This program is free software; you can redistribute it and/or modify+it under the terms of the Artistic License v2.0.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.06';++++use File::Spec ();+++our $name; # name of the current file+our $dir; # dir of the current file++our %files_defaults;+our %skip_dirs;++BEGIN {+ %files_defaults = (+ file_filter => undef,+ descend_filter => undef,+ error_handler => sub { CORE::die @_ },+ sort_files => undef,+ follow_symlinks => 1,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ ($_[0] eq __PACKAGE__) && die 'File::Next::files must not be invoked as File::Next->files';++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $filter = $parms->{file_filter};++ 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++ return;+ }; # iterator+}++++++++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dir = shift;++ my $dh;+ if ( !opendir $dh, $dir ) {+ $parms->{error_handler}->( "$dir: $!" );+ return;+ }++ my @newfiles;+ my $descend_filter = $parms->{descend_filter};+ my $follow_symlinks = $parms->{follow_symlinks};+ my $sort_sub = $parms->{sort_files};++ for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {+ my $has_stat;++ # 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;+ }++ 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;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;+++++our $VERSION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '1.96';+ $COPYRIGHT = 'Copyright 2005-2011 Andy Lester.';+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $input_from_pipe;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec ();+use File::Glob ':glob';+use Getopt::Long ();++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',+ );++ %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 psgi )],+ 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 )],+ );++ 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 };++ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $input_from_pipe = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}+++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" );++ # get rid of leading and trailing whitespaces+ for ( @lines ) {+ s/^\s+//;+ s/\s+$//;+ }++ return @lines;+ }+ }++ return;+}+++sub get_command_line_options {+ my %opt = (+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},+ );++ 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,+ });+ },++ '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++ if ( exists $type_wanted{ $type } ) {+ $type_wanted{ $type } = $wanted;+ }+ else {+ App::Ack::die( qq{Unknown --type "$type"} );+ }+ }, # type sub+ };++ # 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};++ # first pass through options, looking for type definitions+ def_types_from_ARGV();++ for my $i ( filetypes_supported() ) {+ $getopt_specs->{ "$i!" } = \$type_wanted{ $i };+ }+++ 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.' );++ 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;+ }++ while ( my ($key,$value) = each %defaults ) {+ if ( not defined $opt{$key} ) {+ $opt{$key} = $value;+ }+ }++ if ( defined $opt{m} && $opt{m} <= 0 ) {+ App::Ack::die( '-m must be greater than zero' );+ }++ for ( qw( before_context after_context ) ) {+ if ( defined $opt{$_} && $opt{$_} < 0 ) {+ App::Ack::die( "--$_ may not be negative" );+ }+ }++ 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} ) );++ # --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;++ 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.' );+ }+ }++ return \%opt;+}+++sub def_types_from_ARGV {+ my @typedef;++ 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.' );++ for my $td (@typedef) {+ my ($type, $ext) = split /=/, $td->[1];++ 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';++ delete_type($type);+ }+ }+ else {+ # type-add++ # 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';++ App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} )+ unless exists $mappings{$type};+ }++ my @exts = split /,/, $ext;+ s/^\.// for @exts;++ 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".} );+ }+ }++ return;+}+++sub delete_type {+ my $type = shift;++ App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} )+ unless ref $mappings{$type} eq 'ARRAY';++ delete $mappings{$type};+ delete $type_wanted{$type};+ for my $ext ( keys %types ) {+ $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ];+ }+}+++sub ignoredir_filter {+ return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};+}+++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++use constant TEXT => 'text';++sub filetypes {+ my $filename = shift;++ my $basename = $filename;+ $basename =~ s{.*[$dir_sep_chars]}{};++ return 'skipped' unless is_searchable( $basename );++ 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';++ # 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;+ }++ # At this point, we can't tell from just the name. Now we have to+ # open it and look inside.++ 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;+ }+ }++ return 'binary' if -B $filename;++ # 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;++ 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;+ }++ return (TEXT);+}+++sub is_searchable {+ my $filename = shift;++ # 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$/;++ return 1;+}+++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";+ }++ return $str;+}+++sub check_regex {+ my $regex = shift;++ return unless defined $regex;++ eval { qr/$regex/ };+ if ($@) {+ (my $error = $@) =~ s/ at \S+ line \d+.*//;+ chomp($error);+ App::Ack::die( "Invalid regex '$regex':\n $error" );+ }++ return;+}+++++sub warn {+ return CORE::warn( _my_program(), ': ', @_, "\n" );+}+++sub die {+ return CORE::die( _my_program(), ': ', @_, "\n" );+}++sub _my_program {+ require File::Basename;+ return File::Basename::basename( $0 );+}++++sub filetypes_supported {+ return keys %mappings;+}++sub _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;+}++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++ $x =~ s/(.)(.)/$1x(ord($2)-32)/eg;+ App::Ack::print( $x );+ exit 0;+}++sub _key {+ my $str = lc shift;+ $str =~ s/[^a-z]//g;++ return $str;+}+++sub show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs );++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILE]++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 "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --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.++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).++File finding:+ -f Only print the files 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.++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++Miscellaneous:+ --noenv Ignore environment variables and ~/.ackrc+ --help This help+ --man Man page+ --version Display version & copyright+ --thpppt Bill the Cat+ --bar The warning admiral++Exit status is 0 if match, 1 if no match.++This is version $VERSION of ack.+END_OF_HELP++ return;+ }++++sub show_help_types {+ App::Ack::print( <<'END_OF_HELP' );+Usage: ack [OPTION]... PATTERN [FILES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( ' ', map { ".$_" } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub _listify {+ my @whats = @_;++ return '' if !@whats;++ my $end = pop @whats;+ my $str = @whats ? join( ', ', @whats ) . " and $end" : $end;++ no warnings 'once';+ require Text::Wrap;+ $Text::Wrap::columns = 75;+ return Text::Wrap::wrap( '', ' ', $str );+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ return <<"END_OF_VERSION";+ack $VERSION+Running under Perl $ver at $this_perl++$copyright++This program is free software. You may modify or distribute it+under the terms of the Artistic License v2.0.+END_OF_VERSION+}+++sub print_version_statement {+ App::Ack::print( get_version_statement() );++ return;+}+++sub get_copyright {+ return $COPYRIGHT;+}+++sub load_colors {+ eval 'use Term::ANSIColor ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}+++sub is_interesting {+ return if /^\./;++ my $include;++ for my $type ( filetypes( $File::Next::name ) ) {+ if ( defined $type_wanted{$type} ) {+ if ( $type_wanted{$type} ) {+ $include = 1;+ }+ else {+ return;+ }+ }+ }++ return $include;+}++++# 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;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }+}++++{+ my $filename;+ my $regex;+ my $display_filename;++ my $keep_context;++ 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++sub search_resource {+ my $res = shift;+ my $opt = shift;++ $filename = $res->name();++ my $v = $opt->{v};+ my $passthru = $opt->{passthru};+ my $max = $opt->{m};+ my $nmatches = 0;++ $display_filename = undef;++ # 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}/;+ }++ # for context processing+ $last_output_line = -1;+ $any_output = 0;+ my $before_context = $opt->{before_context};+ my $after_context = $opt->{after_context};++ $keep_context = ($before_context || $after_context) && !$passthru;++ my @before;+ my $before_starts_at_line;+ my $after = 0; # number of lines still to print after a match++ 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;+ }++ 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++ ++$nmatches;++ # 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();+ }++ shift @lines if $has_lines;++ 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], $_ );++ last if $max && ( $nmatches >= $max ) && !$after;+ } # while++ 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);+ }+ }+ }++ 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++ $last_output_line = $line_no;+ }++ 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);+ }++ 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;+ }++ return;+} # print_match_or_context()++} # scope around search_resource() and print_match_or_context()+++TOTAL_COUNT_SCOPE: {+my $total_count;++sub get_total_count {+ return $total_count;+}++sub reset_total_count {+ $total_count = 0;+}+++sub search_and_list {+ my $res = shift;+ my $opt = shift;++ my $nmatches = 0;+ my $count = $opt->{count};+ my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator+ my $show_filename = $opt->{show_filename};++ my $regex = qr/$opt->{regex}/;++ 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;+ }+ }+ }++ 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 );+ }+ }++ return $nmatches ? 1 : 0;+} # search_and_list()++} # scope around $total_count++++sub filetypes_supported_set {+ return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported();+}++++sub print_files {+ my $iter = shift;+ my $opt = shift;++ my $ors = $opt->{print0} ? "\0" : "\n";++ my $nmatches = 0;+ while ( defined ( my $file = $iter->() ) ) {+ App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (), $ors;+ $nmatches++;+ last if $opt->{1};+ }++ return $nmatches;+}+++sub print_files_with_matches {+ my $iter = shift;+ my $opt = shift;++ # 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};++ $opt->{show_filename} = 0 if $opt->{h};+ $opt->{show_filename} = 1 if $opt->{H};++ # abuse options to hand in the show_total parameter to search_and_list+ $opt->{show_total} = $opt->{count} && !$opt->{show_filename};+ reset_total_count();++ 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();+ }++ if ( $nmatches && $opt->{show_total} ) {+ App::Ack::print_count('', get_total_count(), "\n", 1, 0 )+ }++ return $nmatches;+}+++sub print_matches {+ my $iter = shift;+ my $opt = shift;++ $opt->{show_filename} = 0 if $opt->{h};+ $opt->{show_filename} = 1 if $opt->{H};++ 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;++ 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;+}+++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;+}+++EXPAND_FILENAMES_SCOPE: {+ my $filter;++ sub expand_filenames {+ my $argv = shift;++ my $attr;+ my @files;++ foreach my $pattern ( @{$argv} ) {+ my @results = bsd_glob( $pattern );++ 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++ push @files, @results;+ } # end foreach pattern++ return \@files;+ } # end expand_filenames+} # EXPAND_FILENAMES_SCOPE++++sub get_starting_points {+ my $argv = shift;+ my $opt = shift;++ my @what;++ if ( @{$argv} ) {+ @what = @{ $is_windows ? expand_filenames($argv) : $argv };+ $_ = File::Next::reslash( $_ ) for @what;++ # 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;+ }++ for my $start_point (@what) {+ App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point;+ }+ return \@what;+}++sub _match {+ my ( $target, $expression, $invert_flag ) = @_;++ if ( $invert_flag ) {+ return $target !~ $expression;+ }+ else {+ return $target =~ $expression;+ }+}+++sub get_iterator {+ my $what = shift;+ my $opt = shift;++ # Starting points are always searched, no matter what+ my %starting_point = map { ($_ => 1) } @{$what};++ my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef;+ my $file_filter;++ 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( @_ ) }+ ;+ }++ my $descend_filter+ = $opt->{n} ? sub {0}+ : $opt->{u} ? sub {1}+ : \&ignoredir_filter;++ 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;+}+++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub input_from_pipe {+ return $input_from_pipe;+}++++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++++1; # End of App::Ack+package App::Ack::Repository;+++use warnings;+use strict;++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ FAIL();+}+++sub next_resource {+ FAIL();+}+++sub close {+ FAIL();+}++1;+package App::Ack::Resource;+++use warnings;+use strict;++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ FAIL();+}+++sub name {+ FAIL();+}+++sub is_binary {+ FAIL();+}++++sub needs_line_scan {+ FAIL();+}+++sub reset {+ FAIL();+}+++sub next_text {+ FAIL();+}+++sub close {+ FAIL();+}++1;+package App::Ack::Plugin::Basic;++++package App::Ack::Resource::Basic;+++use warnings;+use strict;+++our @ISA = qw( App::Ack::Resource );+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ could_be_binary => undef,+ opened => undef,+ id => undef,+ }, $class;++ 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;+ }++ return $self;+}+++sub name {+ my $self = shift;++ return $self->{filename};+}+++sub is_binary {+ my $self = shift;++ if ( $self->{could_be_binary} ) {+ return -B $self->{filename};+ }++ return 0;+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( not defined $rc ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ seek( $self->{fh}, 0, 0 )+ or App::Ack::warn( "$self->{filename}: $!" );++ return;+}+++sub next_text {+ if ( defined ($_ = readline $_[0]->{fh}) ) {+ $. = ++$_[0]->{line};+ return 1;+ }++ return;+}+++sub close {+ my $self = shift;++ if ( not close $self->{fh} ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ return;+}++package App::Ack::Repository::Basic;+++our @ISA = qw( App::Ack::Repository );+++use warnings;+use strict;++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ nexted => 0,+ }, $class;++ return $self;+}+++sub next_resource {+ my $self = shift;++ return if $self->{nexted};+ $self->{nexted} = 1;++ return App::Ack::Resource::Basic->new( $self->{filename} );+}+++sub close {+}++++1;