170325.2 · d0cd00029c71977f587e362856582ddd42f48e77
Bringing over 2.18 from the ack2 repo
reading declarations
Ack.pm
@@ -0,0 +1,588 @@+package App::Ack;++use warnings;+use strict;++=head1 NAME++App::Ack++=head1 DESCRIPTION++A container for functions for the ack program.++=head1 VERSION++Version 2.18++=cut++our $VERSION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.18';+ $COPYRIGHT = 'Copyright 2005-2017 Andy Lester.';+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin' || $^O eq 'msys');+ $is_windows = ($^O eq 'MSWin32');+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}++=head1 SYNOPSIS++If you want to know about the F<ack> program, see the F<ack> file itself.++No user-serviceable parts inside. F<ack> is all that should use this.++=head1 FUNCTIONS++=cut++=head2 remove_dir_sep( $path )++This functions removes a trailing path separator, if there is one, from its argument++=cut++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++=head2 warn( @_ )++Put out an ack-specific warning.++=cut++sub warn {+ return CORE::warn( _my_program(), ': ', @_, "\n" );+}++=head2 die( @_ )++Die in an ack-specific way.++=cut++sub die {+ return CORE::die( _my_program(), ': ', @_, "\n" );+}++sub _my_program {+ require File::Basename;+ return File::Basename::basename( $0 );+}+++=head2 filetypes_supported()++Returns a list of all the types that we can detect.++=cut++sub filetypes_supported {+ return keys %mappings;+}++sub thpppt {+ my $y = q{_ /|,\\'!.x',=(www)=, U };+ $y =~ tr/,x!w/\nOo_/;++ App::Ack::print( "$y ack $_[0]!\n" );+ exit 0;+}++sub ackbar {+ my $x;+ $x = <<'_BAR';+ 6?!I'7!I"?%+!+ 3~!I#7#I"7#I!?!+!="+"="+!:!+ 2?#I!7!I!?#I!7!I"+"=%+"=#+ 1?"+!?*+!=#~"=!+#?"="+!+ 0?"+!?"I"?&+!="~!=!~"=!+%="+"+ /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!+ .?%I"?%+%='?!=#~$="+ ,,!?%I"?(+$=$~!=#:"~$:!~!+ ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!+ +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"+ *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!+ *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"+ )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!+ (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!+ (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!+ %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"+ $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/+ $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!+ $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/+ %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!+ %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!+ %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!+ %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!+ &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!+ &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!+ &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!+ &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!+ &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"+ &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!+ %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&+ %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)+ $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!+ $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"+ "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!+ !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!+ !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*+7+:!,!~#,"=!7'I!?#I"7/+!7++77I!+!7!?!7!I"71+!7,+_BAR++ return _pic_decode($x);+}++sub cathy {+ my $x = <<'CATHY';+ 0+!--+!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! $A"C!K!!! $|!+ 0+!--+!+ 6\! 1:!,!.! !+ 7\! /.!M!~!Z!M!~!+ 8\! /~!D! "M! !+ 4.! $\! /M!~!.!8! +.!M# 4+ 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3+ /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4+ ..! !D!Z!.!Z!.! '\! 9=!M".! 6+ /.! !.!~!M".! '\! 8~! 9+ 4M!.! /.!7!N!M!.! F+ 4.! &:!M! !N"M# !M"N!M! #D!M&=! =+ :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;+ 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9+ (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0+ &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! ++ &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *+ 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! ++ 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4+ 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3+ /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1+ #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0+ $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '+ #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (+ #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %+ *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &+ )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '+ )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! ++ (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! ++ 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *+ %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )+ !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (+ !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &+ !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $+ !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $+ (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #+ '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %+ $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! ++ ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )+ +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (+ 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )+ 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /+ 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2+ 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %+ 0?!$! &N! )." .,! %."M! ":!M!.! 0+ 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0+CATHY+ return _pic_decode($x);+}++sub _pic_decode {+ my($compressed) = @_;+ $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;+ App::Ack::print( $compressed );+ exit 0;+}++=head2 show_help()++Dumps the help page to the user.++=cut++sub show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after+ matching lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before+ matching lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For+ example, --pager="less -R". Ignored if output+ is redirected.+ --nopager Do not send output through a pager. Cancels+ any setting in ~/.ackrc, ACK_PAGER or+ ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's+ results. (default: on when used interactively)+ --[no]break Print a break between results from different+ files. (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line+ numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without+ searching. The PATTERN must not be specified.+ -g Same as -f, but only select files matching+ PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/remove directory from list of ignored dirs+ --[no]ignore-directory=name Synonym for ignore-dir+ --ignore-file=filter Add filter for ignoring files+ -r, -R, --recurse Recurse into subdirectories (default: on)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files of types that ack recognizes.++ --type=X Include only X files, where X is a recognized+ filetype.+ --type=noX Exclude X files.+ See "ack --help-types" for supported filetypes.++File type specification:+ --type-set TYPE:FILTER:FILTERARGS+ Files with the given FILTERARGS applied to the+ given FILTER are recognized as being of type+ TYPE. This replaces an existing definition for+ type TYPE.+ --type-add TYPE:FILTER:FILTERARGS+ Files with the given FILTERARGS applied to the+ given FILTER are recognized as being type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc+ files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore default definitions included with ack.+ --create-ackrc Outputs a default ackrc for your customization+ to standard output.+ --help, -? This help+ --help-types Display all known types+ --dump Dump information on which options are loaded+ from which RC files+ --[no]filter Force ack to treat standard input as a pipe+ (--filter) or tty (--nofilter)+ --man Man page+ --version Display version & copyright+ --thpppt Bill the Cat+ --bar The warning admiral+ --cathy Chocolate! Chocolate! Chocolate!++Exit status is 0 if match, 1 if no match.++ack's home page is at https://beyondgrep.com/++The full ack manual is available by running "ack --man".++This is version $VERSION of ack. Run "ack --version" for full version info.+END_OF_HELP++ return;+ }+++=head2 show_help_types()++Display the filetypes help subpage.++=cut++sub show_help_types {+ App::Ack::print( <<'END_OF_HELP' );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}++=head2 get_version_statement++Returns the version information for ack.++=cut++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+}++=head2 print_version_statement++Prints the version information for ack.++=cut++sub print_version_statement {+ App::Ack::print( get_version_statement() );++ return;+}++=head2 get_copyright++Return the copyright for ack.++=cut++sub get_copyright {+ return $COPYRIGHT;+}+++# print*() subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ 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;+}++=head2 output_to_pipe()++Returns true if ack's input is coming from a pipe.++=cut++sub output_to_pipe {+ return $output_to_pipe;+}++=head2 exit_from_ack++Exit from the application with the correct exit code.++Returns with 0 if a match was found, otherwise with 1. The number of matches is+handed in as the only argument.++=cut++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}+++=head1 COPYRIGHT & LICENSE++Copyright 2005-2017 Andy Lester.++This program is free software; you can redistribute it and/or modify+it under the terms of the Artistic License v2.0.++=cut++1; # End of App::Ack
@@ -0,0 +1,39 @@+# Before You Report an Issue...++Before you report an issue, please consult the [FAQ](https://beyondgrep.com/documentation/ack-2.16-man.html#faq).++# Asking to add a new filetype, or any enhancement++From the man page:++> All enhancement requests MUST first be posted to the ack-users mailing list at <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 ack in the ack issues list at Github: <https://github.com/petdance/ack2/issues>+>+> Patches are always welcome, but patches with tests get the most attention.++# Reporting an Issue++If you have an issue with ack, please add the following to your ticket:++ - What OS you're on+ - What version of ack you're using++Please try to reproduce your issue against the latest ack from git. If you are not able to+reproduce the issue with ack built from git, you should probably file the issue anyway, as+that probably means someone needs to make a release. =)++Also appreciated with an issue are the following:++ - Example invocations along with expected vs received output (see [#439](https://github.com/petdance/ack2/issues/439), great job!)+ - A `.t` test file that tests and verifies the behavior you expect+ - A patch that fixes your issue++# Ack 1.x++Keep in mind that if you're using ack 1.x, you should probably upgrade, as ack 1.x is no longer supported.++# Getting Help++Also, feel free to discuss your issues on the [ack mailing list](http://groups.google.com/group/ack-users)!
@@ -0,0 +1,463 @@+History file for ack. https://beyondgrep.com/+++2.18 Fri Mar 24 14:53:19 CDT 2017+====================================+ack 2.18 will probably be the final release in the ack 2.x series.+I'm going to be starting work on ack 3.000 in earnest. Still,+if you discover problems with ack 2, please report them to+https://github.com/petdance/ack2/issues++If you're interested in ack 3 development, please sign up+for the ack-dev mailing list and/or join the ack Slack. See+https://beyondgrep.com/community/ for details.++[INTERNALS]+Removed the abstraction of App::Ack::Resource and its subclass+App::Ack::Resource::Basic. We are abandoning the idea that we'll have+plugins.+++2.17_02 Thu Mar 23 22:25:13 CDT 2017+====================================+[FIXES]+ack no longer throws an undefined variable warning if it's called+from a directory that doesn't exist. (GH #634)++[DOCUMENTATION]+Explain that filetypes must be two characters or longer. (GH #389)++[INTERNALS]+Removed dependency on File::Glob which isn't used.+++2.17_01 Wed Mar 15 23:25:28 CDT 2017+====================================+[FIXES]+--context=0 (and its short counterpart -C 0) did not set to context+of 0. This means that a command-line --context=0 couldn't override+a --context=5 in your ackrc. Thanks, Ed Avis. (GH #595)++t/ack-s.t would fail in non-English locales. Thanks, Olivier Mengué.+(GH #485, GH #515)++[ENHANCEMENTS]+--after-context and --before-context (and their short counterparts+-A and -B) no longer require a value to be passed. If no value is+set, they default to 2. (GH #351)++Added .xhtml to the --html filetype. Added .wsdl to the --xml filetype.+Thanks, H.Merijn Brand. (GH #456)++[DOCUMENTATION]+Updated incorrect docs about how ack works. Thanks, Gerhard Poul.+(GH #543)+++2.16 Fri Mar 10 13:32:39 CST 2017+====================================+No changes since 2.15_03.++[CONFUSING BEHAVIOR & UPCOMING CHANGES]+The -w has a confusing behavior that it's had since back to ack 1.x+that will be changing in the future. It's not changing in this+version, but this is a heads-up that it's coming.++ack -w is "match a whole word", and ack does this by putting turning+your PATTERN into \bPATTERN\b. So "ack -w foo" effectively becomes+"ack \bfoo\b". Handy.++The problem is that ack doesn't put a \b before PATTERN if it begins+with a non-word character, and won't put a \b after PATTERN if it+ends with a non-word character.++The problem is that if you're searching for "fool" or "foot", but+only as a word, and you do "ack -w foo[lt]" or "ack -w (fool|foot)",+you'll get matches for "football and foolish" which certainly should+not match if you're using -w.+++2.15_03 Sun Feb 26 23:07:35 CST 2017+====================================+[ENHANCEMENTS]+Include .cljs, .cljc and .edn files with the --clojure filetype. Thanks,+Austin Chamberlin.++Added .xsd to the --xml filetype. Thanks, Nick Morrott.++Added support for Swift language. Thanks, Nikolaj Schumacher. (GH #512)++The MSYS2 project is now seen as Windows. Thanks, Ray Donnelly. (GH #450)++Expand the definition of OCaml files. Thanks, Marek Kubica. (GH #511)++Add support for Groovy Server Pages. Thanks, Ethan Mallove. (GH #469)++[INTERNALS]+Added test to test --output. Thanks, Varadinsky! (GH #587, GH #590)++[DOCUMENTATION]+Expanded the explanation of how the -w flag works. Thanks, Ed Avis.+(GH #585)+++2.15_02 Thu Dec 17 15:51:15 CST 2015+====================================+This is the first dev version in nine months. Many changes have gone+into this, probably more than have been listed here. We need to update+this list, but for now these are fixes we know are in here. My apologies+for not having this changelog accurate.++[FIXES]+Reverted an optimization to make \s work properly again. (GH #572,+GH #571, GH #562, GH #491, GH #498)++Fixed an out-of-date FAQ entry. Thanks, Jakub Wilk. (GH #580)++[ENHANCEMENTS]+The JSP filetype (--jsp) now recognizes .jspf files. Thanks, Sebastien+Feugere. (GH #586)++[INTERNALS]+Added test to make sure subdirs of target subdirs are ignored if+--ignore-dir applies to them. Thanks, Pete Houston. (GH #570)+++2.15_01 Fri Feb 13 16:23:54 CST 2015+====================================+[FIXES]+The -l and -c flags would sometimes return inaccurate results due to+a bug introduced in 2.14. Thanks to Elliot Shank for the report! (GH #491)++Behavior when using newlines in a search was inconsistent. Thanks to Yves Chevallier+for the report! (GH #522)++Add minimal requirement of Getopt::Long 2.38, not 2.35, for GetOptionsFromString.++Don't ignore directories that are specified as command line targets (GH #524)++Fix a bug where a regular expression that matches the empty string could cause ack+to go into an infinite loop (GH #542)++[ENHANCEMENTS]+Many optimizations and code cleanups. Thanks, Stephan Hohe.++Added --hpp option for C++ header files. Thankis, Steffen Jaeckel.++ack now supports --ignore-dir=match:.... Thanks, Ailin Nemui! (GitHub ticket #42)++ack also supports --ignore-dir=ext:..., and --noignore-dir supports match/ext as well+++2.14 Wed Sep 3 22:48:00 CDT 2014+====================================+[FIXES]+The -s flag would fail to suppress certain warnings. Thanks, Kassio+Borges. (GitHub ticket #439)++The -w flag would fail to work properly with regex features such as+alternation. Thanks to Ed Avis for the report (GitHub ticket #443)++The -g flag should now work faster on larger codebases. Thanks to+Manuel Meurer for the report (GitHub ticket #458)++[ENHANCEMENTS]+ack now ignores JavaScript and CSS source maps. Thanks, Chris+Rebert.++ack now ships with customized shell completion scripts for bash and zsh.+++2.13_06 Sat Jan 25 23:36:09 CST 2014+====================================+[FIXES]+More fixes for Windows tests. Thanks to GitHub user @ispedals.++[ENHANCEMENTS]++Add docs for available colors in ack.++2.13_05 Thu Jan 9 22:41:33 CST 2014+====================================+[FIXES]+More whack-a-mole with Windows failures. This time, it's POSIX::mkfifo+dying on Windows instead of returning undef like the docs implied,+or at least that I inferred.++[ENHANCEMENTS]+--create-ackrc keeps the comments that describe each of the options,+and it shows the ack version number.+++2.13_04 Sat Jan 4 11:17:28 CST 2014+====================================+[FIXES]+Fixed incorrect deduping of config files under Windows. Thanks,+Denis Howe.+++2.13_03 Thu Jan 2 22:23:03 CST 2014+====================================+[FIXES]+More build fixes for Windows. Windows config finder fixes from+James McCoy.++t/ack-named-pipes.t uses POSIX::mkfifo instead of the external+command, which should be more portable. Thanks, Pete Krawczyk.++[ENHANCEMENTS]+Now ignores Cabal (Haskell) sandboxes. Thanks, Fraser Tweedale.++Added filetypes for Jade, Smarty and Stylus. Thanks, Raúl Gundín.+++2.13_02 Fri Dec 27 12:54:27 CST 2013+====================================+[FIXES]+The building of ack-standalone relied on the output of `perldoc+-l`, which I apparently can't rely on having been installed. I've+changed the way that the squash program finds File::Next.+++2.13_01 Wed Dec 25 13:36:08 CST 2013+====================================+So this is Christmas+And what have you done+I'm sitting here hacking+On ack 2.13_01+ -- via Pete Krawczyk++[FIXES]+Issue #313: ack would fail when trying to check files for readability+on some networked filesystems, or on Mac OS X with ACLs. Now it+uses the filetest pragma. Thanks, Jonathan Perret.+++[INTERNALS]+ack's entire test suite now runs under Perl's -T taint flag. We'll+build more security tests on top of this.++Added some checks to the squash program that I hope will turn up+errors in the Windows builds.+++2.12 Tue Dec 3 07:05:02 CST 2013+====================================+[SECURITY FIXES]+This version of ack prevents the --pager, --regex and --output+options from being used from project-level ackrc files. It is+possible to execute malicious code with these options, and we want+to prevent the security risk of acking through a potentially malicious+codebase, such as one downloaded from an Internet site or checked+out from a code repository.++The --pager, --regex and --output options may still be used from+the global /etc/ackrc, your own private ~/.ackrc, the ACK_OPTIONS+environment variable, and of course from the command line.++[ENHANCEMENTS]+Now ignores Eclipse .metadata directory. Thanks, Steffen Jaeckel.++[INTERNALS]+Removed the Git revision tracking in the --version.+++2.11_02 Sun Oct 6 12:39:21 CDT 2013+====================================+[FIXES]+2.11_01 was mispackaged. This fixes that.+++2.11_01 Sun Sep 29 13:15:41 CDT 2013+====================================+[FIXES]+Fixed a race condition in t/file-permission.t that was causing+failures if tests were run in parallel.+++2.10 Tue Sep 24 16:24:11 CDT 2013+====================================+[ENHANCEMENTS]+Add --perltest for *.t files.++Added Matlab support. Thanks, Zertrin.++[FIXES]+Fix the test suite for Win32. Many thanks to Christian Walde for+bringing the severity of this issue to our attention, as well as+providing a Win32 development environment for us to work with.++Fixed Win32-detection in the Makefile.PL. Thanks, Michael Beijen+and Alexandr Ciornii.++More compatibility fixes for Perl 5.8.8.+++2.08 Thu Aug 22 23:11:45 CDT 2013+====================================+[ENHANCEMENTS]+ack now ignores CMake's build/cache directories by default. Thanks,+Volodymyr Medvid.++Add shebang matching for --lua files.++Add documentation for --ackrc.++Add Elixir filetype.++Add --cathy option. Thanks to Joe McMahon.++Add some helpful debugging tips when an invalid option is found.+Thanks to Charles Lee.++Ignore PDF files by default, because Perl will detect them as text.++Ignore .gif, .jpg, .jpeg and .png files. They won't normally be+selected, but this is an optimization so that ack doesn't have to+open them to know.++[FIXES]+Ack's colorizing of output would get confused with multiple sets+of parentheses. This has been fixed. (Issue #276)++Ack would get confused when trying to colorize the output in+DOS-format files. This has been fixed. (Issue #145)+++2.06+====================================+This mistake of an upload lived for only about 15 minutes.++++2.05_01 Tue May 28 10:12:04 CDT 2013+====================================+[ENHANCEMENTS]+We now ignore the node_modules directories created by npm. Thanks,+Konrad Borowski.++--pager without an argument implies --pager=$PAGER.++--perl now recognizes Plack-style .psgi files. Thanks, Ron Savage.++Added filetypes for Coffescript, JSON, LESS, and Sass.++[FIXES]+Command-line options now override options set in ackrc files.++ACK_PAGER and ACK_PAGER_COLOR now work as advertised.++Fix a bug resulting in uninitialized variable warnings when more+than one capture group was specified in the search pattern.++Make sure ack is happy to build and test under cron and other+console-less environments.++Colored output is now supported and on by default on Windows.++2.04 Fri Apr 26 22:47:55 CDT 2013+====================================+ack now runs on a standard Perl 5.8.8 install with no module updates.+The minimum Perl requirement for ack is now explicitly 5.8.8. Anything+before 5.8.8 will not work, and we've added checks. Thanks, Michael+McClimon.++[FIXES]+ack was colorizing captured groups even if --nocolor was given.+Thanks, Dale Sedivic.++[ENHANCEMENTS]+The --shell file type now recognizes the fish shell.++We now ignore minified CSS or Javascript, in the form of either *.css.min+or *.min.css, or *.js.min or *.min.js.++Added support for the Dart language.++ack 2.02 was much slower than ack 1.96, up to 8x slower in some cases.+These slowdowns have been mostly eliminated, and in some cases ack 2.04+is now faster than 1.96.+++2.02 Thu Apr 18 23:51:52 CDT 2013+====================================+[ENHANCEMENTS]+The firstlinematch file type detection option now only searches the+first 250 characters of the first line of the file. Otherwise, ack+would read entire text files that were only one line long, such as+minified JavaScript, and that would be slow. Thanks, Michael+McClimon.++[DOCUMENTATION]+Many clarifications and cleanups. Thanks, Michael McClimon.+++2.00 Wed Apr 17 22:49:41 CDT 2013+====================================++The first version of ack 2.0.+++# Incompatibilities with ack 1.x++ack 2 makes some big changes in its behaviors that could trip up+users who are used to the idiosyncracies of ack 1.x. These changes+could affect your searching happiness, so please read them.++* ack's default behavior is now to search all files that it identifies+as being text. ack 1.x would only search files that were of a file+type that it recognized.++* Removed the `-a` and `-u` options since the default is to search+all text files.++* Removed the `--binary` option. ack 2.0 will not find and search+through binary files.++* Removed the `--skipped` option.++* Removed the `--invert-file-match` option. `-v` now works with+`-g`. To list files that do not match `/foo/`++ ack -g foo -v++* `-g` now obeys all regex options: `-i`, `-w`, `-Q`, `-v`++* Removed the `-G` switch, because it was too confusing to have two+regexes specified on the command line. Now you use the `-x` switch+to pipe filenames from one `ack` invocation into another.++To search files with filename matching "sales" for the string "foo":++ ack -g sales | ack -x foo++# New features in ack 2.0++ack 2.0 will:++* By default searches all text files, as identified by Perl's `-T` operator+ * We will no longer have a `-a` switch.++* improved flexibility in defining filetype selectors+ * name equality ($filename eq 'Makefile')+ * glob-style matching (`*.pl` identifies a Perl file)+ * regex-style matching (`/\.pl$/i` identifies a Perl file)+ * shebang-line matching (shebang line matching `/usr/bin/perl/`+ identifies a Perl file)++* support for multiple ackrc files+ * global ackrc (/etc/ackrc)+ * https://github.com/petdance/ack/issues/#issue/79+ * user-specific ackrc (~/.ackrc)+ * per-project ackrc files (~/myproject/.ackrc)++* you can use --dump to figure which options are set where++* all inclusion/exclusion rules will be in the ackrc files+ * ack 2.0 has a set of definitions for filetypes, directories to+ include or exclude, etc, *but* these are only included so you don't+ need to ship an ackrc file to a new machine. You may tell ack to+ disregard these defaults if you like.++* In addition to the classic `--thpppt` option to draw Bill the+Cat, `ack --bar` will draw (of course) Admiral Ackbar.
Collection.pm
@@ -0,0 +1,77 @@+package App::Ack::Filter::Collection;++=head1 NAME++App::Ack::Filter::Collection++=head1 DESCRIPTION++The Ack::Filter::Collection class can contain filters and internally sort+them into groups. The groups can then be optimized for faster filtering.++Filters are grouped and replaced by a fast hash lookup. This leads to+improved performance when many such filters are active, like when using+the C<--known> command line option.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++sub new {+ my ( $class ) = @_;++ return bless {+ groups => {},+ ungrouped => [],+ }, $class;+}++sub filter {+ my ( $self, $resource ) = @_;++ for my $group (values %{$self->{'groups'}}) {+ if ($group->filter($resource)) {+ return 1;+ }+ }++ for my $filter (@{$self->{'ungrouped'}}) {+ if ($filter->filter($resource)) {+ return 1;+ }+ }++ return 0;+}++sub add {+ my ( $self, $filter ) = @_;++ if (exists $filter->{'groupname'}) {+ my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group());+ $group->add($filter);+ }+ else {+ push @{$self->{'ungrouped'}}, $filter;+ }++ return;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ my $ungrouped = $self->{'ungrouped'};++ return join(', ', map { "($_)" } @{$ungrouped});+}++1;
ConfigDefault.pm
@@ -0,0 +1,446 @@+package App::Ack::ConfigDefault;++use warnings;+use strict;++use App::Ack ();+++=head1 NAME++App::Ack::ConfigDefault++=head1 DESCRIPTION++A module that contains the default configuration for ack.++=cut++sub options {+ return split( /\n/, _options_block() );+}+++sub options_clean {+ return grep { /./ && !/^#/ } options();+}+++sub _options_block {+ my $lines = <<'HERE';+# This is the default ackrc for ack version ==VERSION==.++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++### Directories to ignore++# Bazaar+# http://bazaar.canonical.com/+--ignore-directory=is:.bzr++# Codeville+# http://freecode.com/projects/codeville+--ignore-directory=is:.cdv++# Interface Builder (Xcode)+# http://en.wikipedia.org/wiki/Interface_Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+# http://git-scm.com/+--ignore-directory=is:.git++# Mercurial+# http://mercurial.selenic.com/+--ignore-directory=is:.hg++# quilt+# http://directory.fsf.org/wiki/Quilt+--ignore-directory=is:.pc++# Subversion+# http://subversion.tigris.org/+--ignore-directory=is:.svn++# Monotone+# http://www.monotone.ca/+--ignore-directory=is:_MTN++# CVS+# http://savannah.nongnu.org/projects/cvs+--ignore-directory=is:CVS++# RCS+# http://www.gnu.org/software/rcs/+--ignore-directory=is:RCS++# SCCS+# http://en.wikipedia.org/wiki/Source_Code_Control_System+--ignore-directory=is:SCCS++# darcs+# http://darcs.net/+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+# http://www.gnu.org/software/autoconf/+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+# https://metacpan.org/release/Devel-Cover+--ignore-directory=is:cover_db++# Node modules created by npm+--ignore-directory=is:node_modules++# CMake cache+# http://www.cmake.org/+--ignore-directory=is:CMakeFiles++# Eclipse workspace folder+# http://eclipse.org/+--ignore-directory=is:.metadata++# Cabal (Haskell) sandboxes+# http://www.haskell.org/cabal/users-guide/installing-packages.html+--ignore-directory=is:.cabal-sandbox++### Files to ignore++# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files http://vim.org/+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.-]min[.]js$/+--ignore-file=match:/[.]js[.]min$/++# minified CSS+--ignore-file=match:/[.]min[.]css$/+--ignore-file=match:/[.]css[.]min$/++# JS and CSS source maps+--ignore-file=match:/[.]js[.]map$/+--ignore-file=match:/[.]css[.]map$/++# PDFs, because they pass Perl's -T detection+--ignore-file=ext:pdf++# Common graphics, just as an optimization+--ignore-file=ext:gif,jpg,jpeg,png+++### Filetypes defined++# Perl+# http://perl.org/+--type-add=perl:ext:pl,pm,pod,t,psgi+--type-add=perl:firstlinematch:/^#!.*\bperl/++# Perl tests+--type-add=perltest:ext:t++# Makefiles+# http://www.gnu.org/s/make/+--type-add=make:ext:mk+--type-add=make:ext:mak+--type-add=make:is:makefile+--type-add=make:is:Makefile+--type-add=make:is:Makefile.Debug+--type-add=make:is:Makefile.Release++# Rakefiles+# http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake+# http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada+# http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP+# http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net+# http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion+# http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure+# http://clojure.org/+--type-add=clojure:ext:clj,cljs,edn,cljc++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# CoffeeScript+# http://coffeescript.org/+--type-add=coffeescript:ext:coffee++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C++ header files+--type-add=hpp:ext:hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS+# http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Dart+# http://www.dartlang.org/+--type-add=dart:ext:dart++# Delphi+# http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# Elixir+# http://elixir-lang.org/+--type-add=elixir:ext:ex,exs++# Emacs Lisp+# http://www.gnu.org/software/emacs+--type-add=elisp:ext:el++# Erlang+# http://www.erlang.org/+--type-add=erlang:ext:erl,hrl++# Fortran+# http://en.wikipedia.org/wiki/Fortran+--type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp++# Go+# http://golang.org/+--type-add=go:ext:go++# Groovy+# http://groovy.codehaus.org/+--type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle++# GSP+# http://groovy.codehaus.org/GSP+--type-add=gsp:ext:gsp++# Haskell+# http://www.haskell.org/+--type-add=haskell:ext:hs,lhs++# HTML+--type-add=html:ext:htm,html,xhtml++# Jade+# http://jade-lang.com/+--type-add=jade:ext:jade++# Java+# http://www.oracle.com/technetwork/java/index.html+--type-add=java:ext:java,properties++# JavaScript+--type-add=js:ext:js++# JSP+# http://www.oracle.com/technetwork/java/javaee/jsp/index.html+--type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml++# JSON+# http://www.json.org/+--type-add=json:ext:json++# Less+# http://www.lesscss.org/+--type-add=less:ext:less++# Common Lisp+# http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua+# http://www.lua.org/+--type-add=lua:ext:lua+--type-add=lua:firstlinematch:/^#!.*\blua(jit)?/++# Objective-C+--type-add=objc:ext:m,h++# Objective-C+++--type-add=objcpp:ext:mm,h++# OCaml+# http://caml.inria.fr/+--type-add=ocaml:ext:ml,mli,mll,mly++# Matlab+# http://en.wikipedia.org/wiki/MATLAB+--type-add=matlab:ext:m++# Parrot+# http://www.parrot.org/+--type-add=parrot:ext:pir,pasm,pmc,ops,pod,pg,tg++# PHP+# http://www.php.net/+--type-add=php:ext:php,phpt,php3,php4,php5,phtml+--type-add=php:firstlinematch:/^#!.*\bphp/++# Plone+# http://plone.org/+--type-add=plone:ext:pt,cpt,metadata,cpy,py++# Python+# http://www.python.org/+--type-add=python:ext:py+--type-add=python:firstlinematch:/^#!.*\bpython/++# R+# http://www.r-project.org/+--type-add=rr:ext:R++# reStructured Text+# http://docutils.sourceforge.net/rst.html+--type-add=rst:ext:rst++# Ruby+# http://www.ruby-lang.org/+--type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec+--type-add=ruby:is:Rakefile+--type-add=ruby:firstlinematch:/^#!.*\bruby/++# Rust+# http://www.rust-lang.org/+--type-add=rust:ext:rs++# Sass+# http://sass-lang.com+--type-add=sass:ext:sass,scss++# Scala+# http://www.scala-lang.org/+--type-add=scala:ext:scala++# Scheme+# http://groups.csail.mit.edu/mac/projects/scheme/+--type-add=scheme:ext:scm,ss++# Shell+--type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/++# Smalltalk+# http://www.smalltalk.org/+--type-add=smalltalk:ext:st++# Smarty+# http://www.smarty.net/+--type-add=smarty:ext:tpl++# SQL+# http://www.iso.org/iso/catalogue_detail.htm?csnumber=45498+--type-add=sql:ext:sql,ctl++# Stylus+# http://learnboost.github.io/stylus/+--type-add=stylus:ext:styl++# Swift+# https://developer.apple.com/swift/+--type-add=swift:ext:swift+--type-add=swift:firstlinematch:/^#!.*\bswift/++# Tcl+# http://www.tcl.tk/+--type-add=tcl:ext:tcl,itcl,itk++# LaTeX+# http://www.latex-project.org/+--type-add=tex:ext:tex,cls,sty++# Template Toolkit (Perl)+# http://template-toolkit.org/+--type-add=tt:ext:tt,tt2,ttml++# Visual Basic+--type-add=vb:ext:bas,cls,frm,ctl,vb,resx++# Verilog+--type-add=verilog:ext:v,vh,sv++# VHDL+# http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome+--type-add=vhdl:ext:vhd,vhdl++# Vim+# http://www.vim.org/+--type-add=vim:ext:vim++# XML+# http://www.w3.org/TR/REC-xml/+--type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl+--type-add=xml:firstlinematch:/<[?]xml/++# YAML+# http://yaml.org/+--type-add=yaml:ext:yaml,yml+HERE+ $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;++ return $lines;+}++1;
ConfigFinder.pm
@@ -0,0 +1,177 @@+package App::Ack::ConfigFinder;++=head1 NAME++App::Ack::ConfigFinder++=head1 DESCRIPTION++A module that contains the logic for locating the various configuration+files.++=head1 LOCATING CONFIG FILES++First, ack looks for a global ackrc.++=over++=item On Windows, this is `ackrc` in either COMMON_APPDATA or APPDATA.+If `ackrc` is present in both directories, ack uses both files in that+order.++=item On a non-Windows OS, this is `/etc/ackrc`.++=back++Then, ack looks for a user-specific ackrc if the HOME environment+variable is set. This is either F<$HOME/.ackrc> or F<$HOME/_ackrc>.++Then, ack looks for a project-specific ackrc file. ack searches+up the directory hierarchy for the first `.ackrc` or `_ackrc` file.+If this is one of the ackrc files found in the previous steps, it is+not loaded again.++It is a fatal error if a directory contains both F<.ackrc> and F<_ackrc>.++After ack loads the options from the found ackrc files, ack looks+at the C<ACKRC_OPTIONS> environment variable.++Finally, ack takes settings from the command line.++=cut++use strict;+use warnings;++use App::Ack ();+use App::Ack::ConfigDefault;+use Cwd 3.00 ();+use File::Spec 3.00;++use if ($^O eq 'MSWin32'), 'Win32';++=head1 METHODS++=head2 new++Creates a new config finder.++=cut++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}+++sub _remove_redundancies {+ my @configs = @_;++ my %seen;+ foreach my $config (@configs) {+ my $key = $config->{path};+ if ( not $App::Ack::is_windows ) {+ # On Unix, uniquify on inode.+ my ($dev, $inode) = (stat $key)[0, 1];+ $key = "$dev:$inode" if defined $dev;+ }+ undef $config if $seen{$key}++;+ }+ return grep { defined } @configs;+}+++sub _check_for_ackrc {+ return unless defined $_[0];++ my @files = grep { -f }+ map { File::Spec->catfile(@_, $_) }+ qw(.ackrc _ackrc);++ die File::Spec->catdir(@_) . " contains both .ackrc and _ackrc.\n" .+ "Please remove one of those files.\n"+ if @files > 1;++ return wantarray ? @files : $files[0];+} # end _check_for_ackrc+++=head2 $finder->find_config_files++Locates config files, and returns a list of them.++=cut++sub find_config_files {+ my @config_files;++ if ( $App::Ack::is_windows ) {+ push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (+ Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),+ Win32::GetFolderPath(Win32::CSIDL_APPDATA()),+ );+ }+ else {+ push @config_files, { path => '/etc/ackrc' };+ }+++ if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {+ push @config_files, { path => $ENV{'ACKRC'} };+ }+ else {+ push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});+ }++ my $cwd = Cwd::getcwd();+ return () unless defined $cwd;++ # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this.+ $cwd =~ /(.+)/;+ $cwd = $1;+ my @dirs = File::Spec->splitdir( $cwd );+ while(@dirs) {+ my $ackrc = _check_for_ackrc(@dirs);+ if(defined $ackrc) {+ push @config_files, { project => 1, path => $ackrc };+ last;+ }+ pop @dirs;+ }++ # We only test for existence here, so if the file is deleted out from under us, this will fail later.+ return _remove_redundancies( @config_files );+}+++=head2 read_rcfile++Reads the contents of the .ackrc file and returns the arguments.++=cut++sub read_rcfile {+ my $file = shift;++ return unless defined $file && -e $file;++ my @lines;++ open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" );+ while ( my $line = <$fh> ) {+ chomp $line;+ $line =~ s/^\s+//;+ $line =~ s/\s+$//;++ next if $line eq '';+ next if $line =~ /^\s*#/;++ push( @lines, $line );+ }+ close $fh or App::Ack::die( "Unable to close $file: $!" );++ return @lines;+}++1;
ConfigLoader.pm
@@ -0,0 +1,831 @@+package App::Ack::ConfigLoader;++use strict;+use warnings;++use App::Ack ();+use App::Ack::ConfigDefault ();+use App::Ack::ConfigFinder ();+use App::Ack::Filter;+use App::Ack::Filter::Collection;+use App::Ack::Filter::Default;+use Carp 1.04 ();+use Getopt::Long 2.38 ();+use Text::ParseWords 3.1 ();++=head1 NAME++App::Ack::ConfigLoader++=head1 DESCRIPTION++Logic for loading configuration files.++=head1 FUNCTIONS++=head2 process_args( @sources )++=cut++my @INVALID_COMBINATIONS;++BEGIN {+ my @context = qw( -A -B -C --after-context --before-context --context );+ my @pretty = qw( --heading --group --break );+ my @filename = qw( -h -H --with-filename --no-filename );++ @INVALID_COMBINATIONS = (+ # XXX normalize+ [qw(-l)] => [@context, @pretty, @filename, qw(-L -o --passthru --output --max-count --column -f -g --show-types)],+ [qw(-L)] => [@context, @pretty, @filename, qw(-l -o --passthru --output --max-count --column -f -g --show-types -c --count)],+ [qw(--line)] => [@context, @pretty, @filename, qw(-l --files-with-matches --files-without-matches -L -o --passthru --match -m --max-count -1 -c --count --column --print0 -f -g --show-types)],+ [qw(-o)] => [@context, qw(--output -c --count --column --column -f --show-types)],+ [qw(--passthru)] => [@context, qw(--output --column -m --max-count -1 -c --count -f -g)],+ [qw(--output)] => [@context, qw(-c --count -f -g)],+ [qw(--match)] => [qw(-f -g)],+ [qw(-m --max-count)] => [qw(-1 -f -g -c --count)],+ [qw(-h --no-filename)] => [qw(-H --with-filename -f -g --group --heading)],+ [qw(-H --with-filename)] => [qw(-h --no-filename -f -g)],+ [qw(-c --count)] => [@context, @pretty, qw(--column -f -g)],+ [qw(--column)] => [qw(-f -g)],+ [@context] => [qw(-f -g)],+ [qw(-f)] => [qw(-g), @pretty],+ [qw(-g)] => [qw(-f), @pretty],+ );+}++sub _generate_ignore_dir {+ my ( $option_name, $opt ) = @_;++ my $is_inverted = $option_name =~ /^--no/;++ return sub {+ my ( undef, $dir ) = @_;++ $dir = App::Ack::remove_dir_sep( $dir );+ if ( $dir !~ /:/ ) {+ $dir = 'is:' . $dir;+ }++ my ( $filter_type, $args ) = split /:/, $dir, 2;++ if ( $filter_type eq 'firstlinematch' ) {+ Carp::croak( qq{Invalid filter specification "$filter_type" for option '$option_name'} );+ }++ my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));+ my $collection;++ my $previous_inversion_matches = $opt->{idirs} && !($is_inverted xor $opt->{idirs}[-1]->is_inverted());++ if ( $previous_inversion_matches ) {+ $collection = $opt->{idirs}[-1];++ if ( $is_inverted ) {+ # XXX this relies on invert of an inverted filter+ # to return the original+ $collection = $collection->invert()+ }+ }+ else {+ $collection = App::Ack::Filter::Collection->new();++ if ( $is_inverted ) {+ push @{ $opt->{idirs} }, $collection->invert();+ }+ else {+ push @{ $opt->{idirs} }, $collection;+ }+ }++ $collection->add($filter);++ if ( $filter_type eq 'is' ) {+ $collection->add(App::Ack::Filter::IsPath->new($args));+ }+ };+}++sub process_filter_spec {+ my ( $spec ) = @_;++ if ( $spec =~ /^(\w+):(\w+):(.*)/ ) {+ my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 );++ return ( $type_name,+ App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) );+ }+ elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification.+ my ( $type_name, $extensions ) = ( $1, $2 );++ my @extensions = split(/,/, $extensions);+ foreach my $extension ( @extensions ) {+ $extension =~ s/^[.]//;+ }++ return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) );+ }+ else {+ Carp::croak "invalid filter specification '$spec'";+ }+}+++sub uninvert_filter {+ my ( $opt, @filters ) = @_;++ return unless defined $opt->{filters} && @filters;++ # Loop through all the registered filters. If we hit one that+ # matches this extension and it's inverted, we need to delete it from+ # the options.+ for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) {+ my $opt_filter = @{ $opt->{filters} }[$i];++ # XXX Do a real list comparison? This just checks string equivalence.+ if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) {+ splice @{ $opt->{filters} }, $i, 1;+ $i--;+ }+ }++ return;+}+++sub process_filetypes {+ my ( $opt, $arg_sources ) = @_;++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version'); # start with default options, minus some annoying ones+ Getopt::Long::Configure(+ 'no_ignore_case',+ 'no_auto_abbrev',+ 'pass_through',+ );+ my %additional_specs;++ my $add_spec = sub {+ my ( undef, $spec ) = @_;++ my ( $name, $filter ) = process_filter_spec($spec);++ push @{ $App::Ack::mappings{$name} }, $filter;++ $additional_specs{$name . '!'} = sub {+ my ( undef, $value ) = @_;++ my @filters = @{ $App::Ack::mappings{$name} };+ if ( not $value ) {+ @filters = map { $_->invert() } @filters;+ }+ else {+ uninvert_filter( $opt, @filters );+ }++ push @{ $opt->{'filters'} }, @filters;+ };+ };++ my $set_spec = sub {+ my ( undef, $spec ) = @_;++ my ( $name, $filter ) = process_filter_spec($spec);++ $App::Ack::mappings{$name} = [ $filter ];++ $additional_specs{$name . '!'} = sub {+ my ( undef, $value ) = @_;++ my @filters = @{ $App::Ack::mappings{$name} };+ if ( not $value ) {+ @filters = map { $_->invert() } @filters;+ }++ push @{ $opt->{'filters'} }, @filters;+ };+ };++ my $delete_spec = sub {+ my ( undef, $name ) = @_;++ delete $App::Ack::mappings{$name};+ delete $additional_specs{$name . '!'};+ };++ my %type_arg_specs = (+ 'type-add=s' => $add_spec,+ 'type-set=s' => $set_spec,+ 'type-del=s' => $delete_spec,+ );++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ if ( ref($args) ) {+ # $args are modified in place, so no need to munge $arg_sources+ local @ARGV = @{$args};+ Getopt::Long::GetOptions(%type_arg_specs);+ @{$args} = @ARGV;+ }+ else {+ ( undef, $source->{contents} ) =+ Getopt::Long::GetOptionsFromString($args, %type_arg_specs);+ }+ }++ $additional_specs{'k|known-types'} = sub {+ my ( undef, $value ) = @_;++ my @filters = map { @{$_} } values(%App::Ack::mappings);++ push @{ $opt->{'filters'} }, @filters;+ };++ return \%additional_specs;+}+++sub removed_option {+ my ( $option, $explanation ) = @_;++ $explanation ||= '';+ return sub {+ warn "Option '$option' is not valid in ack 2.\n$explanation";+ exit 1;+ };+}+++sub get_arg_spec {+ my ( $opt, $extra_specs ) = @_;++ my $dash_a_explanation = <<'EOT';+You don't need -a, ack 1.x users. This is because ack 2.x has+-k/--known-types which makes it only select files of known types, rather+than any text file (which is the behavior of ack 1.x).++If you're surprised to see this message because you didn't put -a on the+command line, you may have options in an .ackrc, or in the ACKRC_OPTIONS+environment variable. Try using the --dump flag to help find it.+EOT++=begin Adding-Options++ *** IF YOU ARE MODIFYING ACK PLEASE READ THIS ***++ If you plan to add a new option to ack, please make sure of+ the following:++ * Your new option has a test underneath the t/ directory.+ * Your new option is explained when a user invokes ack --help.+ (See App::Ack::show_help)+ * Your new option is explained when a user invokes ack --man.+ (See the POD at the end of ./ack)+ * Add your option to t/config-loader.t+ * Add your option to t/Util.pm#get_options+ * Add your option's description and aliases to dev/generate-completion-scripts.pl+ * Go through the list of options already available, and consider+ whether your new option can be considered mutually exclusive+ with another option.++=end Adding-Options++=cut++ sub _context_value {+ my $val = shift;++ # Contexts default to 2.+ return (!defined($val) || ($val < 0)) ? 2 : $val;+ }++ return {+ 1 => sub { $opt->{1} = $opt->{m} = 1 },+ 'A|after-context:-1' => sub { shift; $opt->{after_context} = _context_value(shift) },+ 'B|before-context:-1' => sub { shift; $opt->{before_context} = _context_value(shift) },+ 'C|context:-1' => sub { shift; $opt->{before_context} = $opt->{after_context} = _context_value(shift) },+ 'a' => removed_option('-a', $dash_a_explanation),+ 'all' => removed_option('--all', $dash_a_explanation),+ 'break!' => \$opt->{break},+ c => \$opt->{count},+ 'color|colour!' => \$opt->{color},+ 'color-match=s' => \$ENV{ACK_COLOR_MATCH},+ 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},+ 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},+ 'column!' => \$opt->{column},+ count => \$opt->{count},+ 'create-ackrc' => sub { print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; },+ 'env!' => sub {+ my ( undef, $value ) = @_;++ if ( !$value ) {+ $opt->{noenv_seen} = 1;+ }+ },+ f => \$opt->{f},+ 'files-from=s' => \$opt->{files_from},+ 'filter!' => \$App::Ack::is_filter_mode,+ flush => \$opt->{flush},+ 'follow!' => \$opt->{follow},+ g => \$opt->{g},+ G => removed_option('-G'),+ 'group!' => sub { shift; $opt->{heading} = $opt->{break} = shift },+ 'heading!' => \$opt->{heading},+ 'h|no-filename' => \$opt->{h},+ 'H|with-filename' => \$opt->{H},+ 'i|ignore-case' => \$opt->{i},+ 'ignore-directory|ignore-dir=s' => _generate_ignore_dir('--ignore-dir', $opt),+ 'ignore-file=s' => sub {+ my ( undef, $file ) = @_;++ my ( $filter_type, $args ) = split /:/, $file, 2;++ my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));++ if ( !$opt->{ifiles} ) {+ $opt->{ifiles} = App::Ack::Filter::Collection->new();+ }+ $opt->{ifiles}->add($filter);+ },+ 'lines=s' => sub { shift; my $val = shift; push @{$opt->{lines}}, $val },+ 'l|files-with-matches'+ => \$opt->{l},+ 'L|files-without-matches'+ => \$opt->{L},+ 'm|max-count=i' => \$opt->{m},+ 'match=s' => \$opt->{regex},+ 'n|no-recurse' => \$opt->{n},+ o => sub { $opt->{output} = '$&' },+ 'output=s' => \$opt->{output},+ 'pager:s' => sub {+ my ( undef, $value ) = @_;++ $opt->{pager} = $value || $ENV{PAGER};+ },+ 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt),+ 'nopager' => sub { $opt->{pager} = undef },+ 'passthru' => \$opt->{passthru},+ 'print0' => \$opt->{print0},+ 'Q|literal' => \$opt->{Q},+ 'r|R|recurse' => sub { $opt->{n} = 0 },+ 's' => \$opt->{dont_report_bad_filenames},+ 'show-types' => \$opt->{show_types},+ 'smart-case!' => \$opt->{smart_case},+ 'sort-files' => \$opt->{sort_files},+ 'type=s' => sub {+ my ( $getopt, $value ) = @_;++ my $cb_value = 1;+ if ( $value =~ s/^no// ) {+ $cb_value = 0;+ }++ my $callback = $extra_specs->{ $value . '!' };++ if ( $callback ) {+ $callback->( $getopt, $cb_value );+ }+ else {+ Carp::croak( "Unknown type '$value'" );+ }+ },+ 'u' => removed_option('-u'),+ 'unrestricted' => removed_option('--unrestricted'),+ 'v|invert-match' => \$opt->{v},+ 'w|word-regexp' => \$opt->{w},+ 'x' => sub { $opt->{files_from} = '-' },++ 'version' => sub { App::Ack::print_version_statement(); exit; },+ 'help|?:s' => sub { shift; App::Ack::show_help(@_); exit; },+ 'help-types' => sub { App::Ack::show_help_types(); exit; },+ 'man' => sub { App::Ack::show_man(); exit; },+ $extra_specs ? %{$extra_specs} : (),+ }; # arg_specs+}+++sub process_other {+ my ( $opt, $extra_specs, $arg_sources ) = @_;++ # Start with default options, minus some annoying ones.+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure(+ 'bundling',+ 'no_ignore_case',+ );++ my $argv_source;+ my $is_help_types_active;++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ if ( $source_name eq 'ARGV' ) {+ $argv_source = $args;+ last;+ }+ }++ if ( $argv_source ) { # This *should* always be true, but you never know...+ my @copy = @{$argv_source};+ local @ARGV = @copy;++ Getopt::Long::Configure('pass_through');++ Getopt::Long::GetOptions(+ 'help-types' => \$is_help_types_active,+ );++ Getopt::Long::Configure('no_pass_through');+ }++ my $arg_specs = get_arg_spec($opt, $extra_specs);++ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ my $args_for_source = $arg_specs;++ if ( $source->{project} ) {+ my $illegal = sub {+ die "Options --output, --pager and --match are forbidden in project .ackrc files.\n";+ };++ $args_for_source = {+ %{$args_for_source},+ 'output=s' => $illegal,+ 'pager:s' => $illegal,+ 'match=s' => $illegal,+ };+ }++ my $ret;+ if ( ref($args) ) {+ local @ARGV = @{$args};+ $ret = Getopt::Long::GetOptions( %{$args_for_source} );+ @{$args} = @ARGV;+ }+ else {+ ( $ret, $source->{contents} ) =+ Getopt::Long::GetOptionsFromString( $args, %{$args_for_source} );+ }+ if ( !$ret ) {+ if ( !$is_help_types_active ) {+ my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name";+ App::Ack::die( "Invalid option $where" );+ }+ }+ if ( $opt->{noenv_seen} ) {+ App::Ack::die( "--noenv found in $source_name" );+ }+ }++ # XXX We need to check on a -- in the middle of a non-ARGV source++ return;+}+++sub should_dump_options {+ my ( $sources ) = @_;++ foreach my $source (@{$sources}) {+ my ( $name, $options ) = @{$source}{qw/name contents/};++ if($name eq 'ARGV') {+ my $dump;+ local @ARGV = @{$options};+ Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');+ Getopt::Long::GetOptions(+ 'dump' => \$dump,+ );+ @{$options} = @ARGV;+ return $dump;+ }+ }+ return;+}+++sub explode_sources {+ my ( $sources ) = @_;++ my @new_sources;++ Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');++ my %opt;+ my $arg_spec = get_arg_spec(\%opt);++ my $add_type = sub {+ my ( undef, $arg ) = @_;++ # XXX refactor?+ if ( $arg =~ /(\w+)=/) {+ $arg_spec->{$1} = sub {};+ }+ else {+ ( $arg ) = split /:/, $arg;+ $arg_spec->{$arg} = sub {};+ }+ };++ my $del_type = sub {+ my ( undef, $arg ) = @_;++ delete $arg_spec->{$arg};+ };++ foreach my $source (@{$sources}) {+ my ( $name, $options ) = @{$source}{qw/name contents/};+ if ( ref($options) ne 'ARRAY' ) {+ $source->{contents} = $options =+ [ Text::ParseWords::shellwords($options) ];+ }++ for my $j ( 0 .. @{$options}-1 ) {+ next unless $options->[$j] =~ /^-/;+ my @chunk = ( $options->[$j] );+ push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/;+ $j--;++ my @copy = @chunk;+ local @ARGV = @chunk;+ Getopt::Long::GetOptions(+ 'type-add=s' => $add_type,+ 'type-set=s' => $add_type,+ 'type-del=s' => $del_type,+ );+ Getopt::Long::GetOptions( %{$arg_spec} );++ push @new_sources, {+ name => $name,+ contents => \@copy,+ };+ }+ }++ return \@new_sources;+}+++sub compare_opts {+ my ( $a, $b ) = @_;++ my $first_a = $a->[0];+ my $first_b = $b->[0];++ $first_a =~ s/^--?//;+ $first_b =~ s/^--?//;++ return $first_a cmp $first_b;+}+++sub dump_options {+ my ( $sources ) = @_;++ $sources = explode_sources($sources);++ my %opts_by_source;+ my @source_names;++ foreach my $source (@{$sources}) {+ my ( $name, $contents ) = @{$source}{qw/name contents/};+ if ( not $opts_by_source{$name} ) {+ $opts_by_source{$name} = [];+ push @source_names, $name;+ }+ push @{$opts_by_source{$name}}, $contents;+ }++ foreach my $name (@source_names) {+ my $contents = $opts_by_source{$name};++ print $name, "\n";+ print '=' x length($name), "\n";+ print ' ', join(' ', @{$_}), "\n" foreach sort { compare_opts($a, $b) } @{$contents};+ }++ return;+}+++sub remove_default_options_if_needed {+ my ( $sources ) = @_;++ my $default_index;++ foreach my $index ( 0 .. $#{$sources} ) {+ if ( $sources->[$index]{'name'} eq 'Defaults' ) {+ $default_index = $index;+ last;+ }+ }++ return $sources unless defined $default_index;++ my $should_remove = 0;++ # Start with default options, minus some annoying ones.+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure(+ 'no_ignore_case',+ 'no_auto_abbrev',+ 'pass_through',+ );++ foreach my $index ( $default_index + 1 .. $#{$sources} ) {+ my ( $name, $args ) = @{$sources->[$index]}{qw/name contents/};++ if (ref($args)) {+ local @ARGV = @{$args};+ Getopt::Long::GetOptions(+ 'ignore-ack-defaults' => \$should_remove,+ );+ @{$args} = @ARGV;+ }+ else {+ ( undef, $sources->[$index]{contents} ) = Getopt::Long::GetOptionsFromString($args,+ 'ignore-ack-defaults' => \$should_remove,+ );+ }+ }++ Getopt::Long::Configure('default');+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ return $sources unless $should_remove;++ my @copy = @{$sources};+ splice @copy, $default_index, 1;+ return \@copy;+}+++sub check_for_mutually_exclusive_options {+ my ( $arg_sources ) = @_;++ my %mutually_exclusive_with;+ my @copy = @{$arg_sources};++ for(my $i = 0; $i < @INVALID_COMBINATIONS; $i += 2) {+ my ( $lhs, $rhs ) = @INVALID_COMBINATIONS[ $i, $i + 1 ];++ foreach my $l_opt ( @{$lhs} ) {+ foreach my $r_opt ( @{$rhs} ) {+ push @{ $mutually_exclusive_with{ $l_opt } }, $r_opt;+ push @{ $mutually_exclusive_with{ $r_opt } }, $l_opt;+ }+ }+ }++ while( @copy ) {+ my %set_opts;++ my $source = shift @copy;+ my ( $source_name, $args ) = @{$source}{qw/name contents/};+ $args = ref($args) ? [ @{$args} ] : [ Text::ParseWords::shellwords($args) ];++ foreach my $opt ( @{$args} ) {+ next unless $opt =~ /^[-+]/;+ last if $opt eq '--';++ if( $opt =~ /^(.*)=/ ) {+ $opt = $1;+ }+ elsif ( $opt =~ /^(-[^-]).+/ ) {+ $opt = $1;+ }++ $set_opts{ $opt } = 1;++ my $mutex_opts = $mutually_exclusive_with{ $opt };++ next unless $mutex_opts;++ foreach my $mutex_opt ( @{$mutex_opts} ) {+ if($set_opts{ $mutex_opt }) {+ die "Options '$mutex_opt' and '$opt' are mutually exclusive\n";+ }+ }+ }+ }++ return;+}+++sub process_args {+ my $arg_sources = \@_;++ my %opt = (+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},+ );++ check_for_mutually_exclusive_options($arg_sources);++ $arg_sources = remove_default_options_if_needed($arg_sources);++ if ( should_dump_options($arg_sources) ) {+ dump_options($arg_sources);+ exit(0);+ }++ my $type_specs = process_filetypes(\%opt, $arg_sources);+ process_other(\%opt, $type_specs, $arg_sources);+ while ( @{$arg_sources} ) {+ my $source = shift @{$arg_sources};+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ # All of our sources should be transformed into an array ref+ if ( ref($args) ) {+ if ( $source_name eq 'ARGV' ) {+ @ARGV = @{$args};+ }+ elsif (@{$args}) {+ Carp::croak "source '$source_name' has extra arguments!";+ }+ }+ else {+ Carp::croak 'The impossible has occurred!';+ }+ }+ my $filters = ($opt{filters} ||= []);++ # Throw the default filter in if no others are selected.+ if ( not grep { !$_->is_inverted() } @{$filters} ) {+ push @{$filters}, App::Ack::Filter::Default->new();+ }+ return \%opt;+}+++sub retrieve_arg_sources {+ my @arg_sources;++ my $noenv;+ my $ackrc;++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure('pass_through');+ Getopt::Long::Configure('no_auto_abbrev');++ Getopt::Long::GetOptions(+ 'noenv' => \$noenv,+ 'ackrc=s' => \$ackrc,+ );++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ my @files;++ if ( !$noenv ) {+ my $finder = App::Ack::ConfigFinder->new;+ @files = $finder->find_config_files;+ }+ if ( $ackrc ) {+ # We explicitly use open so we get a nice error message.+ # XXX This is a potential race condition!.+ if(open my $fh, '<', $ackrc) {+ close $fh;+ }+ else {+ die "Unable to load ackrc '$ackrc': $!"+ }+ push( @files, { path => $ackrc } );+ }++ push @arg_sources, {+ name => 'Defaults',+ contents => [ App::Ack::ConfigDefault::options_clean() ],+ };++ foreach my $file ( @files) {+ my @lines = App::Ack::ConfigFinder::read_rcfile($file->{path});++ if(@lines) {+ push @arg_sources, {+ name => $file->{path},+ contents => \@lines,+ project => $file->{project},+ };+ }+ }++ if ( $ENV{ACK_OPTIONS} && !$noenv ) {+ push @arg_sources, {+ name => 'ACK_OPTIONS',+ contents => $ENV{ACK_OPTIONS},+ };+ }++ push @arg_sources, {+ name => 'ARGV',+ contents => [ @ARGV ],+ };++ return @arg_sources;+}++1; # End of App::Ack::ConfigLoader
@@ -0,0 +1,34 @@+# ack2 design++Here we try to document ack's design, including design decisions+that have already been made, so that we don't keep rehashing them.++# Design choices that are inviolate.++* ack will not do replacing of text. It is not a replacement for sed.++* ack will only use core Perl modules and File::Next. No other modules may be required.++* ack must be able to be built as a single-file standalone file.++* ack must be cross-platform. Specifically, it must run on Windows.++# Design questions that have been investigated++## Would ack be faster if we used the integer pragma?++It seems not. Brian M. Carlson investigated this and reported his+findings here: https://github.com/petdance/ack2/issues/398++There seemed to be no effect.++## Can we make --sort be the default?++No. Sorting the filenames requires reading in the entire directory+of filenames before searching, and this can be a performance hit.+In fact, it can kill ack entirely on directories with absurdly large+numbers of files in them.++## Why not make ack use /usr/bin/env in the shebang instead of /usr/bin/perl?++The Perl toolchain takes care of the shebang at install time.
@@ -0,0 +1,165 @@+# Ack Developer's Guide++This is a guide intended to help new developers work on ack. You may also want to read+[CONTRIBUTING.md](https://github.com/petdance/ack2/blob/dev/CONTRIBUTING.md).++## Helper Scripts++### tack++The `tack` script runs ack from repository root using the module files placed under `blib/lib` via+the build process. It's a quick and easy way to try out new code, or to check some debugging+output. Since it relies on the module files under `blib/lib`, make sure you run `make` before+running `tack`!++### ack-standalone++This script is built by the `make ack-standalone` rule, and is a single file distribution of ack.+The `make ack-standalone` rule uses the `squash` script to perform the actual building.++### squash++Takes a list of Perl source files and builds a single Perl script containing all of the input+files.++### dev/timings.pl++Times ack in a number of common scenarios. We currently use a checkout of the Parrot repository+for testing, as it's a medium sized codebase. `dev/timings.pl` expects a checkout of the Parrot+repository in your `$HOME` directory. See `dev/timings.pl --help` for more information on its+options.++### dev/display-option-coverage.pl++The test suite can build what we call an "option coverage" file if the `ACK_OPTION_COVERAGE`+environment variable is set to a truthy value. The option coverage file lists all of the options+provided to the various options provided to the various invocations of ack used in the test suite.+`dev/display-options-coverage.pl` reads this file and prints the options that are *not* used in+the test suite. This helps find new options that haven't had tests written for them yet.++### Helpful Makefile rules++#### nytprof++Runs ack through `Devel::NYTProf`.++#### timings++Shorthand for `dev/timings.pl`.++## Running Tests++### `make test`++Runs the `make test_classic` and `make test_standalone` rules.++### `make test_classic`++Runs the test suite using the module files in the repository.++### `make test_standalone`++Runs the test suite using the `ack-standalone` script.++### `prove -b $TEST_FILE`++Can be used to run individual test files. This relies on the module files being+placed under `blib/lib`, so be sure to run `make` before running `prove`!++## Branching++Development is *not* done on master. We use a dev branch named+`dev`, and from there topic branches named for a specific topic.++ master -> dev -> docs+ \-> speedups+ \-> issue473++The only time we merge `dev` down to `master` is when doing a+release. There are no branches off of `master` other than `dev`.++## Coding Standards++Our policy on commits is that they're cheap; we tend to throw files+into the repository that could prove useful to others, even if we+will remove them later.++### perlcritic/perltidy++We have a perlcriticrc and a perltidyrc file for checking the Perl source+files.++## How to cut a release++### For all releases++These can be done by anyone, except for the upload to CPAN.++* Prep all source files for release.+ * If this is a final release, replace all `2.XX_01` version numbers with `2.YY`, where XX is odd and YY is even.+ * Update the `Changes` file with the new version numbers and put a date in the header.+* `make clean` and `make test` repeatedly.+* `make disttest`+ * Should pass.+* `make tardist`+ * Creates a tarball+* Upload the tarball to pause.cpan.org+* Tag the release+ * `git tag 2.XX`+ * `git push --tags`++### For an official release (like `2.06`)++Do all of the above for a development release, plus:++* Put a version of standalone into the garage.+* Update beyondgrep.com+ * https://github.com/petdance/beyondgrep+ * Front page version number+ * man page archive+* Announce it+ * Mail to ack-users and ack-announce.+ * Post to ack's Google+ page.+ * Tweet on @beyondgrep.++## Guidelines++### Adding new files to ack2++TODO++## Issues++Our issues are hosted on GitHub.++https://github.com/petdance/ack2/issues++### Tags++TODO++## But I Can't Contribute to ack, because...++### ...I don't have any spare time.++I know how you feel! But any contribution is welcome; you don't need to be a full-time contributor.+Every test file, every issue solved, every bug fixed matters!++### ...I don't know Perl.++That's ok; it's easy to learn! Perl may have a reputation for being unreadable, but ack is written+in a very easy-to-read style.++TODO Mention http://perl-begin.org/++### ...I don't know where to start.++TODO++## How do I...?++### ...add a new command line option?++TODO++### MORE
Default.pm
@@ -0,0 +1,30 @@+package App::Ack::Filter::Default;++=head1 NAME++App::Ack::Filter::Default++=head1 DESCRIPTION++The class that implements the filter that ack uses by+default if you don't specify any filters on the command line.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}++sub filter {+ my ( $self, $resource ) = @_;++ return -T $resource->name;+}++1;
Extension.pm
@@ -0,0 +1,65 @@+package App::Ack::Filter::Extension;++=head1 NAME++App::Ack::Filter::Extension++=head1 DESCRIPTION++Implements filters based on extensions.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++use App::Ack::Filter ();+use App::Ack::Filter::ExtensionGroup ();++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;
ExtensionGroup.pm
@@ -0,0 +1,58 @@+package App::Ack::Filter::ExtensionGroup;++=head1 NAME++App::Ack::Filter::ExtensionGroup++=head1 DESCRIPTION++The App::Ack::Filter::ExtensionGroup class optimizes multiple ::Extension+calls into one container. See App::Ack::Filter::IsGroup for details.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ foreach my $ext (@{$filter->{extensions}}) {+ $self->{data}->{lc $ext} = 1;+ }++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ if ($resource->name =~ /[.]([^.]*)$/) {+ return exists $self->{'data'}->{lc $1};+ }++ return 0;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', map { ".$_" } sort keys %{$self->{data}});+}++1;
Filter.pm
@@ -0,0 +1,140 @@+package App::Ack::Filter;++=head1 NAME++App::Ack::Filter++=head1 DESCRIPTION++An abstract superclass that represents objects that can filter `App::Ack::Resource` objects.++=cut++use strict;+use warnings;++use App::Ack::Filter::Inverse ();+use Carp 1.04 ();++my %filter_types;++=head1 NAME++App::Ack::Filter - Filter objects to filter files++=head1 SYNOPSIS++ # filter implementation+ package MyFilter;++ use strict;+ use warnings;+ use base 'App::Ack::Filter';++ sub filter {+ my ( $self, $resource ) = @_;+ }++ BEGIN {+ App::Ack::Filter->register_filter('mine' => __PACKAGE__);+ }++ 1;++ # users+ App::Ack::Filter->create_filter('mine', @args);+++=head1 DESCRIPTION++App::Ack::Filter implementations are responsible for filtering filenames+to be searched.++=head1 METHODS++=head2 App::Ack:Filter->create_filter($type, @args)++Creates a filter implementation, registered as C<$type>. C<@args>+are provided as additional arguments to the implementation's constructor.++=cut++sub create_filter {+ my ( undef, $type, @args ) = @_;++ if ( my $package = $filter_types{$type} ) {+ return $package->new(@args);+ }+ Carp::croak "Unknown filter type '$type'";+}++=head2 App::Ack:Filter->register_filter($type, $package)++Registers a filter implementation package C<$package> under+the name C<$type>.++=cut++sub register_filter {+ my ( undef, $type, $package ) = @_;++ $filter_types{$type} = $package;++ return;+}++=head2 $filter->filter($resource)++Must be implemented by filter implementations. Returns+true if the filter passes, false otherwise. This method+must B<not> alter the passed-in C<$resource> object.++=head2 $filter->invert()++Returns a filter whose L</filter> method returns the opposite of this filter.++=cut++sub invert {+ my ( $self ) = @_;++ return App::Ack::Filter::Inverse->new( $self );+}++=head2 $filter->is_inverted()++Returns true if this filter is an inverted filter; false otherwise.++=cut++sub is_inverted {+ return 0;+}++=head2 $filter->to_string++Converts the filter to a string. This method is also+called implicitly by stringification.++=cut++sub to_string {+ my ( $self ) = @_;++ return '(unimplemented to_string)';+}++=head2 $filter->inspect++Prints a human-readable debugging string for this filter. Useful for,+you guessed it, debugging.++=cut++sub inspect {+ my ( $self ) = @_;++ return ref($self);+}++1;
FirstLineMatch.pm
@@ -0,0 +1,63 @@+package App::Ack::Filter::FirstLineMatch;++=head1 NAME++App::Ack::Filter::FirstLineMatch++=head1 DESCRIPTION++The class that implements filtering resources by their first line.++=cut+++use strict;+use warnings;+use base '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;
Inverse.pm
@@ -0,0 +1,51 @@+package App::Ack::Filter::Inverse;++=head1 NAME++App::Ack::Filter::Inverse++=head1 DESCRIPTION++The filter class that inverts another filter.++=cut+++use strict;+use warnings;+use base '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;
Is.pm
@@ -0,0 +1,62 @@+package App::Ack::Filter::Is;++=head1 NAME++App::Ack::Filter::Is++=head1 DESCRIPTION++Filters based on exact filename match.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++use File::Spec 3.00 ();+use App::Ack::Filter::IsGroup;++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;
IsGroup.pm
@@ -0,0 +1,71 @@+package App::Ack::Filter::IsGroup;++=head1 NAME++App::Ack::Filter::IsGroup++=head1 DESCRIPTION++The App::Ack::Filter::IsGroup class optimizes multiple+App::Ack::Filter::Is calls into one container.++Let's say you have 100 C<--type-add=is:...> filters.++You could have++ my @filters = map { make_is_filter($_) } 1..100;++and then do++ if ( any { $_->filter($rsrc) } @filters ) { ... }++but that's slow, because of of method lookup overhead, function call+overhead, etc. So ::Is filters know how to organize themselves into an+::IsGroup filter.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++use File::Spec 3.00 ();++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $data = $self->{'data'};+ my $base = $resource->basename;++ return exists $data->{$base};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;
IsPath.pm
@@ -0,0 +1,54 @@+package App::Ack::Filter::IsPath;++=head1 NAME++App::Ack::Filter::IsPath++=head1 DESCRIPTION++Filters based on path.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++use App::Ack::Filter::IsPathGroup;++sub new {+ my ( $class, $filename ) = @_;++ return bless {+ filename => $filename,+ groupname => 'IsPathGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::IsPathGroup->new();+}++sub filter {+ my ( $self, $resource ) = @_;++ return $resource->name eq $self->{'filename'};+}++sub inspect {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return ref($self) . " - $filename";+}++sub to_string {+ my ( $self ) = @_;++ my $filename = $self->{'filename'};++ return $filename;+}++1;
IsPathGroup.pm
@@ -0,0 +1,56 @@+package App::Ack::Filter::IsPathGroup;+++=head1 NAME++App::Ack::Filter::IsPathGroup++=head1 DESCRIPTION++The App::Ack::Filter::IsPathGroup class optimizes multiple ::IsPath+calls into one container. See App::Ack::Filter::IsGroup for details.++=cut+++use strict;+use warnings;+use base 'App::Ack::Filter';++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $data = $self->{'data'};++ return exists $data->{$resource->name};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;
@@ -0,0 +1,199 @@+ack is released under the [Artistic License 2.0][1].++[1]: http://www.perlfoundation.org/artistic_license_2_0++Artistic License 2.0+====================++Copyright (c) 2000-2006, The Perl Foundation.++Everyone is permitted to copy and distribute verbatim copies of this+license document, but changing it is not allowed.+++Preamble+--------++This license establishes the terms under which a given free software+Package may be copied, modified, distributed, and/or redistributed. The+intent is that the Copyright Holder maintains some artistic control+over the development of that Package while still keeping the Package+available as open source and free software.++You are always permitted to make arrangements wholly outside of this+license directly with the Copyright Holder of a given Package. If the+terms of this license do not permit the full use that you propose to+make of the Package, you should contact the Copyright Holder and seek+a different licensing arrangement.+++Definitions+-----------++"Copyright Holder" means the individual(s) or organization(s) named in+the copyright notice for the entire Package.++"Contributor" means any party that has contributed code or other material+to the Package, in accordance with the Copyright Holder's procedures.++"You" and "your" means any person who would like to copy, distribute,+or modify the Package.++"Package" means the collection of files distributed by the Copyright+Holder, and derivatives of that collection and/or of those files. A given+Package may consist of either the Standard Version, or a Modified Version.++"Distribute" means providing a copy of the Package or making it accessible+to anyone else, or in the case of a company or organization, to others+outside of your company or organization.++"Distributor Fee" means any fee that you charge for Distributing this+Package or providing support for this Package to another party. It does+not mean licensing fees.++"Standard Version" refers to the Package if it has not been modified,+or has been modified only in ways explicitly requested by the Copyright+Holder.++"Modified Version" means the Package, if it has been changed, and such+changes were not explicitly requested by the Copyright Holder.++"Original License" means this Artistic License as Distributed with the+Standard Version of the Package, in its current version or as it may be+modified by The Perl Foundation in the future.++"Source" form means the source code, documentation source, and+configuration files for the Package.++"Compiled" form means the compiled bytecode, object code, binary, or any+other form resulting from mechanical transformation or translation of+the Source form.+++Permission for Use and Modification Without Distribution+--------------------------------------------------------++(1) You are permitted to use the Standard Version and create and use+Modified Versions for any purpose without restriction, provided that you+do not Distribute the Modified Version.+++Permissions for Redistribution of the Standard Version+------------------------------------------------------++(2) You may Distribute verbatim copies of the Source form of the Standard+Version of this Package in any medium without restriction, either gratis+or for a Distributor Fee, provided that you duplicate all of the original+copyright notices and associated disclaimers. At your discretion, such+verbatim copies may or may not include a Compiled form of the Package.++(3) You may apply any bug fixes, portability changes, and other+modifications made available from the Copyright Holder. The resulting+Package will still be considered the Standard Version, and as such will+be subject to the Original License. Distribution of Modified Versions+of the Package as Source++(4) You may Distribute your Modified Version as Source (either gratis+or for a Distributor Fee, and with or without a Compiled form of the+Modified Version) provided that you clearly document how it differs+from the Standard Version, including, but not limited to, documenting+any non-standard features, executables, or modules, and provided that+you do at least ONE of the following:++(a) make the Modified Version available to the Copyright Holder of+the Standard Version, under the Original License, so that the Copyright+Holder may include your modifications in the Standard Version.++(b) ensure that installation of your Modified Version does not prevent+the user installing or running the Standard Version. In addition, the+Modified Version must bear a name that is different from the name of+the Standard Version.++(c) allow anyone who receives a copy of the Modified Version to make+the Source form of the Modified Version available to others under (i)+the Original License or (ii) a license that permits the licensee to+freely copy, modify and redistribute the Modified Version using the+same licensing terms that apply to the copy that the licensee received,+and requires that the Source form of the Modified Version, and of any+works derived from it, be made freely available in that license fees+are prohibited but Distributor Fees are allowed.+++Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source+----------------------------------------------------------------------------------------------++(5) You may Distribute Compiled forms of the Standard Version without+the Source, provided that you include complete instructions on how to+get the Source of the Standard Version. Such instructions must be valid+at the time of your distribution. If these instructions, at any time+while you are carrying out such distribution, become invalid, you must+provide new instructions on demand or cease further distribution. If+you provide valid instructions or cease distribution within thirty days+after you become aware that the instructions are invalid, then you do+not forfeit any of your rights under this license.++(6) You may Distribute a Modified Version in Compiled form without the+Source, provided that you comply with Section 4 with respect to the+Source of the Modified Version.+++Aggregating or Linking the Package+----------------------------------++(7) You may aggregate the Package (either the Standard Version or+Modified Version) with other packages and Distribute the resulting+aggregation provided that you do not charge a licensing fee for the+Package. Distributor Fees are permitted, and licensing fees for other+components in the aggregation are permitted. The terms of this license+apply to the use and Distribution of the Standard or Modified Versions+as included in the aggregation.++(8) You are permitted to link Modified and Standard Versions with other+works, to embed the Package in a larger work of your own, or to build+stand-alone binary or bytecode versions of applications that include the+Package, and Distribute the result without restriction, provided the+result does not expose a direct interface to the Package.+++Items That are Not Considered Part of a Modified Version+--------------------------------------------------------++(9) Works (including, but not limited to, modules and scripts) that+merely extend or make use of the Package, do not, by themselves, cause+the Package to be a Modified Version. In addition, such works are not+considered parts of the Package itself, and are not subject to the terms+of this license.+++General Provisions+------------------++(10) Any use, modification, and distribution of the Standard or Modified+Versions is governed by this Artistic License. By using, modifying or+distributing the Package, you accept this license. Do not use, modify,+or distribute the Package, if you do not accept this license.++(11) If your Modified Version has been derived from a Modified Version+made by someone other than you, you are nevertheless required to ensure+that your Modified Version complies with the requirements of this license.++(12) This license does not grant you the right to use any trademark,+service mark, tradename, or logo of the Copyright Holder.++(13) This license includes the non-exclusive, worldwide, free-of-charge+patent license to make, have made, use, offer to sell, sell, import+and otherwise transfer the Package with respect to any patent claims+licensable by the Copyright Holder that are necessarily infringed by the+Package. If you institute patent litigation (including a cross-claim or+counterclaim) against any party alleging that the Package constitutes+direct or contributory patent infringement, then this Artistic License+to you shall terminate on the date that such litigation is filed.++(14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT+HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED+WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A+PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT+PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER+OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR+CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,240 @@+Changes+CONTRIBUTING.md+LICENSE.md+MANIFEST+README.md+Makefile.PL++ack+Ack.pm+Collection.pm+ConfigDefault.pm+ConfigFinder.pm+ConfigLoader.pm+Default.pm+Extension.pm+ExtensionGroup.pm+Filter.pm+FirstLineMatch.pm+Inverse.pm+Is.pm+IsGroup.pm+IsPath.pm+IsPathGroup.pm+Match.pm+MatchGroup.pm+Resource.pm+Resources.pm++record-options+squash+test-pager++t/FilterTest.pm+t/Util.pm++t/00-load.t+t/ack-1.t+t/ack-color.t+t/ack-column.t+t/ack-create-ackrc.t+t/ack-c.t+t/ack-dump.t+t/ack-files-from.t+t/ack-filetypes.t+t/ack-f.t+t/ack-group.t+t/ack-g.t+t/ack-help.t+t/ack-help-types.t+t/ack-h.t+t/ack-ignore-dir.t+t/ack-ignore-file.t+t/ack-interactive.t+t/ack-invalid-ackrc.t+t/ack-i.t+t/ack-known-types.t+t/ack-k.t+t/ack-line.t+t/ack-match.t+t/ack-m.t+t/ack-named-pipes.t+t/ack-n.t+t/ack-o.t+t/ack-output.t+t/ack-pager.t+t/ack-passthru.t+t/ack-print0.t+t/ack-removed-options.t+t/ack-show-types.t+t/ack-s.t+t/ack-type-del.t+t/ack-type.t+t/ack-v.t+t/ack-w.t+t/ack-x.t+t/anchored.t+t/asp-net-ext.t+t/bad-ackrc-opt.t+t/basic.t+t/command-line-files.t+t/config-backwards-compat.t+t/config-finder.t+t/config-loader.t+t/context.t+t/default-filter.t+t/exit-code.t+t/ext-filter.t+t/file-permission.t+t/filetypes.t+t/filter.t+t/firstlinematch-filter.t+t/highlighting.t+t/illegal-regex.t+t/incomplete-last-line.t+t/inverted-file-filter.t+t/is-filter.t+t/issue244.t+t/issue276.t+t/issue491.t+t/issue522.t+t/issue562.t+t/issue571.t+t/longopts.t+t/lua-shebang.t+t/match-filter.t+t/mutex-options.t+t/noackrc.t+t/noenv.t+t/process-substitution.t+t/resource-iterator.t+t/r-lang-ext.t+t/runtests.pl+t/zero.t++t/etc/buttonhook.xml.xxx+t/etc/shebang.empty.xxx+t/etc/shebang.foobar.xxx+t/etc/shebang.php.xxx+t/etc/shebang.pl.xxx+t/etc/shebang.py.xxx+t/etc/shebang.rb.xxx+t/etc/shebang.sh.xxx++t/lib/00-coverage.t+t/lib/Ack.t+t/lib/Collection.t+t/lib/ConfigDefault.t+t/lib/ConfigFinder.t+t/lib/ConfigLoader.t+t/lib/Default.t+t/lib/Extension.t+t/lib/ExtensionGroup.t+t/lib/Filter.t+t/lib/FirstLineMatch.t+t/lib/Inverse.t+t/lib/Is.t+t/lib/IsGroup.t+t/lib/IsPath.t+t/lib/IsPathGroup.t+t/lib/Match.t+t/lib/MatchGroup.t+t/lib/Resource.t+t/lib/Resources.t++t/home/.ackrc+t/swamp/#emacs-workfile.pl#+t/swamp/0+t/swamp/CMakeLists.txt+t/swamp/Makefile+t/swamp/Makefile.PL+t/swamp/MasterPage.master+t/swamp/Rakefile+t/swamp/Sample.ascx+t/swamp/Sample.asmx+t/swamp/blib/ignore.pir+t/swamp/blib/ignore.pm+t/swamp/blib/ignore.pod+t/swamp/c-header.h+t/swamp/c-source.c+t/swamp/crystallography-weenies.f+t/swamp/example.R+t/swamp/file.bar+t/swamp/file.foo+t/swamp/fresh.css+t/swamp/fresh.css.min+t/swamp/fresh.min.css+t/swamp/groceries/CVS/fruit+t/swamp/groceries/CVS/junk+t/swamp/groceries/CVS/meat+t/swamp/groceries/RCS/fruit+t/swamp/groceries/RCS/junk+t/swamp/groceries/RCS/meat+t/swamp/groceries/another_subdir/CVS/fruit+t/swamp/groceries/another_subdir/CVS/junk+t/swamp/groceries/another_subdir/CVS/meat+t/swamp/groceries/another_subdir/RCS/fruit+t/swamp/groceries/another_subdir/RCS/junk+t/swamp/groceries/another_subdir/RCS/meat+t/swamp/groceries/another_subdir/fruit+t/swamp/groceries/another_subdir/junk+t/swamp/groceries/another_subdir/meat+t/swamp/groceries/dir.d/CVS/fruit+t/swamp/groceries/dir.d/CVS/junk+t/swamp/groceries/dir.d/CVS/meat+t/swamp/groceries/dir.d/fruit+t/swamp/groceries/dir.d/junk+t/swamp/groceries/dir.d/meat+t/swamp/groceries/dir.d/RCS/fruit+t/swamp/groceries/dir.d/RCS/junk+t/swamp/groceries/dir.d/RCS/meat+t/swamp/groceries/fruit+t/swamp/groceries/junk+t/swamp/groceries/meat+t/swamp/groceries/subdir/fruit+t/swamp/groceries/subdir/junk+t/swamp/groceries/subdir/meat+t/swamp/html.htm+t/swamp/html.html+t/swamp/incomplete-last-line.txt+t/swamp/javascript.js+t/swamp/lua-shebang-test+t/swamp/minified.js.min+t/swamp/minified.min.js+t/swamp/moose-andy.jpg+t/swamp/not-an-#emacs-workfile#+t/swamp/notaMakefile+t/swamp/notaRakefile+t/swamp/notes.md+t/swamp/options-crlf.pl+t/swamp/options.pl+t/swamp/options.pl.bak+t/swamp/parrot.pir+t/swamp/perl-test.t+t/swamp/perl-without-extension+t/swamp/perl.cgi+t/swamp/perl.handler.pod+t/swamp/perl.pl+t/swamp/perl.pm+t/swamp/perl.pod+t/swamp/perl.tar.gz+t/swamp/perltoot.jpg+t/swamp/pipe-stress-freaks.F+t/swamp/sample.asp+t/swamp/sample.aspx+t/swamp/sample.rake+t/swamp/service.svc+t/swamp/solution8.tar+t/swamp/stuff.cmake+t/swamp/swamp/ignoreme.txt+t/text/4th-of-july.txt+t/text/boy-named-sue.txt+t/text/freedom-of-choice.txt+t/text/me-and-bobbie-mcgee.txt+t/text/number.txt+t/text/numbered-text.txt+t/text/science-of-myth.txt+t/text/shut-up-be-happy.txt++xt/man.t+xt/pod.t
@@ -0,0 +1,175 @@+package main;++require 5.008008;++use strict;+use warnings;+use ExtUtils::MakeMaker;++my $debug_mode = (grep { $_ eq '--debug' } @ARGV) ? '--debug' : '';++my %parms = (+ NAME => 'ack',+ AUTHOR => 'Andy Lester <andy@petdance.com>',+ ABSTRACT => 'A grep-like program for searching source code',+ VERSION_FROM => 'Ack.pm',++ PM => {+ 'Ack.pm' => '$(INST_LIBDIR)/App/Ack.pm',+ 'Resource.pm' => '$(INST_LIBDIR)/App/Ack/Resource.pm',+ 'Resources.pm' => '$(INST_LIBDIR)/App/Ack/Resources.pm',+ 'ConfigDefault.pm' => '$(INST_LIBDIR)/App/Ack/ConfigDefault.pm',+ 'ConfigFinder.pm' => '$(INST_LIBDIR)/App/Ack/ConfigFinder.pm',+ 'ConfigLoader.pm' => '$(INST_LIBDIR)/App/Ack/ConfigLoader.pm',+ 'Filter.pm' => '$(INST_LIBDIR)/App/Ack/Filter.pm',+ 'Extension.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Extension.pm',+ 'FirstLineMatch.pm' => '$(INST_LIBDIR)/App/Ack/Filter/FirstLineMatch.pm',+ 'Is.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Is.pm',+ 'Match.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Match.pm',+ 'Default.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Default.pm',+ 'Inverse.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Inverse.pm',+ 'Collection.pm' => '$(INST_LIBDIR)/App/Ack/Filter/Collection.pm',+ 'IsGroup.pm' => '$(INST_LIBDIR)/App/Ack/Filter/IsGroup.pm',+ 'ExtensionGroup.pm' => '$(INST_LIBDIR)/App/Ack/Filter/ExtensionGroup.pm',+ 'MatchGroup.pm' => '$(INST_LIBDIR)/App/Ack/Filter/MatchGroup.pm',+ 'IsPath.pm' => '$(INST_LIBDIR)/App/Ack/Filter/IsPath.pm',+ 'IsPathGroup.pm' => '$(INST_LIBDIR)/App/Ack/Filter/IsPathGroup.pm',+ },+ EXE_FILES => [ 'ack' ],++ PREREQ_PM => {+ 'Carp' => '1.04',+ 'Cwd' => '3.00',+ 'Errno' => 0,+ 'File::Basename' => '1.00015',+ 'File::Next' => '1.16',+ 'File::Spec' => '3.00',+ 'File::Temp' => '0.19', # For newdir()+ 'Getopt::Long' => '2.38',+ 'Pod::Usage' => '1.26',+ 'Term::ANSIColor' => '1.10',+ 'Test::Harness' => '2.50', # Something reasonably newish+ 'Test::More' => '0.98', # For subtest()+ 'Text::ParseWords' => '3.1',+ ( $^O eq 'MSWin32' ? ('Win32::ShellQuote' => '0.002001') : () ),+ },+ MAN3PODS => {}, # no need for man pages for any of the .pm files+ dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },+ clean => { FILES => 'ack-2* nytprof* stderr.log stdout.log completion.*' },+);++if ( $ExtUtils::MakeMaker::VERSION =~ /^\d\.\d\d$/ and $ExtUtils::MakeMaker::VERSION > 6.30 ) {+ $parms{LICENSE} = 'artistic_2';+}++if ( $ExtUtils::MakeMaker::VERSION ge '6.46' ) {+ $parms{META_MERGE} = {+ resources => {+ homepage => 'https://beyondgrep.com/',+ bugtracker => 'https://github.com/petdance/ack2',+ license => 'http://www.perlfoundation.org/artistic_license_2_0',+ repository => 'git://github.com/petdance/ack2.git',+ MailingList => 'https://groups.google.com/group/ack-users',+ }+ };+}++if ( $ExtUtils::MakeMaker::VERSION ge '6.48' ) {+ $parms{MIN_PERL_VERSION} = 5.008008;+}++WriteMakefile( %parms );++package MY;++# suppress EU::MM test rule+sub MY::test {+ return '';+}++sub MY::postamble {+ my $postamble = sprintf(<<'MAKE_FRAG', $debug_mode);+ACK = ack+ALL_PM = \+ Ack.pm \+ Resource.pm Resources.pm \+ ConfigDefault.pm ConfigFinder.pm ConfigLoader.pm \+ Filter.pm Extension.pm FirstLineMatch.pm Is.pm Match.pm Default.pm Inverse.pm Collection.pm IsGroup.pm ExtensionGroup.pm MatchGroup.pm IsPath.pm IsPathGroup.pm++TEST_VERBOSE=0+TEST_FILES=t/*.t t/lib/*.t+TEST_XT_FILES=xt/*.t++.PHONY: tags critic++tags:+ ctags -f tags --recurse --totals \+ --exclude=blib \+ --exclude=.git \+ --exclude='*~' \+ --exclude=ack-standalone \+ --languages=Perl --langmap=Perl:+.t \++critic:+ perlcritic -1 -q -profile perlcriticrc $(ACK) $(ALL_PM) t/*.t t/lib/*.t xt/*.t++ack-standalone : $(ACK) $(ALL_PM) squash Makefile+ $(PERL) squash $(ACK) $(ALL_PM) File::Next %s > ack-standalone+ $(FIXIN) ack-standalone+ -$(NOECHO) $(CHMOD) $(PERM_RWX) ack-standalone+ $(PERL) -c ack-standalone++bininst : $(ACK)+ $(CP) $(ACK) ~/bin/ack2+ $(CP) ackrc ~/.ack2rc++test: test_classic test_standalone++fulltest: test_classic test_standalone test_xt++test_classic: all+ $(FULLPERLRUN) t/runtests.pl 0 $(TEST_VERBOSE) "$(INST_LIB)" "$(INST_ARCHLIB)" $(TEST_FILES)++test_standalone: all ack-standalone+ $(FULLPERLRUN) t/runtests.pl 1 $(TEST_VERBOSE) "$(INST_LIB)" "$(INST_ARCHLIB)" $(TEST_FILES)++test_xt: all+ $(FULLPERLRUN) t/runtests.pl 0 $(TEST_VERBOSE) "$(INST_LIB)" "$(INST_ARCHLIB)" $(TEST_XT_FILES)++PROF_ARGS = -Mblib blib/script/ack foo ~/parrot++nytprof: all+ $(PERL) -d:NYTProf $(PROF_ARGS) >> /dev/null 2>&1+ nytprofhtml++TIMER_ARGS=foo ~/parrot > /dev/null+time-ack196:+ time $(PERL) ./garage/ack196 --noenv $(TIMER_ARGS)++time-ack202:+ time $(PERL) ./garage/ack202 --noenv $(TIMER_ARGS)++time-ack20301:+ time $(PERL) ./garage/ack20301 --noenv $(TIMER_ARGS)++time-ack20302:+ time $(PERL) ./garage/ack20302 --noenv $(TIMER_ARGS)++time-head: ack-standalone+ time $(PERL) ./ack-standalone --noenv $(TIMER_ARGS)++timings: ack-standalone+ ./dev/timings.pl++completion.bash: pm_to_blib+ ./dev/generate-completion-scripts.pl completion.bash++completion.zsh: pm_to_blib+ ./dev/generate-completion-scripts.pl completion.zsh++MAKE_FRAG++ return $postamble;+}++1;
Match.pm
@@ -0,0 +1,66 @@+package App::Ack::Filter::Match;++use strict;+use warnings;+use base 'App::Ack::Filter';++use File::Spec 3.00;+use App::Ack::Filter::MatchGroup ();++=head1 NAME++App::Ack::Filter::Match++=head1 DESCRIPTION++Implements filtering resources by their filename (regular expression).++=cut++sub new {+ my ( $class, $re ) = @_;++ $re =~ s{^/|/$}{}g; # XXX validate?+ $re = qr/$re/i;++ return bless {+ regex => $re,+ groupname => 'MatchGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::MatchGroup->new;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{'regex'};++ return $resource->basename =~ /$re/;+}++sub inspect {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ print ref($self) . " - $re";++ return;+}++sub to_string {+ my ( $self ) = @_;++ my $re = $self->{'regex'};++ return "filename matches $re";+}++BEGIN {+ App::Ack::Filter->register_filter(match => __PACKAGE__);+}++1;
MatchGroup.pm
@@ -0,0 +1,58 @@+package App::Ack::Filter::MatchGroup;++=head1 NAME++App::Ack::Filter::MatchGroup++=head1 DESCRIPTION++The App::Ack::Filter::MatchGroup class optimizes multiple ::Match calls+into one container. See App::Ack::Filter::IsGroup for details.++=cut++use strict;+use warnings;+use base 'App::Ack::Filter';++sub new {+ my ( $class ) = @_;++ return bless {+ matches => [],+ big_re => undef,+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ push @{ $self->{matches} }, $filter->{regex};++ my $re = join('|', map { "(?:$_)" } @{ $self->{matches} });+ $self->{big_re} = qr/$re/;++ return;+}++sub filter {+ my ( $self, $resource ) = @_;++ my $re = $self->{big_re};++ return $resource->basename =~ /$re/;+}++sub inspect {+ my ( $self ) = @_;++ # XXX Needs an explicit return.+}++sub to_string {+ my ( $self ) = @_;++ # XXX Needs an explicit return.+}++1;
@@ -1,2 +1,41 @@-# ack3-ack 3: Currently in the high-level thinking phase+# ack 2++ack is a code-searching tool, similar to grep but optimized for+programmers searching large trees of source code. It runs in pure+Perl, is highly portable, and runs on any platform that runs Perl.++ack is written and maintained by Andy Lester (andy@petdance.com).++* Project home page: https://beyondgrep.com/+* Code home page: https://github.com/petdance/ack2+* Issue tracker: https://github.com/petdance/ack2/issues+* Mailing list for announcements: https://groups.google.com/d/forum/ack-announcements+* Mailing list for users: https://groups.google.com/d/forum/ack-users+* Mailing list for developers: https://groups.google.com/d/forum/ack-dev++# Building++ack requires Perl 5.8.8 or higher. Perl 5.8.8 was released January 2006.++ # Required+ perl Makefile.PL+ make+ make test+ sudo make install # for a system-wide installation (recommended)+ # - or -+ make ack-standalone+ cp ack-standalone ~/bin/ack2 # for a personal installation++Build status: [](https://travis-ci.org/petdance/ack2)++[CPAN Testers](http://cpantesters.org/distro/A/ack.html)++# Development++[Developer's Guide](DEVELOPERS.md)++[Design Guide](DESIGN.md)++# Community++TODO
Resource.pm
@@ -0,0 +1,231 @@+package App::Ack::Resource;++use App::Ack;++use warnings;+use strict;+use overload+ '""' => 'name';++=head1 NAME++App::Ack::Resource++=head1 DESCRIPTION++Abstracts a file from the filesystem.++=head1 METHODS++=head2 new( $filename )++Opens the file specified by I<$filename> and returns a filehandle and+a flag that says whether it could be binary.++If there's a failure, it throws a warning and returns an empty list.++=cut++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => 0,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ $self->{opened} = 1;+ }++ return $self;+}+++=head2 $res->name()++Returns the name of the resource.++=cut++sub name {+ return $_[0]->{filename};+}+++=head2 $res->basename()++Returns the basename (the last component the path)+of the resource.++=cut++sub basename {+ my ( $self ) = @_;++ # XXX Definedness? Pre-populate the slot with an undef?+ unless ( exists $self->{basename} ) {+ $self->{basename} = (File::Spec->splitpath($self->name))[2];+ }++ return $self->{basename};+}+++=head2 $res->open()++Opens a filehandle for reading this resource and returns it, or returns+undef if the operation fails (the error is in C<$!>). Instead of calling+C<close $fh>, C<$res-E<gt>close> should be called.++=cut++sub open {+ my ( $self ) = @_;++ if ( !$self->{opened} ) {+ if ( open $self->{fh}, '<', $self->{filename} ) {+ $self->{opened} = 1;+ }+ else {+ $self->{fh} = undef;+ }+ }++ return $self->{fh};+}+++=head2 $res->needs_line_scan( \%opts )++Tells if the file needs a line-by-line scan. This is a big+optimization because if you can tell from the outset that the pattern+is not found in the resource at all, then there's no need to do the+line-by-line iteration.++Slurp up an entire file up to 100K, see if there are any matches+in it, and if so, let us know so we can iterate over it directly.+If it's bigger than 100K or the match is inverted, we have to do+the line-by-line, too.++=cut++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ return $buffer =~ /$opt->{regex}/m;+}+++=head2 $res->reset()++Resets the resource back to the beginning. This is only called if+C<needs_line_scan()> is true, but not always if C<needs_line_scan()>+is true.++=cut++sub reset {+ my $self = shift;++ # Return if we haven't opened the file yet.+ if ( !defined($self->{fh}) ) {+ return;+ }++ if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++=head2 $res->close()++Close the file.++=cut++sub close {+ my $self = shift;++ # Return if we haven't opened the file yet.+ if ( !defined($self->{fh}) ) {+ return;+ }++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ $self->{opened} = 0;++ return;+}+++=head2 $res->clone()++Clones this resource.++=cut++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}+++=head2 $res->firstliney++Returns the first line of a file (or first 250 characters, whichever+comes first).++=cut++sub firstliney {+ my ( $self ) = @_;++ my $fh = $self->open();++ if ( !exists $self->{firstliney} ) {+ my $buffer = '';+ my $rc = sysread( $fh, $buffer, 250 );+ unless($rc) { # XXX handle this better?+ $buffer = '';+ }+ $buffer =~ s/[\r\n].*//s;+ $self->{firstliney} = $buffer;+ $self->reset;+ }++ $self->close;++ return $self->{firstliney};+}++1;
Resources.pm
@@ -0,0 +1,131 @@+package App::Ack::Resources;++=head1 NAME++App::Ack::Resources++=head1 SYNOPSIS++A factory object for creating a stream of L<App::Ack::Resource> objects.++=cut++use App::Ack;+use App::Ack::Resource;++use File::Next 1.16;+use Errno qw(EACCES);++use warnings;+use strict;++sub _generate_error_handler {+ my $opt = shift;++ if ( $opt->{dont_report_bad_filenames} ) {+ return sub {+ my $msg = shift;+ if ( $! == EACCES ) {+ return;+ }+ App::Ack::warn( $msg );+ };+ }+ else {+ return sub {+ my $msg = shift;+ App::Ack::warn( $msg );+ };+ }+}++=head1 METHODS++=head2 from_argv( \%opt, \@starting_points )++Return an iterator that does the file finding for us.++=cut++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => _generate_error_handler($opt),+ warning_handler => sub {},+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}++=head2 from_file( \%opt, $filename )++Return an iterator that reads the list of files to search from a+given file. If I<$filename> is '-', then it reads from STDIN.++=cut++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => _generate_error_handler($opt),+ warning_handler => _generate_error_handler($opt),+ sort_files => $opt->{sort_files},+ }, $file ) or return undef;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource->new( $file );+}++1;
@@ -0,0 +1,2386 @@+#!/usr/bin/perl++use strict;+use warnings;+our $VERSION = '2.18'; # Check https://beyondgrep.com/ for updates++use 5.008008;+use Getopt::Long 2.38 ();+use Carp 1.04 ();++use File::Spec ();+use File::Next ();++use App::Ack ();+use App::Ack::ConfigLoader ();+use App::Ack::Resources;+use App::Ack::Resource ();++# XXX Don't make this so brute force+# See also: https://github.com/petdance/ack2/issues/89+use App::Ack::Filter ();+use App::Ack::Filter::Default;+use App::Ack::Filter::Extension;+use App::Ack::Filter::FirstLineMatch;+use App::Ack::Filter::Inverse;+use App::Ack::Filter::Is;+use App::Ack::Filter::IsPath;+use App::Ack::Filter::Match;+use App::Ack::Filter::Collection;++our $opt_after_context;+our $opt_before_context;+our $opt_output;+our $opt_print0;+our $opt_color;+our $opt_heading;+our $opt_show_filename;+our $opt_regex;+our $opt_break;+our $opt_count;+our $opt_v;+our $opt_m;+our $opt_g;+our $opt_f;+our $opt_lines;+our $opt_L;+our $opt_l;+our $opt_passthru;+our $opt_column;++# Flag if we need any context tracking.+our $is_tracking_context;++# These are all our globals.++MAIN: {+ $App::Ack::orig_program_name = $0;+ $0 = join(' ', 'ack', $0);+ if ( $App::Ack::VERSION ne $main::VERSION ) {+ App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );+ }++ # Do preliminary arg checking;+ my $env_is_usable = 1;+ for my $arg ( @ARGV ) {+ last if ( $arg eq '--' );++ # Get the --thpppt, --bar, --cathy checking out of the way.+ $arg =~ /^--th[pt]+t+$/ and App::Ack::thpppt($arg);+ $arg eq '--bar' and App::Ack::ackbar();+ $arg eq '--cathy' and App::Ack::cathy();++ # See if we want to ignore the environment. (Don't tell Al Gore.)+ $arg eq '--env' and $env_is_usable = 1;+ $arg eq '--noenv' and $env_is_usable = 0;+ }++ if ( !$env_is_usable ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ load_colors();++ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');+ Getopt::Long::Configure('pass_through', 'no_auto_abbrev');+ Getopt::Long::GetOptions(+ 'help' => sub { App::Ack::show_help(); exit; },+ 'version' => sub { App::Ack::print_version_statement(); exit; },+ 'man' => sub { App::Ack::show_man(); exit; },+ );+ Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');++ if ( !@ARGV ) {+ App::Ack::show_help();+ exit 1;+ }++ main();+}++sub _compile_descend_filter {+ my ( $opt ) = @_;++ my $idirs = 0;+ my $dont_ignore_dirs = 0;++ for my $filter (@{$opt->{idirs} || []}) {+ if ($filter->is_inverted()) {+ $dont_ignore_dirs++;+ }+ else {+ $idirs++;+ }+ }++ # If we have one or more --noignore-dir directives, we can't ignore+ # entire subdirectory hierarchies, so we return an "accept all"+ # filter and scrutinize the files more in _compile_file_filter.+ return if $dont_ignore_dirs;+ return unless $idirs;++ $idirs = $opt->{idirs};++ return sub {+ my $resource = App::Ack::Resource->new($File::Next::dir);+ return !grep { $_->filter($resource) } @{$idirs};+ };+}++sub _compile_file_filter {+ my ( $opt, $start ) = @_;++ my $ifiles_filters = $opt->{ifiles};++ my $filters = $opt->{'filters'} || [];+ my $direct_filters = App::Ack::Filter::Collection->new();+ my $inverse_filters = App::Ack::Filter::Collection->new();++ foreach my $filter (@{$filters}) {+ if ($filter->is_inverted()) {+ # We want to check if files match the uninverted filters+ $inverse_filters->add($filter->invert());+ }+ else {+ $direct_filters->add($filter);+ }+ }++ my %is_member_of_starting_set = map { (get_file_id($_) => 1) } @{$start};++ my @ignore_dir_filter = @{$opt->{idirs} || []};+ my @is_inverted = map { $_->is_inverted() } @ignore_dir_filter;+ # This depends on InverseFilter->invert returning the original filter (for optimization).+ @ignore_dir_filter = map { $_->is_inverted() ? $_->invert() : $_ } @ignore_dir_filter;+ my $dont_ignore_dir_filter = grep { $_ } @is_inverted;+ my $previous_dir = '';+ my $previous_dir_ignore_result;++ return sub {+ if ( $opt_g ) {+ if ( $File::Next::name =~ /$opt_regex/ && $opt_v ) {+ return 0;+ }+ if ( $File::Next::name !~ /$opt_regex/ && !$opt_v ) {+ return 0;+ }+ }+ # ack always selects files that are specified on the command+ # line, regardless of filetype. If you want to ack a JPEG,+ # and say "ack foo whatever.jpg" it will do it for you.+ return 1 if $is_member_of_starting_set{ get_file_id($File::Next::name) };++ if ( $dont_ignore_dir_filter ) {+ if ( $previous_dir eq $File::Next::dir ) {+ if ( $previous_dir_ignore_result ) {+ return 0;+ }+ }+ else {+ my @dirs = File::Spec->splitdir($File::Next::dir);++ my $is_ignoring = 0;++ for ( my $i = 0; $i < @dirs; $i++) {+ my $dir_rsrc = App::Ack::Resource->new(File::Spec->catfile(@dirs[0 .. $i]));++ my $j = 0;+ for my $filter (@ignore_dir_filter) {+ if ( $filter->filter($dir_rsrc) ) {+ $is_ignoring = !$is_inverted[$j];+ }+ $j++;+ }+ }++ $previous_dir = $File::Next::dir;+ $previous_dir_ignore_result = $is_ignoring;++ if ( $is_ignoring ) {+ return 0;+ }+ }+ }++ # Ignore named pipes found in directory searching. Named+ # pipes created by subprocesses get specified on the command+ # line, so the rule of "always select whatever is on the+ # command line" wins.+ return 0 if -p $File::Next::name;++ # We can't handle unreadable filenames; report them.+ if ( not -r _ ) {+ use filetest 'access';++ if ( not -R $File::Next::name ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "${File::Next::name}: cannot open file for reading" );+ }+ return 0;+ }+ }++ my $resource = App::Ack::Resource->new($File::Next::name);++ if ( $ifiles_filters && $ifiles_filters->filter($resource) ) {+ return 0;+ }++ my $match_found = $direct_filters->filter($resource);++ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && $inverse_filters->filter( $resource ) ) {+ $match_found = 0;+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++# Set default colors, load Term::ANSIColor+sub load_colors {+ eval 'use Term::ANSIColor 1.10 ()';+ eval 'use Win32::Console::ANSI' if $App::Ack::is_windows;++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}++sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %App::Ack::mappings) {+ my $filters = $App::Ack::mappings{$k};++ foreach my $filter (@{$filters}) {+ # Clone the resource.+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ # http://search.cpan.org/dist/Perl-Critic/lib/Perl/Critic/Policy/Subroutines/ProhibitReturnSort.pm+ @matches = sort @matches;+ return @matches;+}++# Returns a (fairly) unique identifier for a file.+# Use this function to compare two files to see if they're+# equal (ie. the same file, but with a different path/links/etc).+sub get_file_id {+ my ( $filename ) = @_;++ if ( $App::Ack::is_windows ) {+ return File::Next::reslash( $filename );+ }+ else {+ # XXX Is this the best method? It always hits the FS.+ if( my ( $dev, $inode ) = (stat($filename))[0, 1] ) {+ return join(':', $dev, $inode);+ }+ else {+ # XXX This could be better.+ return $filename;+ }+ }+}++# Returns a regex object based on a string and command-line options.+# Dies when the regex $str is undefined (i.e. not given on command line).++sub build_regex {+ my $str = shift;+ my $opt = shift;++ defined $str or App::Ack::die( 'No regular expression found.' );++ $str = quotemeta( $str ) if $opt->{Q};+ if ( $opt->{w} ) {+ my $pristine_str = $str;++ $str = "(?:$str)";+ $str = "\\b$str" if $pristine_str =~ /^\w/;+ $str = "$str\\b" if $pristine_str =~ /\w$/;+ }++ my $regex_is_lc = $str eq lc $str;+ if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {+ $str = "(?i)$str";+ }++ my $re = eval { qr/$str/m };+ if ( !$re ) {+ die "Invalid regex '$str':\n $@";+ }++ return $re;++}++my $match_column_number;++{++# Number of context lines+my $n_before_ctx_lines;+my $n_after_ctx_lines;++# Array to keep track of lines that might be required for a "before" context+my @before_context_buf;+# Position to insert next line in @before_context_buf+my $before_context_pos;++# Number of "after" context lines still pending+my $after_context_pending;++# Number of latest line that got printed+my $printed_line_no;++my $is_iterating;++my $is_first_match;+my $has_printed_something;++BEGIN {+ $has_printed_something = 0;+}++# Set up context tracking variables.+sub set_up_line_context {+ $n_before_ctx_lines = $opt_output ? 0 : ($opt_before_context || 0);+ $n_after_ctx_lines = $opt_output ? 0 : ($opt_after_context || 0);++ @before_context_buf = (undef) x $n_before_ctx_lines;+ $before_context_pos = 0;++ $is_tracking_context = $n_before_ctx_lines || $n_after_ctx_lines;++ $is_first_match = 1;++ return;+}++# Adjust context tracking variables when entering a new file.+sub set_up_line_context_for_file {+ $printed_line_no = 0;+ $after_context_pending = 0;+ if ( $opt_heading && !$opt_lines ) {+ $is_first_match = 1;+ }++ return;+}++=begin Developers++This subroutine jumps through a number of optimization hoops to+try to be fast in the more common use cases of ack. For one thing,+in non-context tracking searches (not using -A, -B, or -C),+conditions that normally would be checked inside the loop happen+outside, resulting in three nearly identical loops for -v, --passthru,+and normal searching. Any changes that happen to one should propagate+to the others if they make sense. The non-context branches also inline+does_match for performance reasons; any relevant changes that happen here+must also happen there.++=end Developers++=cut++sub print_matches_in_resource {+ my ( $resource ) = @_;++ my $max_count = $opt_m || -1;+ my $nmatches = 0;+ my $filename = $resource->name;+ my $ors = $opt_print0 ? "\0" : "\n";++ my $has_printed_for_this_resource = 0;++ $is_iterating = 1;++ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$filename: $!" );+ }+ return 0;+ }++ my $display_filename = $filename;+ if ( $opt_show_filename && $opt_heading && $opt_color ) {+ $display_filename = Term::ANSIColor::colored($display_filename, $ENV{ACK_COLOR_FILENAME});+ }++ # Check for context before the main loop, so we don't pay for it if we don't need it.+ if ( $is_tracking_context ) {+ $after_context_pending = 0;+ while ( <$fh> ) {+ if ( does_match( $_ ) && $max_count ) {+ if ( !$has_printed_for_this_resource ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::print_filename( $display_filename, $ors );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ elsif ( $opt_passthru ) {+ chomp; # XXX Proper newline handling?+ # XXX Inline this call?+ if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_resource = 1;+ }+ else {+ chomp; # XXX Proper newline handling?+ print_line_if_context( $filename, $_, $., '-' );+ }++ last if ($max_count == 0) && ($after_context_pending == 0);+ }+ }+ else {+ if ( $opt_passthru ) {+ local $_;++ while ( <$fh> ) {+ $match_column_number = undef;+ if ( $opt_v ? !/$opt_regex/o : /$opt_regex/o ) {+ if ( !$opt_v ) {+ $match_column_number = $-[0] + 1;+ }+ if ( !$has_printed_for_this_resource ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::print_filename( $display_filename, $ors );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ else {+ chomp; # XXX proper newline handling?+ if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_resource = 1;+ }+ last unless $max_count != 0;+ }+ }+ elsif ( $opt_v ) {+ local $_;++ $match_column_number = undef;+ while ( <$fh> ) {+ if ( !/$opt_regex/o ) {+ if ( !$has_printed_for_this_resource ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::print_filename( $display_filename, $ors );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ last unless $max_count != 0;+ }+ }+ else {+ local $_;++ while ( <$fh> ) {+ $match_column_number = undef;+ if ( /$opt_regex/o ) {+ $match_column_number = $-[0] + 1;+ if ( !$has_printed_for_this_resource ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::print_filename( $display_filename, $ors );+ }+ }+ s/[\r\n]+$//g;+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ last unless $max_count != 0;+ }+ }++ }++ $is_iterating = 0;++ return $nmatches;+}++sub print_line_with_options {+ my ( $filename, $line, $line_no, $separator ) = @_;++ $has_printed_something = 1;+ $printed_line_no = $line_no;++ my $ors = $opt_print0 ? "\0" : "\n";++ my @line_parts;++ if( $opt_color ) {+ $filename = Term::ANSIColor::colored($filename,+ $ENV{ACK_COLOR_FILENAME});+ $line_no = Term::ANSIColor::colored($line_no,+ $ENV{ACK_COLOR_LINENO});+ }++ if($opt_show_filename) {+ if( $opt_heading ) {+ push @line_parts, $line_no;+ }+ else {+ push @line_parts, $filename, $line_no;+ }++ if( $opt_column ) {+ push @line_parts, get_match_column();+ }+ }+ if( $opt_output ) {+ while ( $line =~ /$opt_regex/og ) {+ # XXX We need to stop using eval() for --output. See https://github.com/petdance/ack2/issues/421+ my $output = eval $opt_output;+ App::Ack::print( join( $separator, @line_parts, $output ), $ors );+ }+ }+ else {+ if ( $opt_color ) {+ # This match is redundant, but we need to perfom it in order to get if capture groups are set.+ $line =~ /$opt_regex/o;++ if ( @+ > 1 ) { # If we have captures...+ while ( $line =~ /$opt_regex/og ) {+ my $offset = 0; # Additional offset for when we add stuff.+ my $previous_match_end = 0;++ last if $-[0] == $+[0];++ for ( my $i = 1; $i < @+; $i++ ) {+ my ( $match_start, $match_end ) = ( $-[$i], $+[$i] );++ next unless defined($match_start);+ next if $match_start < $previous_match_end;++ my $substring = substr( $line,+ $offset + $match_start, $match_end - $match_start );+ my $substitution = Term::ANSIColor::colored( $substring,+ $ENV{ACK_COLOR_MATCH} );++ substr( $line, $offset + $match_start,+ $match_end - $match_start, $substitution );++ $previous_match_end = $match_end; # Offsets do not need to be applied.+ $offset += length( $substitution ) - length( $substring );+ }++ pos($line) = $+[0] + $offset;+ }+ }+ else {+ my $matched = 0; # If matched, need to escape afterwards.++ while ( $line =~ /$opt_regex/og ) {++ $matched = 1;+ my ( $match_start, $match_end ) = ($-[0], $+[0]);+ next unless defined($match_start);+ last if $match_start == $match_end;++ my $substring = substr( $line, $match_start,+ $match_end - $match_start );+ my $substitution = Term::ANSIColor::colored( $substring,+ $ENV{ACK_COLOR_MATCH} );++ substr( $line, $match_start, $match_end - $match_start,+ $substitution );++ pos($line) = $match_end ++ (length( $substitution ) - length( $substring ));+ }+ # XXX Why do we do this?+ $line .= "\033[0m\033[K" if $matched;+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++sub iterate {+ my ( $resource, $cb ) = @_;++ $is_iterating = 1;++ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ return;+ }++ # Check for context before the main loop, so we don't pay for it if we don't need it.+ if ( $is_tracking_context ) {+ $after_context_pending = 0;++ while ( <$fh> ) {+ last unless $cb->();+ }+ }+ else {+ local $_;++ while ( <$fh> ) {+ last unless $cb->();+ }+ }++ $is_iterating = 0;++ return;+}++sub print_line_with_context {+ my ( $filename, $matching_line, $line_no ) = @_;++ my $ors = $opt_print0 ? "\0" : "\n";+ my $is_tracking_context = $opt_after_context || $opt_before_context;++ $matching_line =~ s/[\r\n]+$//g;++ # Check if we need to print context lines first.+ if ( $is_tracking_context ) {+ my $before_unprinted = $line_no - $printed_line_no - 1;+ if ( !$is_first_match && ( !$printed_line_no || $before_unprinted > $n_before_ctx_lines ) ) {+ App::Ack::print('--', $ors);+ }++ # We want at most $n_before_ctx_lines of context.+ if ( $before_unprinted > $n_before_ctx_lines ) {+ $before_unprinted = $n_before_ctx_lines;+ }++ while ( $before_unprinted > 0 ) {+ my $line = $before_context_buf[($before_context_pos - $before_unprinted + $n_before_ctx_lines) % $n_before_ctx_lines];++ chomp $line;++ # Disable $opt->{column} since there are no matches in the context lines.+ local $opt_column = 0;++ print_line_with_options( $filename, $line, $line_no-$before_unprinted, '-' );+ $before_unprinted--;+ }+ }++ print_line_with_options( $filename, $matching_line, $line_no, ':' );++ # We want to get the next $n_after_ctx_lines printed.+ $after_context_pending = $n_after_ctx_lines;++ $is_first_match = 0;++ return;+}++# Print the line only if it's part of a context we need to display.+sub print_line_if_context {+ my ( $filename, $line, $line_no, $separator ) = @_;++ if ( $after_context_pending ) {+ # Disable $opt_column since there are no matches in the context lines.+ local $opt_column = 0;+ print_line_with_options( $filename, $line, $line_no, $separator );+ --$after_context_pending;+ }+ elsif ( $n_before_ctx_lines ) {+ # Save line for "before" context.+ $before_context_buf[$before_context_pos] = $_;+ $before_context_pos = ($before_context_pos+1) % $n_before_ctx_lines;+ }++ return;+}++}++# does_match() MUST have an $opt_regex set.++=begin Developers++This subroutine is inlined a few places in print_matches_in_resource+for performance reasons, so any changes here must be copied there as+well.++=end Developers++=cut++sub does_match {+ my ( $line ) = @_;++ $match_column_number = undef;++ if ( $opt_v ) {+ return ( $line !~ /$opt_regex/o );+ }+ else {+ if ( $line =~ /$opt_regex/o ) {+ # @- = @LAST_MATCH_START+ # @+ = @LAST_MATCH_END+ $match_column_number = $-[0] + 1;+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_match_column {+ return $match_column_number;+}++sub resource_has_match {+ my ( $resource ) = @_;++ my $has_match = 0;+ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ if (/$opt_regex/o xor $opt_v) {+ $has_match = 1;+ last;+ }+ }+ close $fh;+ }++ return $has_match;+}++sub count_matches_in_resource {+ my ( $resource ) = @_;++ my $nmatches = 0;+ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $resource->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ ++$nmatches if (/$opt_regex/o xor $opt_v);+ }+ close $fh;+ }++ return $nmatches;+}++sub main {+ my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();++ my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );++ $opt_after_context = $opt->{after_context};+ $opt_before_context = $opt->{before_context};+ $opt_output = $opt->{output};+ $opt_print0 = $opt->{print0};+ $opt_color = $opt->{color};+ $opt_heading = $opt->{heading};+ $opt_show_filename = $opt->{show_filename};+ $opt_regex = $opt->{regex};+ $opt_break = $opt->{break};+ $opt_count = $opt->{count};+ $opt_v = $opt->{v};+ $opt_m = $opt->{m};+ $opt_g = $opt->{g};+ $opt_f = $opt->{f};+ $opt_lines = $opt->{lines};+ $opt_L = $opt->{L};+ $opt_l = $opt->{l};+ $opt_passthru = $opt->{passthru};+ $opt_column = $opt->{column};++ $App::Ack::report_bad_filenames = !$opt->{dont_report_bad_filenames};++ if ( $opt->{flush} ) {+ $| = 1;+ }++ if ( !defined($opt_color) && !$opt_g ) {+ my $windows_color = 1;+ if ( $App::Ack::is_windows ) {+ $windows_color = eval { require Win32::Console::ANSI; };+ }+ $opt_color = !App::Ack::output_to_pipe() && $windows_color;+ }+ if ( not defined $opt_heading and not defined $opt_break ) {+ $opt_heading = $opt_break = $opt->{break} = !App::Ack::output_to_pipe();+ }++ if ( defined($opt->{H}) || defined($opt->{h}) ) {+ $opt_show_filename = $opt->{show_filename} = $opt->{H} && !$opt->{h};+ }++ if ( my $output = $opt_output ) {+ $output =~ s{\\}{\\\\}g;+ $output =~ s{"}{\\"}g;+ $opt_output = qq{"$output"};+ }++ my $resources;+ if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x+ $resources = App::Ack::Resources->from_stdin( $opt );+ $opt_regex = shift @ARGV if not defined $opt_regex;+ $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );+ }+ else {+ if ( $opt_f || $opt_lines ) {+ if ( $opt_regex ) {+ App::Ack::warn( "regex ($opt_regex) specified with -f or --lines" );+ App::Ack::exit_from_ack( 0 ); # XXX the 0 is misleading+ }+ }+ else {+ $opt_regex = shift @ARGV if not defined $opt_regex;+ $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );+ }+ if ( $opt_regex && $opt_regex =~ /\n/ ) {+ App::Ack::exit_from_ack( 0 );+ }+ my @start;+ if ( not defined $opt->{files_from} ) {+ @start = @ARGV;+ }+ if ( !exists($opt->{show_filename}) ) {+ unless(@start == 1 && !(-d $start[0])) {+ $opt_show_filename = $opt->{show_filename} = 1;+ }+ }++ if ( defined $opt->{files_from} ) {+ $resources = App::Ack::Resources->from_file( $opt, $opt->{files_from} );+ exit 1 unless $resources;+ }+ else {+ @start = ('.') unless @start;+ foreach my $target (@start) {+ if ( !-e $target && $App::Ack::report_bad_filenames) {+ App::Ack::warn( "$target: No such file or directory" );+ }+ }++ $opt->{file_filter} = _compile_file_filter($opt, \@start);+ $opt->{descend_filter} = _compile_descend_filter($opt);++ $resources = App::Ack::Resources->from_argv( $opt, \@start );+ }+ }+ App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};++ my $ors = $opt_print0 ? "\0" : "\n";+ my $only_first = $opt->{1};++ my $nmatches = 0;+ my $total_count = 0;++ set_up_line_context();++RESOURCES:+ while ( my $resource = $resources->next ) {+ if ($is_tracking_context) {+ set_up_line_context_for_file();+ }++ if ( $opt_f ) {+ if ( $opt->{show_types} ) {+ show_types( $resource, $ors );+ }+ else {+ App::Ack::print( $resource->name, $ors );+ }+ ++$nmatches;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ elsif ( $opt_g ) {+ if ( $opt->{show_types} ) {+ show_types( $resource, $ors );+ }+ else {+ local $opt_show_filename = 0; # XXX Why is this local?++ print_line_with_options( '', $resource->name, 0, $ors );+ }+ ++$nmatches;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ elsif ( $opt_lines ) {+ my %line_numbers;+ foreach my $line ( @{ $opt_lines } ) {+ my @lines = split /,/, $line;+ @lines = map {+ /^(\d+)-(\d+)$/+ ? ( $1 .. $2 )+ : $_+ } @lines;+ @line_numbers{@lines} = (1) x @lines;+ }++ my $filename = $resource->name;++ local $opt_color = 0;++ iterate( $resource, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ print_line_with_context( $filename, $_, $. );+ }+ elsif ( $opt_passthru ) {+ print_line_with_options( $filename, $_, $., ':' );+ }+ elsif ( $is_tracking_context ) {+ print_line_if_context( $filename, $_, $., '-' );+ }+ return 1;+ });+ }+ elsif ( $opt_count ) {+ my $matches_for_this_file = count_matches_in_resource( $resource );++ if ( not $opt_show_filename ) {+ $total_count += $matches_for_this_file;+ next RESOURCES;+ }++ if ( !$opt_l || $matches_for_this_file > 0) {+ if ( $opt_show_filename ) {+ App::Ack::print( $resource->name, ':', $matches_for_this_file, $ors );+ }+ else {+ App::Ack::print( $matches_for_this_file, $ors );+ }+ }+ }+ elsif ( $opt_l || $opt_L ) {+ my $is_match = resource_has_match( $resource );++ if ( $opt_L ? !$is_match : $is_match ) {+ App::Ack::print( $resource->name, $ors );+ ++$nmatches;++ last RESOURCES if $only_first;+ last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;+ }+ }+ else {+ $nmatches += print_matches_in_resource( $resource, $opt );+ if ( $nmatches && $only_first ) {+ last RESOURCES;+ }+ }+ }++ if ( $opt_count && !$opt_show_filename ) {+ App::Ack::print( $total_count, "\n" );+ }++ close $App::Ack::fh;+ App::Ack::exit_from_ack( $nmatches );+}++=pod++=encoding UTF-8++=head1 NAME++ack - grep-like text finder++=head1 SYNOPSIS++ ack [options] PATTERN [FILE...]+ ack -f [options] [DIRECTORY...]++=head1 DESCRIPTION++ack is designed as an alternative to F<grep> for programmers.++ack searches the named input FILEs or DIRECTORYs for lines containing a+match to the given PATTERN. By default, ack prints the matching lines.+If no FILE or DIRECTORY is given, the current directory will be searched.++PATTERN is a Perl regular expression. Perl regular expressions+are commonly found in other programming languages, but for the particulars+of their behavior, please consult+L<http://perldoc.perl.org/perlreref.html|perlreref>. If you don't know+how to use regular expression but are interested in learning, you may+consult L<http://perldoc.perl.org/perlretut.html|perlretut>. If you do not+need or want ack to use regular expressions, please see the+C<-Q>/C<--literal> option.++Ack can also list files that would be searched, without actually+searching them, to let you take advantage of ack's file-type filtering+capabilities.++=head1 FILE SELECTION++If files are not specified for searching, either on the command+line or piped in with the C<-x> option, I<ack> delves into+subdirectories selecting files for searching.++I<ack> is intelligent about the files it searches. It knows about+certain file types, based on both the extension on the file and,+in some cases, the contents of the file. These selections can be+made with the B<--type> option.++With no file selection, I<ack> searches through regular files that+are not explicitly excluded by B<--ignore-dir> and B<--ignore-file>+options, either present in F<ackrc> files or on the command line.++The default options for I<ack> ignore certain files and directories. These+include:++=over 4++=item * Backup files: Files matching F<#*#> or ending with F<~>.++=item * Coredumps: Files matching F<core.\d+>++=item * Version control directories like F<.svn> and F<.git>.++=back++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<--ackrc>++Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.+You can specify zero lines of context to override another context+specified in an ackrc.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> suppresses+the color. This is on by default unless the output is redirected.++On Windows, this option is off by default unless the+L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>+environment variable is used.++=item B<--color-filename=I<color>>++Sets the color to be used for filenames.++=item B<--color-match=I<color>>++Sets the color to be used for matches.++=item B<--color-lineno=I<color>>++Sets the color to be used for line numbers.++=item B<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are separated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were receiving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print searchable files where the relative path + filename matches+I<PATTERN>.++Note that++ ack -g foo++is exactly the same as++ ack -f | ack foo++This means that just as ack will not search, for example, F<.jpg>+files, C<-g> will not list F<.jpg> files either. ack is not intended+to be a general-purpose file finder.++Note also that if you have C<-i> in your .ackrc that the filenames+to be matched will be case-insensitive as well.++This option can be combined with B<--color> to make it easier to+spot the match.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++=item B<-w>, B<--word-regexp>++Turn on "words mode". This sometimes matches a whole word, but the+semantics is quite subtle. If the passed regexp begins with a word+character, then a word boundary is required before the match. If the+passed regexp ends with a word character, or with a word character+followed by newline, then a word boundary is required after the match.++Thus, for example, B<-w> with the regular expression C<ox> will not+match the strings C<box> or C<oxen>. However, if the regular+expression is C<(ox|ass)> then it will match those strings. Because+the regular expression's first character is C<(>, the B<-w> flag has+no effect at the start, and because the last character is C<)>, it has+no effect at the end.++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=item B<--cathy>++Chocolate, Chocolate, Chocolate!++=back++=head1 THE .ackrc FILE++The F<.ackrc> file contains command-line options that are prepended+to the command line before processing. Multiple options may live+on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>+might look like this:++ # Always sort the files+ --sort-files++ # Always color, even if piping to another program+ --color++ # Use "less -r" as my pager+ --pager=less -r++Note that arguments with spaces in them do not need to be quoted,+as they are not interpreted by the shell. Basically, each I<line>+in the F<.ackrc> file is interpreted as one element of C<@ARGV>.++F<ack> looks in several locations for F<.ackrc> files; the searching+process is detailed in L</"ACKRC LOCATION SEMANTICS">. These+files are not considered if B<--noenv> is specified on the command line.++=head1 Defining your own types++ack allows you to define your own types in addition to the predefined+types. This is done with command line options that are best put into+an F<.ackrc> file - then you do not have to define your types over and+over again. In the following examples the options will always be shown+on one command line so that they can be easily copy & pasted.++File types can be specified both with the the I<--type=xxx> option,+or the file type as an option itself. For example, if you create+a filetype of "cobol", you can specify I<--type=cobol> or simply+I<--cobol>. File types must be at least two characters long. This+is why the C language is I<--cc> and the R language is I<--rr>.++I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>+tells you, that perl files are files ending+in .pl, .pm, .pod or .t. So what if you would like to include .xs+files as well when searching for --perl files? I<ack --type-add perl:ext:xs --perl foo>+does this for you. B<--type-add> appends+additional extensions to an existing type.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 AVAILABLE COLORS++F<ack> uses the colors available in Perl's L<Term::ANSIColor> module, which+provides the following listed values. Note that case does not matter when using+these values.++=head2 Foreground colors++ black red green yellow blue magenta cyan white++ bright_black bright_red bright_green bright_yellow+ bright_blue bright_magenta bright_cyan bright_white++=head2 Background colors++ on_black on_red on_green on_yellow+ on_blue on_magenta on_cyan on_white++ on_bright_black on_bright_red on_bright_green on_bright_yellow+ on_bright_blue on_bright_magenta on_bright_cyan on_bright_white++=head1 ACK & OTHER TOOLS++=head2 Simple vim integration++F<ack> integrates easily with the Vim text editor. Set this in your+F<.vimrc> to use F<ack> instead of F<grep>:++ set grepprg=ack\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++=head2 Editor integration++Many users have integrated ack into their preferred text editors.+For details and links, see L<https://beyondgrep.com/more-tools/>.++=head2 Shell and Return Code++For greater compatibility with I<grep>, I<ack> in normal use returns+shell return or exit code of 0 only if something is found and 1 if+no match is found.++(Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)++The I<grep> code 2 for errors is not used.++If C<-f> or C<-g> are specified, then 0 is returned if at least one+file is found. If no files are found, then 1 is returned.++=cut++=head1 DEBUGGING ACK PROBLEMS++If ack gives you output you're not expecting, start with a few simple steps.++=head2 Use B<--noenv>++Your environment variables and F<.ackrc> may be doing things you're+not expecting, or forgotten you specified. Use B<--noenv> to ignore+your environment and F<.ackrc>.++=head2 Use B<-f> to see what files have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=head1 COMMUNITY++There are ack mailing lists and a Slack channel for ack. See+L<https://beyondgrep.com/community/> for details.++=head1 FAQ++=head2 Why isn't ack finding a match in (some file)?++First, take a look and see if ack is even looking at the file. ack is+intelligent in what files it will search and which ones it won't, but+sometimes that can be surprising.++Use the C<-f> switch, with no regex, to see a list of files that ack+will search for you. If your file doesn't show up in the list of files+that C<ack -f> shows, then ack never looks in it.++NOTE: If you're using an old ack before 2.0, it's probably because it's of+a type that ack doesn't recognize. In ack 1.x, the searching behavior is+driven by filetype. B<If ack 1.x doesn't know what kind of file it is,+ack ignores the file.> You can use the C<--show-types> switch to show+which type ack thinks each file is.++=head2 Wouldn't it be great if F<ack> did search & replace?++No, ack will always be read-only. Perl has a perfectly good way+to do search & replace in files, using the C<-i>, C<-p> and C<-n>+switches.++You can certainly use ack to select your files to update. For+example, to change all "foo" to "bar" in all PHP files, you can do+this from the Unix shell:++ $ perl -i -p -e's/foo/bar/g' $(ack -f --php)++=head2 Can I make ack recognize F<.xyz> files?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?++The Perl language limits the repetition quantifier to 32K. You+can search for C<.{32767}> but not C<.{32768}>.++=head2 Ack does "X" and shouldn't, should it?++We try to remain as close to grep's behavior as possible, so when in doubt,+see what grep does! If there's a mismatch in functionality there, please+bring it up on the ack-users mailing list.++=head1 ACKRC LOCATION SEMANTICS++Ack can load its configuration from many sources. The following list+specifies the sources Ack looks for configuration files; each one+that is found is loaded in the order specified here, and+each one overrides options set in any of the sources preceding+it. (For example, if I set --sort-files in my user ackrc, and+--nosort-files on the command line, the command line takes+precedence)++=over 4++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems.++Under Windows XP and earlier, the global ackrc is at+C<C:\Documents and Settings\All Users\Application Data\ackrc>++Under Windows Vista/7, the global ackrc is at+C<C:\ProgramData\ackrc>++The C<--noenv> option prevents all ackrc files from being loaded.++=item * User ackrc++Options are then loaded from the user's ackrc. This is located at+C<$HOME/.ackrc> on Unix-like systems.++Under Windows XP and earlier, the user's ackrc is at+C<C:\Documents and Settings\$USER\Application Data\ackrc>.++Under Windows Vista/7, the user's ackrc is at+C<C:\Users\$USER\AppData\Roaming\ackrc>.++If you want to load a different user-level ackrc, it may be specified+with the C<$ACKRC> environment variable.++The C<--noenv> option prevents all ackrc files from being loaded.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * --ackrc++The C<--ackrc> option may be included on the command line to specify an+ackrc file that can override all others. It is consulted even if C<--noenv>+is present.++=item * ACK_OPTIONS++Options are then loaded from the environment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by contrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<https://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * MetaCPAN++L<http://metacpan.org/release/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+H.Merijn Brand,+Duke Leto,+Gerhard Poul,+Ethan Mallove,+Marek Kubica,+Ray Donnelly,+Nikolaj Schumacher,+Ed Avis,+Nick Morrott,+Austin Chamberlin,+Varadinsky,+SE<eacute>bastien FeugE<egrave>re,+Jakub Wilk,+Pete Houston,+Stephen Thirlwall,+Jonah Bishop,+Chris Rebert,+Denis Howe,+RaE<uacute>l GundE<iacute>n,+James McCoy,+Daniel Perrett,+Steven Lee,+Jonathan Perret,+Fraser Tweedale,+RaE<aacute>l GundE<aacute>n,+Steffen Jaeckel,+Stephan Hohe,+Michael Beijen,+Alexandr Ciornii,+Christian Walde,+Charles Lee,+Joe McMahon,+John Warwick,+David Steinbrunner,+Kara Martens,+Volodymyr Medvid,+Ron Savage,+Konrad Borowski,+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-2017 Andy Lester.++This program is free software; you can redistribute it and/or modify+it under the terms of the Artistic License v2.0.++See http://www.perlfoundation.org/artistic_license_2_0 or the LICENSE.md+file that comes with the ack distribution.++=cut
dev/20110906-agenda.md
@@ -0,0 +1,21 @@+# When: 2011-09-06 14:00 America/Chicago++Andy Lester (@petdance) and Rob Hoelz (@hoelzro) are meeting in+person for a mini ackathon. We'll also be following irc://irc.perl.org/ack+for anyone interested in attending remotely.++# Agenda++* Go over Rob's ConfigFinder implementation.+* Discuss the use of Test::Deep in the test suite.+ * Can we have non-core test-only modules?+* Discuss a roadmap for after Rob completes ConfigLoader.++# Notes/minutes++* Keep Rob's ConfigFinder, and expand out some of the rules. Drop+support for $ACKRC. Fold in the documentation.++* Replace Rob's ConfigLoader with the stuff that Andy had already+written back in February. Andy is starting tests for+App::Ack::ConfigLoader.
@@ -0,0 +1,29 @@+# --ignore-dir and --ignore-file revamp++Users have been dissatisfied with `--ignore-dir` and `--ignore-file`. Here are some common "bug reports"++ - `--ignore-dir=foo/bar` doesn't ignore `foo/bar` at all (GH #291)+ - `--ignore-dir=foo` ignores all directories named `foo`, not just the one at the root of the search (GH #216)+ - `--ignore-dir=foo foo -f` doesn't search `foo/` (GH #492)+ - No way to ignore a file in a folder (GH #479)+ - `--ignore-dir` doesn't implement all filters (GH #42)+ - What's the right behavior for `--ignore-dir=foo`, `--noignore-dir=bar`, `--ignore-dir=baz`, wrt. `foo/bar/baz/file.txt`++Also consider GH #330, and consider that you could be anywhere in a project but still source the `--ignore-dir=./foo` rule+from an ackrc a few directories above. Are we preparing to teach ack about the notion of a project root? Also, consider+`--noignore-dir`++## How does --ignore-dir work at the moment?++## Scenario 1++--ignore-dir can ignore paths as well as directories++## Scenario 2++--ignore-dir only ignores directories, but warns if you try to ignore a path++What's the distinction between --ignore-file and --type/--notype? Does it make sense to allow an "anonymous" type (ex.+--type=is:Something), and make --ignore-file a synonym for --notype? Could --ignore-dir be implemented like this?++If I have `--ignore-dir=test` in my .ackrc, and I do ack $term test, what happens?
dev/UNICODE.md
@@ -0,0 +1,10 @@+# ack 2 Unicode support++ - File encoding (what do we do now? what if someone tries an ack UCS-2-file?)+ - ARGV encoding+ - Document new regex-fu that came with 5.14?+ - Filename/pattern normalization+ - Output stream encoding+ - Collation level is something I mentioned+ - I18N::Langinfo+ - Coloration + unicode chars
dev/chats/2011-08-21-design-ackathon.md
@@ -0,0 +1,719 @@+All times GMT.++* **alester**+ * 14:00:24: Morning!+* **hoelzro**+ * 14:02:07: morning `=)`+* **alester**+ * 14:02:38: That was close.+ * 14:02:42: I hate starting meetings late.+ * 14:03:10: I figured we'd have more!+* **hoelzro**+ * 14:03:29: yeah, me too+ * 14:03:31: `=(`+* **cjm**+ * 14:03:49: morning+* **hoelzro**+ * 14:03:52: so, what's first on the agenda?+ * 14:03:53: cjm: hi!+* **alester**+ * 14:04:20: cjm: Have we met? The nick isn't familiar.+ * 14:04:27: and does everyone have a chekcout of ack2?+* **cjm**+ * 14:04:45: I don't think we've met in person. I've made some contributions before+* **hoelzro**+ * 14:04:50: yup, and 2 #2 pencils!+* **alester**+ * 14:05:00: Wha'ts your name, cjm?+* **cjm**+ * 14:05:09: is ack2 a different repo, or just ack?+* **alester**+ * 14:05:16: no, new repo+ * 14:05:19: parallel to ack+* **cjm**+ * 14:05:20: Christopher J. Madsen+* **alester**+ * 14:05:33: Oh, ok, that's ringing a bell.+ * 14:05:53: [https://github.com/petdance/ack2](https://github.com/petdance/ack2)+* **cjm**+ * 14:06:16: I'm the one who rewrote the tests so test writers didn't need to mess with shell quoting rules+* **alester**+ * 14:06:34: That's goodness.+ * 14:08:05: OK, so here are my thoughts.+ * 14:08:27: First, I want to make everything clear (to the world) what the design changes about ack are.+ * 14:08:32: About ack 2.0.+ * 14:08:42: I am assuming anyone reading them will know what ack 1.x is.+ * 14:09:32: These design declarations will help anyone wanting to help, and they will also form the basis for the ack 2.0 docs that say "Here's how ack 2.0 is better than 1.x"+ * 14:10:08: These design declarations need to have copious examples+ * 14:10:25: because a picture is worth a thousand words, and an example with syntax is worth at least that.+ * 14:10:50: Second, I want to start chopping up the elephent.+ * 14:11:03: There's an old joke that says "How do you eat an elephant? One forkful at a time."+ * 14:11:18: Right now the elephant is "Create & publish ack 2.0"+ * 14:12:08: The ideal chunk will be something where someone looking at the forkfuls and say "Oh, I could do that."+ * 14:12:41: Some of these chunks will be code. Some will be docs. Some might be tests.+ * 14:12:53: (cjm++ for cross-platform testing thinking)+ * 14:13:26: And the "someone" of "someone can say I can do that" includes me. `:-)`+ * 14:13:34: Because I am stymied by looking at this elephant.+ * 14:13:46: I have started writing actual code. It's in ack2 repo+ * 14:14:15: but I'm wary of going to far without having design and future plans because I don't want to run afoul of blinders.+ * 14:14:37: So the more people that can see these design docs, the more people can say "Yeah, but what about…" before we go down a bad path.+ * 14:15:23: Oooh, another benefit of forkfuls: I'd like to be able to say "Hey, we're having an ackathon, come and hack some ack next Tuesday at 8" or whatever+* **hoelzro**+ * 14:15:36: ackathons+++* **alester**+ * 14:15:37: because clearly this "plan an online meeting" DOES get people interested.+ * 14:16:01: And if nothing else, hoelzro and I can have in-person ackathons without too much trouble.+ * 14:16:16: There's a strange amount of love that people have for ack, and they want to help.+ * 14:16:25: We just have to get it to where they CAN.+ * 14:16:39: OK, so, thoughts?+* **cjm**+ * 14:17:32: that all sounds reasonble+* **hoelzro**+ * 14:17:37: we should probably mention that most of our "design documents" are at [https://github.com/](https://github.com/petdance/ack2/tree/master/dev)[petdance](https://github.com/petdance/ack2/tree/master/dev)[/ack2/tree/master/dev](https://github.com/petdance/ack2/tree/master/dev)+* **alester**+ * 14:17:54: Well, I have others, hoelzro.+ * 14:17:59: But clearly they are not where they shoudl be.+* **hoelzro**+ * 14:18:02: although at this point, they're pretty informal, and some contain my own ramblings.+* **alester**+ * 14:18:17: ramblings+++ * 14:18:27: Ramblings can get cleaned up.+ * 14:18:55: A lot of README.md is design doc+* **alester**+ * 14:19:57: welcome, wolverian +* **wolverian**+ * 14:20:44: thanks, great to be here. `:)`+* **alester**+ * 14:20:54: Everyone pull from ack2, I renamed the file+ * 14:21:02: We are glad to have you+ * 14:21:25: Actualy, I think going through the principles line-by-line would be a good way to start+ * 14:21:29: and will help bring up questions.+ * 14:21:38: Especially because the first line is wrong.+ * 14:21:43: \* only search files of types it recognizes+ * 14:21:46: is not correct+ * 14:22:41: \* search all text files by default, although specifications of filetypes will override this.+* **zitterbewegung**+ * 14:23:48: Good day.+* **hoelzro**+ * 14:23:56: zitterbewegung: morning+* **alester**+ * 14:23:57: glad to have you zitterbewegung +* **hoelzro**+ * 14:24:00: or afternoon+* **alester**+ * 14:24:10: OK, updated: repull [https://github.com/petdance/ack2](https://github.com/petdance/ack2)+* **zitterbewegung**+ * 14:24:10: Morning for me.+* **zitterbewegung**+ * 14:25:30: Maybe a good idea would be to search through compressed items?+ * 14:25:35: I see that you have a plugin system.+* **cjm**+ * 14:25:43: [https://github.com/](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)[petdance](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)[/ack2/blob/master/dev/high-level-principles.md](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)+* **alester**+ * 14:25:49: zitterbewegung: not yet+ * 14:25:53: plugins will be 2.1+ * 14:26:05: It is too much to try to wedge into 2.0+* **zitterbewegung**+ * 14:26:55: Oh ok.+* **alester**+ * 14:27:13: OK, repull. I am done with my last minute changes, I hope. `:-)`+ * 14:27:31: Let's look at # Usage principles of ack 2.0 that stay the same+ * 14:27:42: Anything else needs to be said here? Any problems with this?+* **zitterbewegung**+ * 14:28:43: Could you recognise languages by pattern matching if shebang matching fails or would that be too slow or inefficient?+ * 14:28:51: Like possibly a simple heuristic.+* **alester**+ * 14:28:56: Such as?+ * 14:29:10: Finding /<?php/ somewhere in the file?+* **zitterbewegung**+ * 14:29:19: Yea. + * 14:29:35: Or for dr scheme+ * 14:29:42: [#lang](irc://irc.perl.org/#lang) scheme+ * 14:29:47: Match for the module declaration+ * 14:30:29: I would expect this to be implementable as a regex or a lookup table that would be extremely efficent.+* **alester**+ * 14:30:59: I'm going to put that in the 2.1+ potential features section.+* **zitterbewegung**+ * 14:31:01: Like O(1) or O(n) time with only matching the first kilobyte of the file.+ * 14:31:04: Ok sure+* **alester**+ * 14:31:13: because that could be a whole yak-shave right there.+ * 14:31:33: Also, the concerns about speed will be hard to sort out without having a solid 2.0 to start with.+* **zitterbewegung**+ * 14:31:43: Alright sure.+* **wolverian**+ * 14:33:18: file(1) might be useful for that kind of a thing; I think there's a CPAN module that embeds the magic db in itself, too, for working on windows.+* **alester**+ * 14:33:19: OK, changes pushed.+* **hoelzro**+ * 14:33:50: alester: what does ack does about file encodings, btw?+* **wolverian**+ * 14:33:55: in general, for me, shebang line support is enough.+* **alester**+ * 14:33:56: hoelzro: Ouch.+* **hoelzro**+ * 14:34:33: so, nothing really?+ * 14:34:36: `=/`+* **cjm**+ * 14:34:38: you don't need to say "(although this will probably be ack 2.1)" anymore+* **hoelzro**+ * 14:34:53: I mean, I don't know if that's really the job of ack...yet+* **cjm**+ * 14:34:54: since the heading now says 2.1++* **hoelzro**+ * 14:35:10: but since you're trying to attract Windows users, that might be important+ * 14:35:23: since Windows uses UTF-16 (or is it UCS-2?) by default, I think+* **alester**+ * 14:35:36: Here's the thing: I don't know shit about encodings. `:-)`+* **hoelzro**+ * 14:35:49: alester: 2.1?+ * 14:35:56: alester: I know...a decent amount.+ * 14:36:02: not a master by any means+* **wolverian**+ * 14:36:06: hoelzro: or windows-1251.+* **alester**+ * 14:36:09: What would be in 2.1?+* **hoelzro**+ * 14:36:15: wolverian: thanks, good call+* **alester**+ * 14:36:28: Reload+ * 14:36:36: (I'm just going to say "Reload" whenever I push a change)+* **zitterbewegung**+ * 14:36:58: From my understanding the most common encodings are UTF-8 and Ascii+* **hoelzro**+ * 14:37:06: zitterbewegung: on Unix, they are+ * 14:37:13: and on the Internet+* **zitterbewegung**+ * 14:37:15: Oh yea and windows you have UTF-16+* **alester**+ * 14:37:25: I think that unless we know what we're doing, we just use the default behavior of Perl.+* **zitterbewegung**+ * 14:37:25: So I would support those as a bare minimum.+* **hoelzro**+ * 14:37:34: yes, and that brings many frown faces.+* **zitterbewegung**+ * 14:37:35: Yea default behavior of perl would be the best.+* **hoelzro**+ * 14:37:36: `=(` `=(` `=(`+* **alester**+ * 14:37:48: What should we do instead, hoelzro ?+* **hoelzro**+ * 14:37:54: isn't sure of the default behavior of Perl on Windows+ * 14:38:09: alester: the UTF-16 part brings frown faces, not Perl's default behavior+* **cjm**+ * 14:38:10: same as unix, except for :crlf+* **alester**+ * 14:38:14: oh, ok+* **hoelzro**+ * 14:38:17: hmm+ * 14:38:29: what if it's reading in a UTF-16 encoded file?+* **cjm**+ * 14:38:37: then you'd better tell it that+* **hoelzro**+ * 14:38:40: do I get 0x00 0x65 0x00 0x66, et+ * 14:38:43: that's what I thought+ * 14:39:10: well, Windows marks its UTF-16 files with a BOM, right?+* **zitterbewegung**+ * 14:39:14: Should we try to translate UTF-16 to UTF-8? I believe thats possible right?+* **alester**+ * 14:39:23: Reload: Added a note about encodings.+* **hoelzro**+ * 14:39:28: zitterbewegung: yeah, the Encode module can handle that+* **cjm**+ * 14:39:42: Windows usually uses a BOM+* **wolverian**+ * 14:39:49: why would ack need to translate \*to\* an encoding? isn't decoding them into character strings enough?+* **hoelzro**+ * 14:40:01: wolverian: that's exactly what we would d+ * 14:40:02: \*do+* **alester**+ * 14:40:04: wolverian: I don't know. Forgive me terminology. `:-)`+* **hoelzro**+ * 14:40:14: but we also need to encode when printing the results+ * 14:40:15: maybe.+* **alester**+ * 14:40:20: 2.0 can't handle encoding hoohah.+ * 14:40:32: There is far too much other goodness to deal with.+* **wolverian**+ * 14:40:36: that was mostly a reply to zitterbewegung. ah, printing results. yes, that probably needs to look at the locale. though I have no idea what windows does here.+* **alester**+ * 14:40:59: So encodings is off the table for this. </gavel>+* **hoelzro**+ * 14:41:02: alester: is encoding something I should look at for 2.0?+ * 14:41:05: oh, nvm+* **wolverian**+ * 14:41:14: alester: good call, it's a swamp+* **zitterbewegung**+ * 14:41:15: wolverian: I thought it would make things easier.+* **alester**+ * 14:41:16: see how I worded it.+* **zitterbewegung**+ * 14:41:28: wolverian: Reducing the problem to something we know. Maybe not. Anyways.+* **alester**+ * 14:41:30: \* use Perl's default file-handling as far as dealing with files of different encodings+ * 14:41:45: It's worked for 1.x. `:-)`+ * 14:42:27: OK, any other comments in the first two sections? # Usage principles of ack 2.0 that stay the same+ * 14:42:27: and # Continuing design principles of ack 2.0+* **cjm**+ * 14:45:12: "ignore version control directories, core dumps and other large files"+ * 14:45:12: will that be only if you install .ackrc, or will there be a built-in list?+* **alester**+ * 14:45:35: \* no hardcoded filetypes or directories+ * 14:45:35: \* Everyone has different needs. ack will ship with a default+ * 14:45:35: ackrc, but without that, there will be no exclusions.+ * 14:45:42: Does that say it enough? + * 14:46:03: Eh, it's pretty awkward.+* **cjm**+ * 14:46:31: those two seem to conflict+* **alester**+ * 14:46:35: ok+ * 14:46:38: Here's what I want to say+ * 14:46:45: EVERYTHING is based on the ackrc(s). + * 14:46:53: There will be nothing hardcoded in ack itself.+ * 14:47:18: You should never have to patch your ack, which is what many people do now to get filetypes in.+* **cjm**+ * 14:47:25: then you should probably just delete \* ignore version control directories, core dumps and other large files+* **alester**+ * 14:47:37: Right, except that that will be the default behavior.+* **zitterbewegung**+ * 14:47:46: Maybe ignore large binary files?+* **alester**+ * 14:47:52: I guess maybe default behavior based onthe default ackrc should be in there.+* **zitterbewegung**+ * 14:48:05: Would that be good default behavior?+* **alester**+ * 14:48:07: "large" is a vague.+* **zitterbewegung**+ * 14:48:13: Oh ok.+ * 14:48:22: Large I mean larger than 1megabyte+* **alester**+ * 14:48:24: Anything that is not -T is ignored.+* **cjm**+ * 14:48:54: so "non-text" instead of "large"?+* **alester**+ * 14:48:58: yes+ * 14:49:03: but there will be other things, too.+ * 14:49:07: like \*.min.js+* **hoelzro**+ * 14:49:21: hooray!+* **alester**+ * 14:49:26: heh+* **cjm**+ * 14:50:03: except \*.min.js will be ackrc, and non-text will be ack core?+* **zitterbewegung**+ * 14:50:15: Heh I have a silly idea for 2.1. Classify files by their entropy.+* **alester**+ * 14:50:20: yes, non-text will be core+* **zitterbewegung**+ * 14:50:28: That would be a hilarious plugin.+ * 14:51:15: Like /dev/random would be 100% random+ * 14:51:21: anyways+* **alester**+ * 14:52:58: oooh+ * 14:53:04: how do we handle ackrc isntallation+* **cjm**+ * 14:54:06: It'd be nice if there's an ack --install+* **alester**+ * 14:54:14: that's a heck of an idea.+* **hoelzro**+ * 14:54:29: alester: you mean installing ackrc to /etc/ackrc or something?+* **alester**+ * 14:54:34: hoelzro: yes+* **hoelzro**+ * 14:54:36: ok+* **alester**+ * 14:54:53: cjm: That would let people do the single-file download, and then run ack --install+ * 14:55:02: I REALLY want to keep the single-file download, and that would do it.+* **zitterbewegung**+ * 14:55:39: Could we create a script that detects the user environment+* **hoelzro**+ * 14:55:44: alester: we should probably detect the case where no ackrc was found+* **zitterbewegung**+ * 14:55:47: like a boostrap function+* **alester**+ * 14:55:50: Sure+* **zitterbewegung**+ * 14:56:13: and then create the ackrc in a default directory? Or do we want to prompt for that directory?+* **alester**+ * 14:56:42: zitterbewegung: Not sure. +* **cjm**+ * 14:57:03: I don't see the point in a prompt, except for global vs. per-user+* **alester**+ * 14:57:20: I'm leaving that for later+* **cjm**+ * 14:57:20: ack needs to know where to look for it, so a custom value isn't useful+* **hoelzro**+ * 14:57:33: we could do that as a switch I'd bet+ * 14:57:43: what if there's a ~/.ackrc, but no /etc/ackrc?+* **alester**+ * 14:57:45: Remember that we will have MANY pleaces to look for ackrcs+ * 14:58:00: There will be a well-defined hierarchy.+* **zitterbewegung**+ * 14:59:11: is going to lunch+* **alester**+ * 14:59:13: Reload+* **Gimpson**+ * 14:59:21: Any possibility of multiple ackrcs in a particular location?+ * 14:59:32: So I have my js ackrs, my perl ackrc and my mason ackrc+* **alester**+ * 14:59:35: Gimpson: Didn't see you come in, welcome.+* **Gimpson**+ * 14:59:54: And don't have to cat them together, they can remain separate and upgradable.+ * 14:59:59: (and thanks)+* **alester**+ * 15:00:02: Gimpson: I'm not seeing the usecase for that.+ * 15:00:08: Oh, I see what you're saying.+ * 15:00:18: /etc/ackrc.d/+* **Gimpson**+ * 15:00:24: Yeah, something like that.+* **alester**+ * 15:00:30: An interesting idea, but I think that'll be 2.1 if ever+* **hoelzro**+ * 15:00:32: /etc/ackrc.d+++* **alester**+ * 15:00:42: because we don't know how people will use the ackrc anywy+ * 15:00:59: I want to see the ackrc cowpaths.+ * 15:01:40: but I'm adding that to the 2.1 section.+ * 15:01:55: Gimpson: when did you come in?+ * 15:02:06: oh look, there you are, I missed it.+ * 15:02:23: Reload+* **cjm**+ * 15:02:58: for those who missed it: [https://github.com/](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)[petdance](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)[/ack2/blob/master/dev/high-level-principles.md](https://github.com/petdance/ack2/blob/master/dev/high-level-principles.md)+* **alester**+ * 15:03:18: So let's leave that for now, and I want to start thinking for forkfuls.+ * 15:03:31: [https://github.com/petdance/ack2/blob/master/dev/forkfuls.md](https://github.com/petdance/ack2/blob/master/dev/forkfuls.md)+* **Gimpson**+ * 15:03:54: I was a bit late, came in around 7:30+ * 15:04:01: er start:30+* **alester**+ * 15:04:33: Sorry for the earliness. hoelzro and I are Central, so we're already awake. `:-)`+ * 15:04:44: OH+ * 15:04:51: EVERYTHING has to be specified via command-line+ * 15:05:12: Nothing can be in the ackrc files except for command-line switches+ * 15:05:53: You have to be able to do everything you want via the command-line.+ * 15:08:10: Scale of 1-10: How familiar are you all with the ack 1.x source code & tree?+ * 15:08:18: Me: 9+* **Gimpson**+ * 15:08:33: Me: 5+* **cjm**+ * 15:08:43: Me: ~4+* **wolverian**+ * 15:08:48: 1 +* **hoelzro**+ * 15:09:06: 2+* **alester**+ * 15:09:25: ok+ * 15:10:31: OK, look at forkfuls, please+ * 15:10:39: What sorts of high-level stuff do we need to do?+ * 15:11:16: The ackrc handling is going to have to be VERY well defined.+ * 15:11:30: because we have to get it right the first time.+ * 15:11:53: I really don't want to say in 2.1 "Oh, btw, we decided that the global ackrc now is secondary to ..."+ * 15:12:03: or whatever. Because tat will screw up a lot of people.+* **hoelzro**+ * 15:12:23: alester: I wouldn't mind taking a look at the ackrc location stuff+ * 15:12:43: but I'd need some external help to make sure I don't succumb to my own personal bias.+* **alester**+ * 15:12:50: ok+ * 15:13:07: You're talking about defining the rules, right?+ * 15:13:12: of where we find them?+* **Gimpson**+ * 15:13:12: alester: Are you looking for more TODOs for forkfuls, or wanting to start defining those rules now?+* **alester**+ * 15:13:22: No, don't want to define rules now.+* **hoelzro**+ * 15:13:39: alester: well, that and writing the code+* **alester**+ * 15:13:57: hoelzro: That's fine too+ * 15:14:28: The glossary is gonna be big.+ * 15:15:14: reload+* **hoelzro**+ * 15:16:46: alester: are we considering ACK\_OPTIONS to be part of the ackrc logic?+* **alester**+ * 15:17:11: yes+* **hoelzro**+ * 15:17:16: mkay+ * 15:17:26: could you throw that in the notes?+* **alester**+ * 15:17:43: reload+ * 15:18:10: OH! We need a debug mode.+ * 15:18:42: ack --debug + * 15:18:52: or ack --rule-dump+ * 15:19:47: reload+ * 15:19:58: just added a forkful "Look at glark and see if there's anything we want to steal"+* **hoelzro**+ * 15:20:17: debug+++* **alester**+ * 15:20:59: ack -f started as debug+ * 15:21:04: and now it's a feature+ * 15:23:24: Gimpson: If you and hoelzro want to put your heads together on rules for ackrc finding, that'd be swell.+* **hoelzro**+ * 15:23:34: I'll start a thread+* **alester**+ * 15:25:02: I'm wondering if we should have a separate ack2 list.+ * 15:25:21: Nah, just start it on ack-users+ * 15:25:28: if it gets too noisy, we can split it off.+* **cjm**+ * 15:25:34: agree+* **alester**+ * 15:25:52: hoelzro: A request: Don't start the thread by saying "What do you think it should be?"+ * 15:26:07: Start with "Here's my draft of rules for finding ackrc files in ack 2.0"+ * 15:26:16: because 90% of whatever you come up with in that first draft will be fine.+* **hoelzro**+ * 15:27:18: mkay+* **alester**+ * 15:27:30: And usually when things get opened up to the floor like that, what you come up with is about what you would have had on your own. `:-)`+ * 15:27:35: Attention is a scarce resource.+* **hoelzro**+ * 15:28:03: heh+* **alester**+ * 15:28:23: Anything else in forkfuls anyone's interested in?+ * 15:28:34: or where you see it can be broken up?+* **Gimpson**+ * 15:29:20: Filetype detection, do we know all the new ways we'll support?+* **alester**+ * 15:29:26: yes+* **Gimpson**+ * 15:29:33: extension and regex are the two I know of+ * 15:29:43: shebang+* **alester**+ * 15:29:44: [https://github.com/petdance/ack2/blob/master/ackrc](https://github.com/petdance/ack2/blob/master/ackrc)+* **Gimpson**+ * 15:30:12: Ah, cool.+* **alester**+ * 15:31:54: and then adding some other heuristic like "entirefilematch" for <?php fits nicely+ * 15:32:15: That ackrc is pretty much translated directly from ack 1.x+* **alester**+ * 15:34:36: Hi ad.+ * 15:34:40: Welcome to the party.+* **zitterbewgung-starb**+ * 15:35:34: Hi again.+* **alester**+ * 15:35:36: zitterbewegung: MUST HAVE COFFE+ * 15:35:46: ad: Tell us a bit about your backgroudn.+* **zitterbewgung-starb**+ * 15:35:49: will order you some+* **alester**+ * 15:35:58: zitterbewegung: I prefer my coffee cold.+* **ad**+ * 15:36:01: alester: Just lurking around, thanks+* **alester**+ * 15:36:05: I mean caffeine+* **zitterbewgung-starb**+ * 15:36:14: orders you a cold grande latte+ * 15:36:17: oh wait+* **alester**+ * 15:36:24: lives on diet Coke+* **zitterbewgung-starb**+ * 15:36:27: orders you a cold caffeine IV+ * 15:36:42: oh yea diet coke is great.+* **alester**+ * 15:36:57: OK, so far we have hoelzro going to write up some rules for ackrc/ACK\_OPTIONS + * 15:37:07: Which I assume will be roughly+ * 15:37:21: in order of priority+ * 15:37:47: ACK\_OPTIONS -> /etc/ackrc -> ~/.ackrc -> project ackrc -> command line switches+ * 15:38:02: from least to most priority+ * 15:38:09: (Gimpson: I guess we ARE talking about rules now :-))+* **hoelzro**+ * 15:38:26: ah, I didn't include command line in my doc+* **alester**+ * 15:38:47: we MUST have the directoriy hierarchy+* **hoelzro**+ * 15:38:58: mkay+* **alester**+ * 15:39:09: because per-project is going to be huge+ * 15:39:28: Because search-all-text-by-default is going to screw up a lot of stuff+* **hoelzro**+ * 15:39:37: hmm+ * 15:39:38: idea+* **alester**+ * 15:39:41: for isntance, here at work, we have plenty of .xml in the source trees+* **hoelzro**+ * 15:39:53: actually, scratch that+* **alester**+ * 15:40:25: and I have to be able to have a .ackrc in my project that says --ignore-file=filetype,xml+ * 15:41:02: oops, I mean --ignore-file=ext,xml+ * 15:41:32: ad: Are you using ack now?+* **ad**+ * 15:41:56: alester: No, but I know what it is+* **Gimpson**+ * 15:43:23: project ackrc is just a .ackrc in a particular directory?+* **alester**+ * 15:43:59: yes+* **hoelzro**+ * 15:44:17: alester: what about --include-file/--include-dir directives?+* **alester**+ * 15:44:26: hoelzro: What about them?+ * 15:44:50: I see ackrc finding & reading to be aggregation of directives.+ * 15:44:56: Nothing more.+* **hoelzro**+ * 15:48:09: just an idea+* **alester**+ * 15:48:37: I don't get what the idea is.+ * 15:49:31: Oh, they don't exist. You're suggesting explicitly adding --include-file/--include-dir to negate --exclude-file/--exclude-dir?+* **hoelzro**+ * 15:50:02: well, I meant it in the context if including an ackrc file or a directory of them+* **alester**+ * 15:50:44: like --ackrc=/path/to/file ?+ * 15:51:16: Oh, also you have to deal with the $ACKRC variable, too+ * 15:51:34: Or maybe we kill it.+* **cjm**+ * 15:52:23: if we have ./.ackrc, ../.ackrc, etc., we probably don't need $ACKRC+* **alester**+ * 15:52:44: That's what I'm thinking.+* **Gimpson**+ * 15:53:29: Rules are defined completely at runtime before recursing into subdirs?+ * 15:54:03: For example, if I have a .ackrc in ., but there's another in bin/.ackrc, does the .ackrc in bin get used?+ * 15:54:09: When recursing into that directory?+* **cjm**+ * 15:54:32: I don't think so. Juggling multiple sets of rules will get too complicated+* **alester**+ * 15:54:45: No+ * 15:54:50: we go up looking for .ackrc+ * 15:54:59: not down+ * 15:56:32: One that definitely need to be in the docs is examples.+* **alester**+ * 15:56:42: hi und3f+ * 15:56:46: It's a damn party!+* **und3f**+ * 15:56:57: alester, hello+* **zitterbewgung-starb**+ * 16:00:16: Parttayyy+* **alester**+ * 16:02:57: is running around naked in a circle+ * 16:04:37: OK, are we about done for today?+ * 16:04:47: I'm going to archive this chat for posterity.+* **cjm**+ * 16:04:59: bye, ackolytes+* **hoelzro**+ * 16:05:08: sounds good+* **ad**+ * 16:05:09: Just a quick question+* **alester**+ * 16:05:19: Thanks to all of you for showing up. We definitely need to do this again. People are clearly interested.+ * 16:05:20: Yes, ad?+* **ad**+ * 16:05:34: Any non core modules required now or in the future?+* **alester**+ * 16:05:41: [File::Next](File::Next) is it.+ * 16:06:03: What makes you ask?+* **ad**+ * 16:06:10: Ok, need to look for an easy way to deploy on FreeBSD +* **alester**+ * 16:06:37: How do you mean?+* **ad**+ * 16:07:10: I mean, I don't want to install ad hoc in every machine+* **alester**+ * 16:07:18: Sure.+ * 16:07:36: I don't see anything on betterthangrep.com where someone has packaged ack+ * 16:07:47: Does FreeBSD have some package system?+* **ad**+ * 16:07:57: Yes, FreeBSD ports+* **alester**+ * 16:07:57: It's in Macports+ * 16:08:04: OK, so someone needs to package ack.+* **ad**+ * 16:08:09: And there is something called bedpan+ * 16:08:17: Damn autocorrect!+ * 16:08:21: Bsdpan+* **alester**+ * 16:08:53: I don't know anything about BSD's packaaging (or anyone's really)+ * 16:09:01: So we'll have to rely on someone else.+ * 16:09:10: Or you can install with "sudo cpan App::Ack"+* **ad**+ * 16:09:25: I was trying to avoid that+* **alester**+ * 16:09:34: Which "that"?+ * 16:09:37: cpan shell?+* **ad**+ * 16:09:41: Yes+* **alester**+ * 16:09:47: Understood.+* **ad**+ * 16:10:24: I'll play around and let you know if there is a way +* **alester**+ * 16:10:35: It would be useful for ack 1.x, too.+* **ad**+ * 16:11:03: You're Andy Lester of perlbuzz, right? + * 16:11:18: I 'll know how to contact you, then+ * 16:11:38: Yes, for 1.x too+* **alester**+ * 16:12:27: That's me.+ * 16:12:35: There's an ack-users mailing list, too.+* **ad**+ * 16:12:43: Noted+* **zitterbewgung-starb**+ * 16:13:18: Do you need someone to package ack for macports?+* **alester**+ * 16:13:27: It's already there, zitterbewgung-starb +* **zitterbewgung-starb**+ * 16:13:31: oh ok+* **alester**+ * 16:13:35: see betterthangrep.co+ * 16:13:37: .com+* **zitterbewgung-starb**+ * 16:13:48: ok nvm+ * 16:13:58: this chat was fun `:-)`+* **alester**+ * 16:15:32: It was, wasn't it?+ * 16:15:36: And isn't that why we do this?+ * 16:15:45: I will definitely schedule more.+* **hoelzro**+ * 16:16:18: sounds good to me!+* **alester**+ * 16:16:49: Yes, and thanks espeically to hoelzro for kicking me in the ass to get ack 2.0 going again. `:-)`+* **zitterbewgung-starb**+ * 16:16:55: `:-)`+ * 16:17:09: Looks like we even have parts of 2.1 planed out too+* **alester**+ * 16:17:14: but to all of you for showing up and helping and commenting and (presumably) spreading the word about ack.+* **zitterbewgung-starb**+ * 16:17:38: heard about this from your twitter.+* **alester**+ * 16:17:50: zitterbewgung-starb: Which one? @petdance or @perlbuzz?+* **zitterbewgung-starb**+ * 16:18:32: petdance+* **alester**+ * 16:18:49: I have a todo for Mondy morning to point @petdance people to @perlbuzz and vice versa.+* **zitterbewgung-starb**+ * 16:19:10: oh ok i can follow the other one then+* **alester**+ * 16:19:52: zitterbewgung: Holy cats, you're local, too.+ * 16:20:03: Would you be up for in-person ackathonning, too?+* **zitterbewgung-starb**+ * 16:20:11: Uh possibly+* **alester**+ * 16:20:14: I'm in McHenry, hoelzro is in Madison+* **zitterbewgung-starb**+ * 16:20:18: I don't know much perl+ * 16:20:22: but i'm a quick learner+* **alester**+ * 16:20:22: ok+* **zitterbewgung-starb**+ * 16:20:36: I taught myself python pretty fast+* **alester**+ * 16:20:42: As we've seen this morning,the power of having people together is pretty strong.+* **ad**+ * 16:21:08: Afternoon here `:)`+* **zitterbewgung-starb**+ * 16:23:25: alester: I met you at barcamp . I am located in Darien.+ * 16:23:39: alester: I gave a presentation at barcamp on quantum computation and I bought your book+* **alester**+ * 16:23:58: oh, cool+ * 16:24:05: Did the book do you any good?+* **zitterbewgung-starb**+ * 16:24:28: alester: Well I am probably going to use it in about 6months+* **alester**+ * 16:24:36: Use it NOW+* **zitterbewgung-starb**+ * 16:24:36: Once i finish my degree. + * 16:24:43: Yea I should+* **alester**+ * 16:24:46: start your resume now+* **zitterbewgung-starb**+ * 16:25:01: Yea you sent me back my resume and I am going to revise it now+* **hoelzro**+ * 16:28:33: well, I'm outta here people+ * 16:28:41: gotta help my girlfriend with the food-makings.+ * 16:29:03: later!+* **zitterbewgung-starb**+ * 16:29:05: hoelzro: Have fun `:-)`
dev/chats/2011-09-01-config-finder.md
@@ -0,0 +1,122 @@+* Rob Hoelz: got a minute to chat about Ack?+* Andy Lester: Not really, but go ahead. :-)+* Andy Lester: It's never stopped me before!+* Rob Hoelz: I was just thinking about testing the configuration loader+* Andy Lester: ok+* Andy Lester: oh,+* Andy Lester: oh+* Rob Hoelz: I was thinking of two ways of doing it:+* Andy Lester: ok+* Andy Lester: go on+* Rob Hoelz: 1) make local overrides for open+* Rob Hoelz: 2) inject an Ack::FileReader object to Ack::ConfigLoader, and use that to actually read files in+* Rob Hoelz: and provide a mock reader in the tests+* Rob Hoelz: what do you think?+* Andy Lester: I think that file finding is one concern.+* Andy Lester: I think that option building is another.+* Andy Lester: and the two don't have to coincide.+* Rob Hoelz: I'd normally agree+* Rob Hoelz: but+* Andy Lester: (This is just off the top of my head, not an edict)+* Rob Hoelz: with the talk of a --dont-load-project-ackrc/--load-project-ackrc option in /etc/ackrc or ~/.ackrc, they would have to, right?+* Andy Lester: I'm not sure we have to have mingle the finding of ackrc files with the reading of ackrc files with the interpretation of those contents.+* Andy Lester: I see three different parts+* Andy Lester: Oh, yeah, there's that.+* Rob Hoelz: if that feature weren't on the menu, I'd just have Ack::ConfigLocator+* Andy Lester: I think we may have to limit where a --dont-load-project-ackrc file can be found+* Andy Lester: Well, here's the thing.+* Andy Lester: It's not really a "don't read the project file"+* Andy Lester: it's a "ignore project file"+* Andy Lester: We can go ahead and read the options out of that file.+* Andy Lester: and then if we run across --ignore-project-ackrc+* Andy Lester: toss those options.+* Rob Hoelz: interesting+* Andy Lester: Remember: Every option has to know its source+* Rob Hoelz: right+* Andy Lester: because the --ackrc-debug is going to say "This option came from ~/.ackrc, and this one came from ACK\_OPTIONS"+* Andy Lester: so ignoring is trivial+* Andy Lester: and we're not worried about the time of finding/reading the project ackrc+* Rob Hoelz: is that for *all* options, or just ack rules?+* Andy Lester: ALL options+* Rob Hoelz: ok+* Rob Hoelz: ok, that clears things up a bit+* Andy Lester: that lets us display the hierarchy of options+* Andy Lester: Becuase with such a trail of potential sources of options, people will want to know why such-and-such is happening.+* Rob Hoelz: ok+* Rob Hoelz: so, let's call this module Ack::ConfigFinder+* Andy Lester: ok+* Andy Lester: Hey, wanna try to hack on Sunday?+* Rob Hoelz: can't =(+* Andy Lester: ok+* Rob Hoelz: away for Labor Day stuff+* Andy Lester: dat's fine+* Andy Lester: I think it would be helpful long-term to dump the key parts of this chat, in English, to a file somewhere+* Rob Hoelz: sure+* Andy Lester: We basically just created the top block of documentation of Ack::ConfigFinder+* Rob Hoelz: I'm not saying anything incriminating =P+* Andy Lester: discussing how it does the finding.+* Andy Lester: :-)+* Andy Lester: Just pull out the good stuff+* Andy Lester: and capture it to a file.+* Rob Hoelz: I'm guessing that Ack::ConfigFind::find\_configs should return a list of hashes/objects, instead of just paths+* Rob Hoelz: namely, so we can say { path => '...', project\_file => 1 }+* Rob Hoelz: thoughts?+* Andy Lester: you mean get that back+* Andy Lester: "Ok, this was the project-level file"+* Andy Lester: ?+* Rob Hoelz: yes+* Rob Hoelz: so we can forgo reading project files if $dont\_read\_project\_file is set+* Andy Lester: We'll always read them.+* Rob Hoelz: and that was my next question =)+* Andy Lester: Because me might not know until we've loaded a given file "Oh wait, we're ignoring the project file"+* Andy Lester: which is why we want to say --ignore-project-ackrc+* Andy Lester: not --dont-read+* Rob Hoelz: I see+* Rob Hoelz: hmm+* Rob Hoelz: random debug mode question+* Andy Lester: k+* Rob Hoelz: let's say for time being that --add-rule overrides previous entries (I don't know if it does)+* Andy Lester: I think it does+* Andy Lester: which is another reason that we need to know the source of everything.+* Rob Hoelz: ok, good+* Andy Lester: I'm thinking each source gets some sort of numerical ranking +* Rob Hoelz: sounds sensible+* Andy Lester: (for internal use only)+* Andy Lester: /etc/ackrc = 10+* Andy Lester: ~/.ackrc = 20+* Andy Lester: project ackrc = 30+* Rob Hoelz: so, let's say I have --add-rule=perl,ext,pl|pod|t in my /etc/ackrc+* Andy Lester: ACK\_OPTIONS=40+* Andy Lester: command line = 50+* Andy Lester: or something like that+* Rob Hoelz: and I have --add-rule=perl,ext,pl in my ~/.ackrc+* Andy Lester: ok+* Rob Hoelz: when we dump debugging info for options+* Rob Hoelz: would it show the first rule for /etc/ackrc, but specify, "overriden by ~/.ackrc"?+* Andy Lester: I think it should.+* Andy Lester: We have to figure that out ourselves+* Andy Lester: might as well tell the user.+* Rob Hoelz: ok, I was hoping you'd say that+* Rob Hoelz: glad to see we're on the same page+* Rob Hoelz: as far as the ranking of option sources goes+* Andy Lester: Yeah, because it's going to be the source of confusino.+* Andy Lester: We're mkaing it VERY flexible+* Rob Hoelz: I was thinking of assigning rank based on the source's position in the array returned by ConfigFinder+* Rob Hoelz: which isn't *much* different+* Rob Hoelz: but it would allow us to easily insert new types of options files in the future if we come up with any+* Andy Lester: right+* Andy Lester: and the numbers are just arbitrary+* Rob Hoelz: oh, ok+* Andy Lester: they're internal only+* Andy Lester: Although+* Andy Lester: we might assign a sequence number+* Rob Hoelz: I was thinking you wanted constants in the code =)+* Andy Lester: where we say in debug mode that rule #27 overrode #13+* Andy Lester: Well, sure, they can be constants in the code+* Andy Lester: but they can change.+* Andy Lester: I just assigned #10, #20, #30 because I still do that from working in BASIC in the late 70s :-)+* Rob Hoelz: heh+* Andy Lester: ok, all done here?+* Rob Hoelz: ypu+* Andy Lester: kthx+* Rob Hoelz: I've got enough =)
dev/config-test-cases.md
@@ -0,0 +1,11 @@+# Multiple sources++Does a lower-priority source trump a higher one?++# Debug mode++Define debug mode, anyway.++# Options overwriting each other++What about things like -C 10 followed by -A 5?
dev/display-option-coverage.pl
@@ -0,0 +1,46 @@+#!/usr/bin/env perl++use strict;+use warnings;+use lib 'blib/lib';++use App::Ack::ConfigLoader ();++if ( ! -e 'opts.coverage' ) {+ die <<'END_DIE';+Coverage file 'opts.coverage' not found!+If you'd like to generate this file, please+set the ACK_OPTION_COVERAGE environment variable+to a truthy value and run 'make test'.+END_DIE+}++my @extra_options = (+ 'type-add=s',+ 'type-set=s',+ 'dump',+);++my $arg_spec = App::Ack::ConfigLoader::get_arg_spec({});+foreach my $option ( @extra_options ) {+ $arg_spec->{ $option } = 1;+}++my $fh;+open $fh, '<', 'opts.coverage' or die $!;++while ( <$fh> ) {+ chomp;++ delete $arg_spec->{$_};+}++close $fh;++if ( keys %{$arg_spec} ) {+ print "The following options were not used in the test suite:\n\n";+ print " $_\n" foreach sort keys %{$arg_spec};+}+else {+ print "All options were used in the test suite\n";+}
dev/find-minimum-dep-versions.pl
@@ -0,0 +1,160 @@+#!/usr/bin/env perl++# This script looks through the list of dependencies in+# @dependencies (see below) and tries to find the minimum+# version of each with which ack2 will run.+#+# This script considers each version in isolation, so+# it won't catch things like "Getopt::Long 10 + File::Spec 5 works,+# Getopt::Long 5 + File::Spec 10 works, but Getopt::Long 5 ++# File::Spec 5 doesn't work". I figured this would be acceptable,+# and I didn't want the script to work through the exponential number+# of combinations.+#+# This script also assumes that for each dependency, there exists a+# release R for which all releases before R fail, and all releases+# including and after R succeed. This script is intended as a rough+# estimate tools that delivers minimum module versions higher than+# 0.++use strict;+use warnings;+use feature 'say';++use Capture::Tiny qw(capture_merged);+use File::Spec;+use File::Temp;+use MetaCPAN::API;++# ANSI terminal control codes+my $SAVE = "\e[s";+my $UNSAVE = "\e[u";+my $ERASE_TO_END = "\e[K";++sub get_releases {+ my ( $module ) = @_;++ my $mcpan = MetaCPAN::API->new;+ my $distribution = $mcpan->fetch(+ 'module/' . $module,+ )->{'distribution'};++ my $from = 0;+ my $hits;+ my @releases;++ do {+ my $result = $mcpan->post(+ release => {+ query => {+ term => {+ 'release.distribution' => $distribution,+ },+ },+ filter => {+ term => {+ maturity => 'released',+ },+ },+ sort => [+ { version => 'asc' },+ ],+ from => $from,+ fields => [qw/download_url version/],+ },+ );++ $hits = $result->{'hits'}{'hits'};+ $from += @$hits;++ if(@$hits) {+ foreach my $hit (@$hits) {+ push @releases, {+ version => $hit->{'fields'}{'version'},+ url => $hit->{'fields'}{'download_url'},+ },+ }+ }+ } while(@$hits);++ return @releases;+}++sub bisect (&@) {+ my ( $predicate, @values ) = @_;++ my $low = 0;+ my $high = @values;+ my $last_good;++ while($low <= $high) {+ my $middle = $low + int(($high - $low) / 2);+ local $_ = $values[$middle];+ my $result = $predicate->($_);++ if ($result) {+ $last_good = $_;+ $high = $middle - 1;+ }+ else {+ $low = $middle + 1;+ }+ }++ return $last_good;+}++my @dependencies = (+ 'Carp',+ 'Cwd',+ 'File::Basename',+ 'File::Next',+ 'File::Spec',+ 'File::Temp',+ 'Getopt::Long',+ 'Pod::Usage',+ 'Term::ANSIColor',+ 'Test::Harness',+ 'Test::More',+ 'Text::ParseWords',+);++my @output;++local $| = 1;+foreach my $dep (@dependencies) {+ print "Testing $dep... $SAVE";++ my @releases = get_releases($dep);+ my $first_good_release = bisect {+ print "$UNSAVE$ERASE_TO_END$_->{'version'}";+ my $tempdir = File::Temp->newdir;+ my $status;+ capture_merged {+ $status = system 'cpanm', '--notest', '--local-lib', $tempdir->dirname, $_->{'url'};+ return if $status;++ local $ENV{'PERL5LIB'} = File::Spec->catdir($tempdir->dirname, 'lib/perl5');++ $status = system 'make', 'test';+ };++ $status == 0+ } @releases;++ print "\n";+ if (!defined $first_good_release) {+ push @output, "$dep - no suitable version found";+ }+ elsif ($first_good_release == $releases[0]) {+ # All tested versions are ok. Earlier, untested versions might also work+ push @output, "$dep - <=$first_good_release->{'version'}";+ }+ else {+ push @output, "$dep - $first_good_release->{'version'}";+ }+ print "$output[-1]\n";+}++print "\n";+say foreach @output;
dev/generate-completion-scripts.pl
@@ -0,0 +1,231 @@+#!/usr/bin/env perl++use strict;+use warnings;+use lib 'blib/lib';+use lib 't';++use Template;+use Util;++# XXX special completion for --type-add?+# XXX don't complete mutually exclusive options?+# XXX don't complete repeat options? (bash)+# XXX don't do anything post -- (bash)+# XXX handle option arguments (zsh)++my %ZSH_DESCRIPTIONS = (+ '--ackrc' => 'specify an alternative ackrc',+ '--after-context' => 'print NUM lines of trailing context after matching lines',+ '--bar' => 'check with the admiral for traps',+ '--before-context' => 'print NUM lines of leading context before matching lines',+ '--break' => 'print a break between results for different files',+ '--cathy' => 'chocolate, chocolate, chocolate!' ,+ '--color' => 'highlight matching text',+ '--color-filename' => 'set color for filenames',+ '--color-lineno' => 'set color for line numbers',+ '--color-match' => 'set color for matches',+ '--column' => 'show column number of first match',+ '--context' => 'print NUM lines of context around matching lines',+ '--count' => 'print a count of matching lines for each input file',+ '--create-ackrc' => 'dumps default ack options to standard output',+ '--dump' => 'writes the list of loaded options and where they came from',+ '--files-from' => 'specifies the list of files to search within FILE',+ '--files-with-matches' => 'only print filenames of matching files',+ '--files-without-matches' => 'only print filenames of non-matching files',+ '--filter' => 'force ack to behave as if it were receiving input via a pipe',+ '--flush' => 'flushes output immediately',+ '--follow' => 'follow symlinks',+ '--group' => 'group matches by filename',+ '--heading' => q{print a filename heading above each file's result},+ '--help' => 'displays help',+ '--help-types' => 'print all known types',+ '--ignore-ack-defaults' => 'ignore default definitions provided with ack',+ '--ignore-case' => 'ignore case in matches',+ '--ignore-directory' => 'ignore directories (at any level) named DIRNAME',+ '--ignore-file' => 'ignore files matching FILTERTYPE:FILTERARGS',+ '--invert-match' => 'select non-matching lines',+ '--lines' => 'print only line NUM of each file',+ '--literal' => 'quote all metacharacters in PATTERN',+ '--man' => 'displays the ack man page',+ '--match' => 'specify PATTERN explicitly',+ '--max-count' => 'stop reading after NUM matches',+ '--no-filename' => q{don't print filenames on output},+ '--noenv' => q{don't consider ackrc files/environment variables for configuration},+ '--output' => 'output the evaluation of EXPR for each line',+ '--pager' => q{direct ack's output through PAGER},+ '--passthru' => 'print all lines, whether or not they match the expression, highlighting matches',+ '--print0' => 'separate output lines with NUL characters',+ '--recurse' => 'recurse into subdirectories',+ '--show-types' => 'outputs the filetypes that ack associates with each file',+ '--smart-case' => 'ignores case in search strings if PATTERN contains no uppercase characters',+ '--sort-files' => 'sorts found files lexicographically',+ '--thpppt' => 'display the Bill The Cat logo.',+ '--type-add' => 'add a type definition',+ '--type-del' => 'removes a type definition',+ '--type-set' => 'add a type definition',+ '--version' => 'displays version and copyright information',+ '--with-filename' => 'print the filename for each match',+ '--word-regexp' => 'force PATTERN to match only whole words',+ '-1' => 'stops after reporting first match',+ '-f' => 'only print files that would be searched',+ '-g' => 'only print files whose names match PATTERN',+ '-o' => 'only print the part of each matching line that matches PATTERN',+ '-s' => 'suppress error messages about nonexistent or unreadable files',+ '-x' => 'search files specified on standard input',+);++my %OPTION_ALIASES = (+ '--after-context' => ['-A'],+ '--before-context' => ['-B'],+ '--context' => ['-C'],+ '--count' => ['-c'],+ '--no-filename' => ['-h'],+ '--with-filename' => ['-H'],+ '--files-with-matches' => ['-l'],+ '--files-without-matches' => ['-L'],+ '--max-count' => ['-m'],+ '--no-recurse' => ['-n'],+ '--help' => ['-?'],+ '--ignore-case' => ['-i'],+ '--literal' => ['-Q'],+ '--recurse' => ['-r', '-R'],+ '--invert-match' => ['-v'],+ '--word-regexp' => ['-w'],+ '--color' => ['--colour'],+ '--ignore-directory' => ['--ignore-dir'],+);++my %IS_AN_ALIAS = map { $_ => 1 } map { @$_ } values %OPTION_ALIASES;++my $BASH_TEMPLATE = <<'END_TEMPLATE';+declare -g -a _ack_options+declare -g -a _ack_types=()++_ack_options=(+[% FOREACH option IN options -%]+ "[% option -%]" \+[% END -%]+)++function __setup_ack() {+ local type++ while read LINE; do+ case $LINE in+ --*)+ type="${LINE%% *}"+ type=${type/--\[no\]/}+ _ack_options[ ${#_ack_options[@]} ]="--$type"+ _ack_options[ ${#_ack_options[@]} ]="--no$type"+ _ack_types[ ${#_ack_types[@]} ]="$type"+ ;;+ esac+ done < <(ack --help-types)+}+__setup_ack+unset -f __setup_ack++function _ack_complete() {+ local current_word+ local pattern++ current_word=${COMP_WORDS[$COMP_CWORD]}++ if [[ "$current_word" == -* ]]; then+ pattern="${current_word}*"+ for option in ${_ack_options[@]}; do+ if [[ "$option" == $pattern ]]; then+ COMPREPLY[ ${#COMPREPLY[@]} ]=$option+ fi+ done+ else+ local previous_word+ previous_word=${COMP_WORDS[$(( $COMP_CWORD - 1 ))]}+ if [[ "$previous_word" == "=" ]]; then+ previous_word=${COMP_WORDS[$(( $COMP_CWORD - 2 ))]}+ fi++ if [ "$previous_word" == '--type' -o "$previous_word" == '--notype' ]; then+ pattern="${current_word}*"+ for type in ${_ack_types[@]}; do+ if [[ "$type" == $pattern ]]; then+ COMPREPLY[ ${#COMPREPLY[@]} ]=$type+ fi+ done+ fi+ fi+}++complete -o default -F _ack_complete ack ack2 ack-grep+END_TEMPLATE++my $ZSH_TEMPLATE = <<'END_TEMPLATE';+#compdef ack ack2 ack-grep++declare -a ack_types+declare -a arguments+local type++arguments=( \+[% FOREACH option IN options -%]+ [% IF option != '--type' && option != '--notype' && !is_alias.exists(option) -%]+ [% FILTER collapse -%]+ [%- IF aliases.exists(option) -%]+{[% option -%],[% aliases.$option.join(',') -%]}+ [%- ELSE -%]+"[% option -%]"+ [%- END -%]+ [%- IF descriptions.exists(option) -%]+"[[% descriptions.$option -%]]"+ [%- END -%]+ [% END -%]+ \+ [% END -%]+[% END -%]+)++while read LINE; do+ case $LINE in+ --*)+ type="${LINE%% *}"+ type=${type/--\[no\]/}+ arguments[$(( ${#arguments[@]} + 1 ))]="--${type}[restrict to files of type $type]"+ arguments[$(( ${#arguments[@]} + 1 ))]="--no${type}[restrict to files other than type $type]"+ ack_types[$(( ${#ack_types[@]} + 1 ))]="$type"+ ;;+ esac+done < <(ack --help-types)++arguments[$(( ${#arguments[@]} + 1 ))]="--type=[restrict to files of given type]:filetype:(${ack_types[@]})"+arguments[$(( ${#arguments[@]} + 1 ))]="--notype=[restrict to files other than given type]:notfiletype:(${ack_types[@]})"+arguments[$(( ${#arguments[@]} + 1 ))]="*:files:_files"++_arguments -S $arguments+END_TEMPLATE++my $tt = Template->new({});++# XXX do a sanity check on ZSH_DESCRIPTIONS vs get_options, OPTION_ALIASES++my $vars = {+ options => [ get_options() ],+ descriptions => \%ZSH_DESCRIPTIONS,+ aliases => \%OPTION_ALIASES,+ is_alias => \%IS_AN_ALIAS,+};++my ( $filename ) = @ARGV;++if ( !$filename ) {+ die "usage: $0 (completion.bash|completion.zsh)\n";+}+elsif ( $filename eq 'completion.bash' ) {+ $tt->process(\$BASH_TEMPLATE, $vars, 'completion.bash') || die $tt->error;+}+elsif ( $filename eq 'completion.zsh' ) {+ $tt->process(\$ZSH_TEMPLATE, $vars, 'completion.zsh') || die $tt->error;+}+else {+ die "I don't know how to generate $filename\n";+}
dev/high-level-principles.md
@@ -0,0 +1,81 @@+# Usage principles of ack 2.0 that stay the same++ack will still:++* have a system for recognizing types of files, to allow including and excluding certain types of files.++* ignore version control directories, core dumps and other large files++* never be a file editor++* support a local ~/.ackrc++* sniff shebang lines to detect language++* try to be as switch-compatible with GNU grep as possible+++# Definite new features in ack 2.0++ack 2.0 will:++* by default search all text files, as identified by Perl's -T operator+ * We will no longer have a `-a` switch.++* improved flexibility in defining filetypes+ * glob-style matching (`*.pl` identifies a Perl file)+ * regex-style matching (`/\.pl$/i` identifies a Perl file)+ * shebang-line matching (shebang line matching `/usr/bin/perl/` identifies a Perl file)++* support for multiple ackrc files+ * global ackrc (/etc/ackrc)+ * https://github.com/petdance/ack/issues/#issue/79+ * user-specific ackrc (~/.ackrc)+ * per-project ackrc files (~/myproject/ackrc)++* all inclusion/exclusion rules will be in the ackrc files+ * ack 2.0 will have no hardcoded filetype specifications,+ directories to include or exclude, and so on. It will ship+ with a default global ackrc, but does not need it.+++# Continuing design principles of ack 2.0++ack will still:++* run on Perl 5.8.8++* be distributed through CPAN++* be distributed as a standalone+ * No modules other than File::Next can be required except for what comes with core Perl.+ * Plugins can require whatever they want, like Archive::Tar++* be purely Perl 5, and use Perl 5 regular expressions++* be taint-safe++* run on Windows as well as Linux/Unix/etc++* use Perl's default file-handling as far as dealing with files of+different encodings++* be configured entirely from the command-line. ackrc files will+still be merely collections of command-line switches.+++# Features that may get added in ack 2.1+, but definitely not in 2.0++* allow filetype-based plugins for searching arbitrary filetypes,+such as PDFs or Excel files++* add an --also-in-file for matching two matches in the same file+ * https://github.com/petdance/ack/issues#issue/153++* have profiles that are collections of arguments, so you can say+--profile=web, --profile=programming, --profile=sysadmin or whatever.++* Allow file-wide type detection, instead of just looking at just+the shebang.++* Multiple ackrc files in a location, like `/etc/ackrc.d/`
dev/ideas.md
@@ -0,0 +1,37 @@+# Match Types (inclusion)++- firstline+ - shebang+ - Vim modeline (first couple of lines?)+ - Emacs modeline (first couple of lines?)+- extension+ - .pl+ - .tar.gz (complex extensions)+- direct match+ - case sensitive/insensitive+- pattern?+- combined rules:+ - Ex. Prolog files also have a .pl extension, so we should check extension and contents, maybe+- unknown type?+ - Does this match extensionless files (see questions.md)+ - Or just files that don't match any other patterns?++# Match Types (exclusion)++- Exclude a set of directory trees (CVS, .svn, .git, .hg, \_darcs)+- Exclude a set of files (.\*~)++# Configuration Files++- /etc/ackrc, ~/.ackrc, look up directory hierarchy for .ackrcs? (the last one is probably for 2.1)+- Option to suppress loading of various ack rc files (--no-system-rc, --no-home-rc, or something)+ - If we did, this, could you put --no-system-rc in ~/.ackrc?++# Bikeshedding++- A debugging switch to indicate which option/pattern came from where+ - Multiple ack configuration files+ - ACK\_OPTS+ - command line+- Command line option to remove a rule from a file type:+ - Ex. I don't want to treat .t files as Perl in a given project: --type-remove=perl,ext,t
@@ -0,0 +1,41 @@+#!/usr/bin/env perl++# This script is intended for Ack maintainers; we *really* like our items+# in the issue tracker to be properly labelled and have milestones. So+# this script finds any that don't satisfy both criteria and lets the+# maintainer know.++use strict;+use warnings;+use feature 'say';++use Net::GitHub;++sub retrieve_issues {+ my $gh = Net::GitHub->new;+ $gh->set_default_user_repo('petdance', 'ack2');+ my $issue = $gh->issue;+ my @issues = $issue->repos_issues( { state => 'open', per_page => 100 });++ while($issue->has_next_page) {+ push @issues, $issue->next_page;+ }++ return @issues;+}++my @issues = retrieve_issues();++foreach my $issue (@issues) {+ my $is_pull_request = defined($issue->{'pull_request'}{'html_url'});++ next if $is_pull_request;++ my $labels = $issue->{'labels'};+ my $milestone = $issue->{'milestone'};+ my $url = $issue->{'html_url'};+ my $number = $issue->{'number'};++ say "Issue #$number has no labels! ($url)" unless @{$labels};+ say "Issue #$number has no milestone! ($url)" unless $milestone;+}
dev/notes.md
@@ -0,0 +1,21 @@+# Questions++* Should resources be "sources"?++# Tasks++* Filtering for directories++* Filtering for files++* Disallow types that begin with underscore, because it will blow up my hash++* Error reporting on arguments has to specify where the argument+is. If there's an error in the /etc/ackrc, we need to say that,+vs. on the command line.++* Clarify the -f/-g relationship++# Gotchas++* ack -A --type-set=perl,ext,pl,pm,t 15 foo gets treated as ack -A 15 --type-set=perl,ext,pl,pm,t foo; this is confusing.
@@ -0,0 +1,103 @@+# Matching multiple interpreters on the shebang line (2011-08-16)++**Q:** From what I understand, shebang matches look for a regular expression+like this:++ m|^#!/usr/bin/(?<interpeter>\w+)|++and looks up `$+{'interpreter'}` in a hash for its corresponding+language (or some similar algorithm). What if the shebang line is+`#!perl`? Or `#!/usr/bin/env perl`?++**A:** The rules for filetypes are all going to be defined in one of the various `ackrc` files.++For example, here are config lines for detecting Perl files:++ --type-add=perl,ext,pod+ --type-add=perl,ext,pl+ --type-add=perl,ext,pm+ --type-add=perl,firstlinematch,/perl/++In this case, the `firstlinematch` type says "If the first line of+the file matches the regex `/perl/` (where the slashes are regex+delimiters, not part of a path we expect to match).++# Extensionless files (2011-08-16)++**Q:** Will it be possible with ack2, to match extension-less files? Ex.+I'd like to match the file `groceries`, but not `groceries.txt`.++**A:** Yes, definitely. As with checking the shebang lines, the rules are in the `ackrc` file.++Here are sample lines for matching a makefile:++ --type-add=make,ext,mk+ --type-add=make,ext,mak+ --type-add=make,is,makefile+ --type-add=make,is,gnumakefile++These rules would match the files `foo.mk`, `foo.mak` and `makefile`,+but would not match `makefile.in` or `test.makefile`.++**Q:** What if I want to search all files in a directory structure with no extension? Can+I do `--type-add=plain,ext,`?++**A:** In ack 2.0, the default will be to search all text files.+Filetypes only come into it if you want to exclude or include by+type.++That said, I'm wondering if we need a meta-type of "unknown".++# FatPacker (2011-08-16)++**Q:** Have you looked into using FatPacker at all? I don't see+anything wrong with your squash system, but people are already+familiar with FatPacker.++**A:** I know of it, but unless there's benefit in using it over+the existing squash, I don't want to use it. The only external+dependency ack should have is on File::Next.++# More Questions (2011-11-04)++**Q:** When adding a filetype to match a file by extension, regex, or name,+is this comparison case-insenstive? (I'm assuming yes)++**A:** I think that we have to go with case-insensitive, yes. I+think anything that would rely on something other than that is just+asking for disaster.++**Q:** How about a --check option to just validate that your ackrc is ok?++**A:** Yes, please.++**Q:** Some people might want to install ack via cpan, and sometimes in a perlbrew. Maybe+another "global" location is needed for ackrc in cases like these? (see File::ShareDir)++**A:** We have to have one ackrc to start with. I don't think we+want it as a compile-time option or anything like that. It has to+be /etc/ackrc, and then overrideable via some environment option.++**Q:** Should we worry about character encodings in filenames when adding file types? I'm thinking+this might be something for Ack 2.1.++**A:** No, leave for later.++**Q:** Should --noenv be allowed in config files/ACK\_OPTIONS (ex. if I want to skip my .ackrc, or /etc/ackrc)++**A:** No, --noenv should only be allowed on the command line. We+should have a test for that.++**Q:** Should we have a --config option for manually adding config files?++**A:** I can imagine a need, but let's not do it if nobody asks for it.++# Ignore Rules (2011-11-13)++**Q:** When a user (or config file) says --ignore-dir=CVS, I'm assuming Ack ignores ./CVS/, ./foo/CVS/, ./foo/bar/CVS/, etc?++**Q:** When a user (or config file) says --ignore-dir=t/swamp, does Ack ignore ./t/swamp/, ./foo/t/swamp/, ./foo/bar/t/swamp/, etc?++**Q:** When a user (or config file) says --ignore-file=foo.txt, does Ack ignore ./foo.txt, ./bar/foo.txt, etc?++**Q:** When a user (or config file) says --ignore-file=docs/foo.txt, does Ack ignore ./docs/foo.txt, ./bar/docs/foo.txt, etc?
dev/roadmap.md
@@ -0,0 +1,22 @@+# Ack2 Roadmap++# All options have to be able to say where they came from++# Get ack -f working++* Get option-handling working+* Implement file-finding object+* Bring over -f tests+* Make all -f tests pass+* Inclusion of dirs+* Exclusion of dirs+* Inclusion of files+* Exclusion of files++# Implement search functionality (or bring it over from Ack1)++* Create file/resource objects++## Make sure all previous command options are supported (this clearly needs fleshing out)++## Add --check option (for verifying that your ackrc is OK)
@@ -0,0 +1,284 @@+#!/usr/bin/env perl++use strict;+use warnings;+use autodie;+use 5.10.0;++use Getopt::Long;+use File::Slurp qw(read_dir read_file write_file);+use File::Spec;+use JSON;+use List::MoreUtils qw(any);+use Term::ANSIColor qw(colored);+use Time::HiRes qw(gettimeofday tv_interval);++my $SOURCE_DIR = File::Spec->catdir($ENV{HOME}, 'parrot');++unless ( -d $SOURCE_DIR ) {+ die "Expecting to find parrot in $SOURCE_DIR - get it from github.com/parrot/parrot";+}++sub grab_versions {+ my @acks = @_;++ my @annotated_acks;++ foreach my $ack (@acks) {+ my $version;++ if($ack =~ /standalone/) {+ $version = 'HEAD';+ }+ else {+ my $output = `$^X $ack --noenv --version 2>&1`;+ if($output =~ /ack\s+(?<version>\d+[.]\d+(_\d+)?)/) {+ $version = $+{'version'};+ } else {+ # XXX uh-oh+ }+ }++ push @annotated_acks, {+ path => $ack,+ version => $version,+ };++ if($version eq 'HEAD') {+ $annotated_acks[-1]{'store_timings'} = 1;+ }+ }++ return @annotated_acks;+}++sub create_format {+ my ( $invocations, $acks, $show_colors ) = @_;++ my $max_invocation_length = -1;++ foreach my $invocation (@$invocations) {+ my $length = length(join(' ', 'ack', @$invocation));+ if($length > $max_invocation_length) {+ $max_invocation_length = $length;+ }+ }++ my @max_version_lengths = (length(color('000.00'))) x @$acks;++ for(0..$#$acks) {+ if(length($acks->[$_]{'version'}) > $max_version_lengths[$_]) {+ $max_version_lengths[$_] = length($acks->[$_]{'version'});+ }+ }++ return join(' | ', "%${max_invocation_length}s", map {+ "%${_}s"+ } @max_version_lengths) . "\n";+}++my $num_iterations = 1;++sub time_ack {+ my ( $ack, $invocation, $perl ) = @_;++ my @args = ( $perl, $ack->{'path'}, '--noenv', @$invocation );++ if ( $ack->{'path'} =~ /ack-1/ ) {+ @args = grep { !/--known/ } @args;+ }++ my $end;+ my $start = [gettimeofday()];+ for ( 1 .. $num_iterations ) {+ my ( $read, $write );+ pipe $read, $write;+ my $pid = fork;++ my $has_error_lines;++ if($pid) {+ close $write;+ while(<$read>) {+ $has_error_lines = 1;+ }+ waitpid $pid, 0;+ return if $has_error_lines;+ } else {+ close $read;+ open STDOUT, '>', File::Spec->devnull;+ open STDERR, '>&', $write;+ exec @args;+ exit 255;+ }+ }+ $end = [gettimeofday()];++ return tv_interval($start, $end) / $num_iterations;+}++my $show_colors;++sub color {+ my ( $previous_value, $value );++ if ( @_ == 2 ) {+ ( $previous_value, $value ) = @_;+ }+ else {+ ( $value ) = @_;+ }++ return $value if !$show_colors;+ return $value if !defined($value);++ return colored(['white'], $value) if !defined($previous_value);++ if ( $previous_value < $value ) {+ return colored(['red'], $value);+ }+ else {+ return colored(['green'], $value);+ }+}++my @invocations = (+ # normal mode+ [ 'foo', $SOURCE_DIR ],+ [ 'foo', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '--known', $SOURCE_DIR ],++ # -f+ [ '-f', $SOURCE_DIR ],+ [ '-f', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ '-f', '--rust', $SOURCE_DIR ], # where there are little/none+ [ '-f', '--known', $SOURCE_DIR ],++ # -l+ [ 'foo', '-l', $SOURCE_DIR ],+ [ 'foo', '-l', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '-l', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '-l', '--known', $SOURCE_DIR ],++ # -c+ [ 'foo', '-c', $SOURCE_DIR ],+ [ 'foo', '-c', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '-c', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '-c', '--known', $SOURCE_DIR ],++ # -A+ [ 'foo', '-A10', $SOURCE_DIR ],+ [ 'foo', '-A10', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '-A10', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '-A10', '--known', $SOURCE_DIR ],++ # -B+ [ 'foo', '-B10', $SOURCE_DIR ],+ [ 'foo', '-B10', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '-B10', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '-B10', '--known', $SOURCE_DIR ],++ # -C+ [ 'foo', '-C10', $SOURCE_DIR ],+ [ 'foo', '-C10', '--cc', $SOURCE_DIR ], # where there are a lot of matches+ [ 'foo', '-C10', '--rust', $SOURCE_DIR ], # where there are little/none+ [ 'foo', '-C10', '--known', $SOURCE_DIR ],+);++my $perform_store;+my $perfom_clear;+my @use_acks;+my $perl = $^X;++GetOptions(+ 'clear' => \$perfom_clear,+ 'store' => \$perform_store,+ 'color' => \$show_colors,+ 'times=i' => \$num_iterations,+ 'ack=s@' => \@use_acks,+ 'perl=s' => \$perl,+);++if($perfom_clear) {+ unlink('.timings.json');+}++my $json = JSON->new->utf8->pretty;+my $previous_timings;+if(-e '.timings.json') {+ $previous_timings = $json->decode(scalar(read_file('.timings.json')));+}++my @acks = map { File::Spec->catfile('garage', $_) } read_dir('garage');+push @acks, 'ack-standalone';+@acks = grep { !/_/ } @acks; # Skip dev versions++@acks = grab_versions(@acks);+if(@use_acks) {+ foreach my $ack (@acks) {+ next if $ack->{'version'} eq 'HEAD';+ next if $ack->{'version'} eq 'previous';+ unless(any { $_ eq $ack->{'version'} } @use_acks) {+ undef $ack;+ }+ }+ @acks = grep { defined() } @acks;+}+@acks = sort {+ return 1 if $a->{'version'} eq 'HEAD';+ return -1 if $b->{'version'} eq 'HEAD';+ return eval($a->{'version'}) + 0 <=> eval($b->{'version'}) + 0;+} @acks;++if($previous_timings) {+ splice @acks, -1, 0, {+ version => 'previous',+ };+}++my $format = create_format(\@invocations, \@acks, $show_colors);+my $header = sprintf $format, '', map { color($_->{'version'}) } @acks;+print $header;+print '-' x (length($header) - 1), "\n"; # -1 for the newline++my %stored_timings;++foreach my $invocation (@invocations) {+ my @timings;++ my $previous_timing;++ foreach my $ack (@acks) {+ my $elapsed;++ if($ack->{'path'}) {+ $elapsed = time_ack($ack, $invocation, $perl);+ } else {+ $elapsed = $previous_timings->{join(' ', 'ack', @$invocation)};+ }++ if(defined $elapsed) {+ $elapsed = sprintf('%.2f', $elapsed);+ }+ push @timings, color($previous_timing, $elapsed);+ $previous_timing = $elapsed if defined $elapsed;++ if($perform_store && $ack->{'store_timings'}) {+ $stored_timings{join(' ', 'ack', @$invocation)} = $elapsed;+ }+ }+ printf $format, join(' ', 'ack', @$invocation), map { defined() ? $_ : color('x_x') } @timings;+}++if($perform_store) {+ write_file('.timings.json', $json->encode(\%stored_timings));+}++__DATA__++TODO:++ * Percentage slowdown per invocation+ * Overall stats dump at the end.+ * Stop passing bad options to 1.x (--rust, --known)
@@ -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;
garage/ack-2.02
@@ -0,0 +1,4433 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++use strict;+use warnings;++use 5.008;+++# XXX Don't make this so brute force+# See also: https://github.com/petdance/ack2/issues/89++use Getopt::Long 2.36 ();++use Carp 1.04 ();++our $VERSION = '2.02';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ App::Ack::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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ my %is_member_of_starting_set = map { (App::Ack::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{ App::Ack::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;++ foreach my $filter (@ifiles_filters) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource || $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource;+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource;+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = App::Ack::filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++sub main {+ my @arg_sources = App::Ack::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} ) {+ $opt->{color} = !App::Ack::output_to_pipe() && !$App::Ack::is_windows;+ }+ 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} = App::Ack::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} = App::Ack::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;++ App::Ack::iterate($resource, $opt, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ }+ elsif ( $passthru ) {+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ }+ return 1;+ });+ }+ elsif ( $opt->{count} ) {+ my $matches_for_this_file = App::Ack::count_matches_in_resource( $resource, $opt );++ 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 = App::Ack::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 += App::Ack::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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> 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<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are seperated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were recieving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=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?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head1 ACKRC LOCATION SEMANTICS++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++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems, and+C<C:\Documents and Settings\All Users\Application Data> on Windows.+This can be omitted using C<--noenv>.++=item * User ackrc++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>. If a different+ackrc is desired, it may be overriden with the C<$ACKRC> environment+variable.+This can be omitted using C<--noenv>.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * ACK_OPTIONS++Options are then loaded from the enviroment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by constrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-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.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.10';++++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 @_ },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $filter = $parms->{file_filter};++ return sub {+ while (@queue) {+ my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );+ if ( -f $fullpath || -p $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;+ }+ elsif ( -d _ ) {+ unshift( @queue, _candidate_files( $parms, $fullpath ) );+ }+ } # while++ return;+ }; # iterator+}+++++++sub from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{error_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!" );+ return undef;+ }+ }+ my $filter = $parms->{file_filter};++ 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;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!" );+ 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( $dirname, $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, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;++use Getopt::Long 2.36 ();+++our $VERSION;+our $GIT_REVISION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.02';+ $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';+ $GIT_REVISION = q{f3c8827};+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();+use File::Glob 1.00015 ':glob';++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}+++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 = 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;+}++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;+}+++sub create_ignore_rules {+ my $what = shift;+ my $where = shift;+ my $opts = shift;++ my @opts = @{$opts};++ my %rules;++ for my $opt ( @opts ) {+ if ( $opt =~ /^(is|ext|regex),(.+)$/ ) {+ my $method = $1;+ my $arg = $2;+ if ( $method eq 'regex' ) {+ push( @{$rules{regex}}, qr/$arg/ );+ }+ else {+ ++$rules{$method}{$arg};+ }+ }+ else {+ App::Ack::die( "Invalid argument for --$what: $opt" );+ }+ }++ return \%rules;+}+++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++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 $ok = eval {+ qr/$str/+ };++ my $error = $@;++ if ( !$ok ) {+ die "Invalid regex '$str':\n $error";+ }++ 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 show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after matching+ lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before matching+ lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without searching.+ The PATTERN must not be specified.+ -g Same as -f, but only select files matching PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/Remove directory from the 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 (ack's default behavior)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files with 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 of type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore the default definitions that ack includes.+ --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++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 OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';++ return <<"END_OF_VERSION";+ack ${VERSION}${git_revision}+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 1.10 ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}+++# print subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++{++my @capture_indices;+my $match_column_number;++sub does_match {+ my ( $opt, $line ) = @_;++ my $re = $opt->{regex};+ my $invert = $opt->{v};++ return unless $re;++ $match_column_number = undef;+ @capture_indices = ();++ if ( $invert ? $line !~ /$re/ : $line =~ /$re/ ) {+ if ( not $invert ) {+ # @- = @LAST_MATCH_START+ # @+ = @LAST_MATCH_END+ $match_column_number = $-[0] + 1;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ }+ return 1;+ }+ else {+ return;+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++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;++ App::Ack::iterate($resource, $opt, sub {+ if ( App::Ack::does_match($opt, $_) ) {+ if( !$has_printed_for_this_resource ) {+ if( $break && has_printed_something() ) {+ App::Ack::print_blank_line();+ }+ if( $print_filename) {+ if( $heading ) {+ my $filename = $resource->name;+ if($color) {+ $filename = Term::ANSIColor::colored($filename,+ $ENV{ACK_COLOR_FILENAME});+ }+ App::Ack::print_filename( $filename, $ors );+ }+ }+ }+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ elsif ( $passthru ) {+ chomp;+ if( $break && !$has_printed_for_this_resource && has_printed_something() ) {+ App::Ack::print_blank_line();+ }+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ $has_printed_for_this_resource = 1;+ }+ return $max_count != 0;+ });++ return $nmatches;+}++sub count_matches_in_resource {+ my ( $resource, $opt ) = @_;++ my $nmatches = 0;++ App::Ack::iterate( $resource, $opt, sub {+ ++$nmatches if App::Ack::does_match($opt, $_);+ return 1;+ } );++ return $nmatches;+}++sub resource_has_match {+ my ( $resource, $opt ) = @_;++ return count_matches_in_resource($resource, $opt) > 0;+}++{++my @before_ctx_lines;+my @after_ctx_lines;+my $is_iterating;++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,+ );+}++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;+ my $current_line;++ @after_ctx_lines = @before_ctx_lines = ();++ if ( $resource->next_text() ) {+ $current_line = $_; # prime the first line of input+ }++ while ( defined $current_line ) {+ while ( (@after_ctx_lines < $n_after_ctx_lines) && $resource->next_text() ) {+ 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++ push @before_ctx_lines, $current_line;+if($n_after_ctx_lines) {+ $current_line = shift @after_ctx_lines;+ }+ elsif($resource->next_text()) {+ $current_line = $_;+ }+ else {+ undef $current_line;+ }+ shift @before_ctx_lines while @before_ctx_lines > $n_before_ctx_lines;+ }++ $is_iterating = 0; # XXX this won't happen on an exception+ # then again, do we care? ack doesn't really+ # handle exceptions anyway.++ return;+}++}++my $has_printed_something;++BEGIN {+ $has_printed_something = 0;+}++sub has_printed_something {+ return $has_printed_something;+}++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 $re = $opt->{regex};+ 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 ) {+ # XXX avoid re-evaluation if we can+ while( $line =~ /$re/g ) {+ my $output = eval $output_expr;+ App::Ack::print( join( $separator, @line_parts, $output ), $ors );+ }+ }+ else {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ elsif( $color ) {+ # XXX I know $& is a no-no; fix it later+ if($line =~ s/$re/Term::ANSIColor::colored($&, $ENV{ACK_COLOR_MATCH})/ge) {+ $line .= "\033[0m\033[K";+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++{++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;+ }+ }++ my $ors = $opt->{print0} ? "\0" : "\n";+ my $match_word = $opt->{w};+ my $re = $opt->{regex};+ my $is_tracking_context = $opt->{after_context} || $opt->{before_context};+ my $output_expr = $opt->{output};++ chomp $matching_line;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ App::Ack::print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ App::Ack::print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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 = App::Ack::does_match( $opt, $line ) ? ':' : '-';+ App::Ack::print_line_with_options($opt, $filename, $line, $. + $offset, $separator);+ $previous_line_printed = $. + $offset;+ $offset++;+ }+ }++ $is_first_match = 0;++ return;+}++}++# inefficient, but functional+sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %mappings) {+ my $filters = $mappings{$k};++ foreach my $filter (@{$filters}) {+ # clone the resource+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ return sort @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 ( $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;+ }+ }+}++sub create_ackrc {+ print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() );+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ return FAIL();+}+++sub name {+ return FAIL();+}+++sub is_binary {+ return FAIL();+}++++sub needs_line_scan {+ return FAIL();+}+++sub reset {+ return FAIL();+}+++sub next_text {+ return FAIL();+}+++sub close {+ return FAIL();+}+++sub clone {+ return FAIL();+}++1;+package App::Ack::Resources;++++use warnings;+use strict;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => 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;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource::Basic->new( $file );+}++1;+package App::Ack::Resource::Basic;+++use warnings;+use strict;++BEGIN {+ our @ISA = 'App::Ack::Resource';+}+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => undef,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ }+ else {+ if ( !open( $self->{fh}, '<', $self->{filename} ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return;+ }+ }++ return $self;+}+++sub name {+ my $self = shift;++ return $self->{filename};+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++sub next_text {+ if ( defined ($_ = readline $_[0]->{fh}) ) {+ $. = ++$_[0]->{line};+ s/[\r\n]+$//; # chomp may not handle this+ $_ .= "\n"; # add back newline (XXX make it native)+ return 1;+ }++ return;+}+++sub close {+ my $self = shift;++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ return;+}+++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}+++1;+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,+ }, $class;+}++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 $buffer;+ my $rc = sysread( $resource->{fh}, $buffer, 250 );+ return unless $rc;+ $buffer =~ s/[\r\n].*//s;++ return $buffer =~ /$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,+ }, $class;+}++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'};+}++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::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++BEGIN {+ if ($App::Ack::is_windows) {+ require Win32;+ }+}+++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}++sub _remove_redundancies {+ my ( @configs ) = @_;++ 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 );+}++1;+package App::Ack::ConfigLoader;++use strict;+use warnings;++use Carp 1.04 ();+use Getopt::Long 2.36 ();+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 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;+ }++ 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+ Getopt::Long::GetOptionsFromArray($args, %type_arg_specs);+ }+ 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).+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 { App::Ack::create_ackrc(); 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' => \$opt->{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};++ Getopt::Long::Configure('pass_through');++ Getopt::Long::GetOptionsFromArray( \@copy,+ '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) ) {+ $ret = Getopt::Long::GetOptionsFromArray( $args, %{$arg_specs} );+ }+ 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;+ Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');+ Getopt::Long::GetOptionsFromArray($options,+ 'dump' => \$dump,+ );+ 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;+ Getopt::Long::GetOptionsFromArray(\@chunk,+ 'type-add=s' => $add_type,+ 'type-set=s' => $add_type,+ 'type-del=s' => $del_type,+ );+ Getopt::Long::GetOptionsFromArray(\@chunk, %{$arg_spec});++ splice @copy, -1 * @chunk if @chunk; # XXX explain this+ 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;++ 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',+ );++ foreach my $index ( $default_index + 2 .. $#$sources ) {+ next if $index % 2 != 0;++ my ( $name, $args ) = @{$sources}[ $index, $index + 1 ];++ if (ref($args)) {+ Getopt::Long::GetOptionsFromArray($args,+ 'ignore-ack-defaults' => \$should_remove,+ );+ }+ 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;++ 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;+}++1; # End of App::Ack::ConfigLoader+package App::Ack::ConfigDefault;++use warnings;+use strict;++sub options {+ my @options = split( /\n/, _options_block() );+ @options = grep { /./ && !/^#/ } @options;++ return @options;+}++sub _options_block {+ return <<'HERE';+# This is the default ackrc for ack 2.0++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++# Directories to ignore+# Bazaar+--ignore-directory=is:.bzr++# Codeville+--ignore-directory=is:.cdv++# Interface Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+--ignore-directory=is:.git++# Mercurial+--ignore-directory=is:.hg++# quilt+--ignore-directory=is:.pc++# Subversion+--ignore-directory=is:.svn++# Monotone+--ignore-directory=is:_MTN++# CVS+--ignore-directory=is:CVS++# RCS+--ignore-directory=is:RCS++# SCCS+--ignore-directory=is:SCCS++# darcs+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+--ignore-directory=is:cover_db++++# Files to ignore+# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.]min[.]js$/+++# Filetypes defined++# Perl http://perl.org/+--type-add=perl:ext:pl,pm,pod,t+--type-add=perl:firstlinematch:/^#!.*\bperl/++# 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++# Rakefiles http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure http://clojure.org/+--type-add=clojure:ext:clj++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Delphi http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# 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++# Common Lisp http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua http://www.lua.org/+--type-add=lua:ext:lua++# 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++# 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++# 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+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z)?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;
garage/ack-2.03_01
@@ -0,0 +1,4444 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++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.03_01';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ App::Ack::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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ my %is_member_of_starting_set = map { (App::Ack::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{ App::Ack::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;++ foreach my $filter (@ifiles_filters) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource || $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource;+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ my $resource = App::Ack::Resource::Basic->new($File::Next::name);+ return 0 if ! $resource;+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = App::Ack::filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++sub main {+ my @arg_sources = App::Ack::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} ) {+ $opt->{color} = !App::Ack::output_to_pipe() && !$App::Ack::is_windows;+ }+ 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} = App::Ack::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} = App::Ack::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;++ App::Ack::iterate($resource, $opt, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ }+ elsif ( $passthru ) {+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ }+ return 1;+ });+ }+ elsif ( $opt->{count} ) {+ my $matches_for_this_file = App::Ack::count_matches_in_resource( $resource, $opt );++ 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 = App::Ack::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 += App::Ack::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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> 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<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are seperated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were recieving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=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?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head1 ACKRC LOCATION SEMANTICS++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++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems, and+C<C:\Documents and Settings\All Users\Application Data> on Windows.+This can be omitted using C<--noenv>.++=item * User ackrc++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>. If a different+ackrc is desired, it may be overriden with the C<$ACKRC> environment+variable.+This can be omitted using C<--noenv>.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * ACK_OPTIONS++Options are then loaded from the enviroment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by constrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-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.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.12';++++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 @_ },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ 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 from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{error_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!" );+ return undef;+ }+ }+ my $filter = $parms->{file_filter};++ 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;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!" );+ 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( $dirname, $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, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;++use Getopt::Long 2.35 ();+++our $VERSION;+our $GIT_REVISION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.03_01';+ $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';+ $GIT_REVISION = q{7d62916};+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();+use File::Glob 1.00015 ':glob';++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}+++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 = 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;+}++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;+}+++sub create_ignore_rules {+ my $what = shift;+ my $where = shift;+ my $opts = shift;++ my @opts = @{$opts};++ my %rules;++ for my $opt ( @opts ) {+ if ( $opt =~ /^(is|ext|regex),(.+)$/ ) {+ my $method = $1;+ my $arg = $2;+ if ( $method eq 'regex' ) {+ push( @{$rules{regex}}, qr/$arg/ );+ }+ else {+ ++$rules{$method}{$arg};+ }+ }+ else {+ App::Ack::die( "Invalid argument for --$what: $opt" );+ }+ }++ return \%rules;+}+++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++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;++}+++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 show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after matching+ lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before matching+ lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without searching.+ The PATTERN must not be specified.+ -g Same as -f, but only select files matching PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/Remove directory from the 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 (ack's default behavior)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files with 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 of type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore the default definitions that ack includes.+ --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++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 OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';++ return <<"END_OF_VERSION";+ack ${VERSION}${git_revision}+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 1.10 ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}+++# print subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++{++my @capture_indices;+my $match_column_number;++# does_match() MUST have an $opt->{regex} set.++sub does_match {+ my ( $opt, $line ) = @_;++ $match_column_number = undef;+ @capture_indices = ();++ if ( $opt->{v} ) {+ return ( $line !~ $opt->{regex} );+ }+ else {+ if ( $line =~ $opt->{regex} ) {+ # @- = @LAST_MATCH_START+ # @+ = @LAST_MATCH_END+ $match_column_number = $-[0] + 1;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++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;++ my $matching_sub = sub {+ if ( App::Ack::does_match($opt, $_) ) {+ if( !$has_printed_for_this_resource ) {+ if( $break && has_printed_something() ) {+ App::Ack::print_blank_line();+ }+ if( $print_filename) {+ if( $heading ) {+ my $filename = $resource->name;+ if($color) {+ $filename = Term::ANSIColor::colored($filename,+ $ENV{ACK_COLOR_FILENAME});+ }+ App::Ack::print_filename( $filename, $ors );+ }+ }+ }+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ $has_printed_for_this_resource = 1;+ $nmatches++;+ $max_count--;+ }+ elsif ( $passthru ) {+ chomp;+ if( $break && !$has_printed_for_this_resource && has_printed_something() ) {+ App::Ack::print_blank_line();+ }+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ $has_printed_for_this_resource = 1;+ }+ return $max_count != 0;+ };++ App::Ack::iterate($resource, $opt, $matching_sub );++ return $nmatches;+}++sub count_matches_in_resource {+ my ( $resource, $opt ) = @_;++ my $nmatches = 0;++ App::Ack::iterate( $resource, $opt, sub {+ ++$nmatches if App::Ack::does_match($opt, $_);+ return 1;+ } );++ return $nmatches;+}++sub resource_has_match {+ my ( $resource, $opt ) = @_;++ return count_matches_in_resource($resource, $opt) > 0;+}++{++my @before_ctx_lines;+my @after_ctx_lines;+my $is_iterating;++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,+ );+}++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;+ my $current_line;++ @after_ctx_lines = @before_ctx_lines = ();++ if ( $resource->next_text() ) {+ $current_line = $_; # prime the first line of input+ }++ while ( defined $current_line ) {+ while ( (@after_ctx_lines < $n_after_ctx_lines) && $resource->next_text() ) {+ 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;+ }+ elsif($resource->next_text()) {+ $current_line = $_;+ }+ else {+ undef $current_line;+ }+ }++ $is_iterating = 0; # XXX this won't happen on an exception+ # then again, do we care? ack doesn't really+ # handle exceptions anyway.++ return;+}++}++my $has_printed_something;++BEGIN {+ $has_printed_something = 0;+}++sub has_printed_something {+ return $has_printed_something;+}++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 ) {+ my $re = $opt->{regex};+ while ( $line =~ /$re/og ) {+ my $output = eval $output_expr;+ App::Ack::print( join( $separator, @line_parts, $output ), $ors );+ }+ }+ else {+ if ( $color ) {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ else {+ my $matched = 0; # flag; if matched, need to escape afterwards++ my $re = $opt->{regex};+ while ( $line =~ /$re/og ) {++ $matched = 1;+ my ( $match_start, $match_end ) = ($-[0], $+[0]);++ 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 ));+ }+ $line .= "\033[0m\033[K" if $matched;+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++{++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;+ }+ }++ 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};++ chomp $matching_line;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ App::Ack::print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ App::Ack::print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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} && App::Ack::does_match( $opt, $line )) ? ':' : '-';+ App::Ack::print_line_with_options($opt, $filename, $line, $. + $offset, $separator);+ $previous_line_printed = $. + $offset;+ $offset++;+ }+ }++ $is_first_match = 0;++ return;+}++}++# inefficient, but functional+sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %mappings) {+ my $filters = $mappings{$k};++ foreach my $filter (@{$filters}) {+ # clone the resource+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ return sort @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 ( $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;+ }+ }+}++sub create_ackrc {+ print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() );+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ return FAIL();+}+++sub name {+ return FAIL();+}+++sub is_binary {+ return FAIL();+}++++sub needs_line_scan {+ return FAIL();+}+++sub reset {+ return FAIL();+}+++sub next_text {+ return FAIL();+}+++sub close {+ return FAIL();+}+++sub clone {+ return FAIL();+}++1;+package App::Ack::Resources;++++use warnings;+use strict;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => 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;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource::Basic->new( $file );+}++1;+package App::Ack::Resource::Basic;+++use warnings;+use strict;++BEGIN {+ our @ISA = 'App::Ack::Resource';+}+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => undef,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ }+ else {+ if ( !open( $self->{fh}, '<', $self->{filename} ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return;+ }+ }++ return $self;+}+++sub name {+ return $_[0]->{filename};+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++sub next_text {+ if ( defined ($_ = readline $_[0]->{fh}) ) {+ $. = ++$_[0]->{line};++ # Normalize line endings if we need to+ if ( /\r\n?$/ ) {+ # Strip any trailing \r\n from the line and turn it to \n+ s/[\r\n]+$/\n/;+ }+ return 1;+ }++ return;+}+++sub close {+ my $self = shift;++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ return;+}+++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}+++1;+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,+ }, $class;+}++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 $buffer;+ my $rc = sysread( $resource->{fh}, $buffer, 250 );+ return unless $rc;+ $buffer =~ s/[\r\n].*//s;++ return $buffer =~ /$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,+ }, $class;+}++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'};+}++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::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++BEGIN {+ if ($App::Ack::is_windows) {+ require Win32;+ }+}+++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}++sub _remove_redundancies {+ my ( @configs ) = @_;++ 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 );+}++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 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;+ }++ 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).+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 { App::Ack::create_ackrc(); 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' => \$opt->{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} );++ splice @copy, -1 * @ARGV if @ARGV; # XXX explain this+ 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;++ 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',+ );++ 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;++ 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;+}++1; # End of App::Ack::ConfigLoader+package App::Ack::ConfigDefault;++use warnings;+use strict;++sub options {+ my @options = split( /\n/, _options_block() );+ @options = grep { /./ && !/^#/ } @options;++ return @options;+}++sub _options_block {+ return <<'HERE';+# This is the default ackrc for ack 2.0++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++# Directories to ignore+# Bazaar+--ignore-directory=is:.bzr++# Codeville+--ignore-directory=is:.cdv++# Interface Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+--ignore-directory=is:.git++# Mercurial+--ignore-directory=is:.hg++# quilt+--ignore-directory=is:.pc++# Subversion+--ignore-directory=is:.svn++# Monotone+--ignore-directory=is:_MTN++# CVS+--ignore-directory=is:CVS++# RCS+--ignore-directory=is:RCS++# SCCS+--ignore-directory=is:SCCS++# darcs+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+--ignore-directory=is:cover_db++++# Files to ignore+# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.]min[.]js$/+++# Filetypes defined++# Perl http://perl.org/+--type-add=perl:ext:pl,pm,pod,t+--type-add=perl:firstlinematch:/^#!.*\bperl/++# 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++# Rakefiles http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure http://clojure.org/+--type-add=clojure:ext:clj++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Delphi http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# 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++# Common Lisp http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua http://www.lua.org/+--type-add=lua:ext:lua++# 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++# 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++# 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+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z)?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;
garage/ack-2.03_02
@@ -0,0 +1,4588 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++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.03_02';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ App::Ack::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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ my %is_member_of_starting_set = map { (App::Ack::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{ App::Ack::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;+ foreach my $filter (@ifiles_filters) {+ return 0 if $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = App::Ack::filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++sub main {+ my @arg_sources = App::Ack::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} ) {+ $opt->{color} = !App::Ack::output_to_pipe() && !$App::Ack::is_windows;+ }+ 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} = App::Ack::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} = App::Ack::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;++ App::Ack::iterate($resource, $opt, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ }+ elsif ( $passthru ) {+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ }+ return 1;+ });+ }+ elsif ( $opt->{count} ) {+ my $matches_for_this_file = App::Ack::count_matches_in_resource( $resource, $opt );++ 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 = App::Ack::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 += App::Ack::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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> 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<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are seperated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were recieving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=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?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head1 ACKRC LOCATION SEMANTICS++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++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems, and+C<C:\Documents and Settings\All Users\Application Data> on Windows.+This can be omitted using C<--noenv>.++=item * User ackrc++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>. If a different+ackrc is desired, it may be overriden with the C<$ACKRC> environment+variable.+This can be omitted using C<--noenv>.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * ACK_OPTIONS++Options are then loaded from the enviroment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by constrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-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.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.12';++++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 @_ },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ 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 from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{error_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!" );+ return undef;+ }+ }+ my $filter = $parms->{file_filter};++ 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;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!" );+ 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( $dirname, $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, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;++use Getopt::Long 2.35 ();+++our $VERSION;+our $GIT_REVISION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.03_02';+ $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';+ $GIT_REVISION = q{c731854};+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();+use File::Glob 1.00015 ':glob';++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}+++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 = 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;+}++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;+}+++sub create_ignore_rules {+ my $what = shift;+ my $where = shift;+ my $opts = shift;++ my @opts = @{$opts};++ my %rules;++ for my $opt ( @opts ) {+ if ( $opt =~ /^(is|ext|regex),(.+)$/ ) {+ my $method = $1;+ my $arg = $2;+ if ( $method eq 'regex' ) {+ push( @{$rules{regex}}, qr/$arg/ );+ }+ else {+ ++$rules{$method}{$arg};+ }+ }+ else {+ App::Ack::die( "Invalid argument for --$what: $opt" );+ }+ }++ return \%rules;+}+++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++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;++}+++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 show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after matching+ lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before matching+ lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without searching.+ The PATTERN must not be specified.+ -g Same as -f, but only select files matching PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/Remove directory from the 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 (ack's default behavior)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files with 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 of type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore the default definitions that ack includes.+ --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++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 OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';++ return <<"END_OF_VERSION";+ack ${VERSION}${git_revision}+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 1.10 ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}+++# print subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++{++my @capture_indices;+my $match_column_number;++# does_match() MUST have an $opt->{regex} set.++sub does_match {+ my ( $opt, $line ) = @_;++ $match_column_number = undef;+ @capture_indices = ();++ 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;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++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 ( App::Ack::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 );+ }+ }+ App::Ack::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();+ }+ App::Ack::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 ( App::Ack::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 );+ }+ }+ App::Ack::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();+ }+ App::Ack::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 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}: $!" );+ }+ return 0;+ }++ while ( <$fh> ) {+ my $does_match = /$opt->{regex}/o;+ $does_match = !$does_match if $opt->{v};+ ++$nmatches if $does_match;+ }+ close $fh;++ return $nmatches;+}++sub resource_has_match {+ my ( $resource, $opt ) = @_;++ my $fh = $resource->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ # XXX direct access to filename+ App::Ack::warn( "$resource->{filename}: $!" );+ }+ return 0;+ }++ while ( <$fh> ) {+ my $does_match = /$opt->{regex}/o;+ $does_match = !$does_match if $opt->{v};+ return 1 if $does_match;+ }+ close $fh;++ return 0;+}++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,+ );+}++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 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 ) {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ 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]);++ 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 ));+ }+ $line .= "\033[0m\033[K" if $matched;+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++{++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;+ }+ }++ 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};++ $matching_line =~ s/[\r\n]+$//g;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ App::Ack::print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ App::Ack::print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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} && App::Ack::does_match( $opt, $line )) ? ':' : '-';+ App::Ack::print_line_with_options($opt, $filename, $line, $. + $offset, $separator);+ $previous_line_printed = $. + $offset;+ $offset++;+ }+ }++ $is_first_match = 0;++ return;+}++}++# inefficient, but functional+sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %mappings) {+ my $filters = $mappings{$k};++ foreach my $filter (@{$filters}) {+ # clone the resource+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ return sort @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 ( $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;+ }+ }+}++sub create_ackrc {+ print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() );+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ return FAIL();+}+++sub name {+ return FAIL();+}+++sub is_binary {+ return FAIL();+}+++sub open {+ return FAIL();+}+++sub needs_line_scan {+ return FAIL();+}+++sub reset {+ return FAIL();+}+++sub close {+ return FAIL();+}+++sub clone {+ return FAIL();+}+++sub firstliney {+ return FAIL();+}++1;+package App::Ack::Resources;++++use warnings;+use strict;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => 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;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource::Basic->new( $file );+}++1;+package App::Ack::Resource::Basic;+++use warnings;+use strict;++use Fcntl ();++BEGIN {+ our @ISA = 'App::Ack::Resource';+}+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => 0,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ $self->{opened} = 1;+ }++ return $self;+}+++sub name {+ return $_[0]->{filename};+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++sub close {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ $self->{opened} = 0;++ return;+}+++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}++sub firstliney {+ my ( $self ) = @_;++ my $fh = $self->open();++ 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;+ }++ $self->close;++ return $self->{firstliney};+}++sub open {+ my ( $self ) = @_;++ return $self->{fh} if $self->{opened};++ unless ( open $self->{fh}, '<', $self->{filename} ) {+ return;+ }++ $self->{opened} = 1;++ return $self->{fh};+}++1;+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,+ }, $class;+}++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,+ }, $class;+}++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'};+}++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::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++BEGIN {+ if ($App::Ack::is_windows) {+ require Win32;+ }+}+++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}++sub _remove_redundancies {+ my ( @configs ) = @_;++ 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 );+}++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 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;+ }++ 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).+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 { App::Ack::create_ackrc(); 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' => \$opt->{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} );++ splice @copy, -1 * @ARGV if @ARGV; # XXX explain this+ 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;++ 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',+ );++ 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;++ 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;+}++1; # End of App::Ack::ConfigLoader+package App::Ack::ConfigDefault;++use warnings;+use strict;++sub options {+ my @options = split( /\n/, _options_block() );+ @options = grep { /./ && !/^#/ } @options;++ return @options;+}++sub _options_block {+ return <<'HERE';+# This is the default ackrc for ack 2.0++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++# Directories to ignore+# Bazaar+--ignore-directory=is:.bzr++# Codeville+--ignore-directory=is:.cdv++# Interface Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+--ignore-directory=is:.git++# Mercurial+--ignore-directory=is:.hg++# quilt+--ignore-directory=is:.pc++# Subversion+--ignore-directory=is:.svn++# Monotone+--ignore-directory=is:_MTN++# CVS+--ignore-directory=is:CVS++# RCS+--ignore-directory=is:RCS++# SCCS+--ignore-directory=is:SCCS++# darcs+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+--ignore-directory=is:cover_db++++# Files to ignore+# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.]min[.]js$/+++# Filetypes defined++# Perl http://perl.org/+--type-add=perl:ext:pl,pm,pod,t+--type-add=perl:firstlinematch:/^#!.*\bperl/++# 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++# Rakefiles http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure http://clojure.org/+--type-add=clojure:ext:clj++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Delphi http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# 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++# Common Lisp http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua http://www.lua.org/+--type-add=lua:ext:lua++# 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++# 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++# 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+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z)?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;
garage/ack-2.03_03
@@ -0,0 +1,4614 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++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.03_03';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }+ App::Ack::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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ my %is_member_of_starting_set = map { (App::Ack::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{ App::Ack::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;+ foreach my $filter (@ifiles_filters) {+ return 0 if $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ return $match_found;+ };+}++sub show_types {+ my $resource = shift;+ my $ors = shift;++ my @types = App::Ack::filetypes( $resource );+ my $types = join( ',', @types );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );++ return;+}++sub main {+ my @arg_sources = App::Ack::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} ) {+ $opt->{color} = !App::Ack::output_to_pipe() && !$App::Ack::is_windows;+ }+ 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} = App::Ack::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} = App::Ack::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;++ App::Ack::iterate($resource, $opt, sub {+ chomp;++ if ( $line_numbers{$.} ) {+ App::Ack::print_line_with_context($opt, $filename, $_, $.);+ }+ elsif ( $passthru ) {+ App::Ack::print_line_with_options($opt, $filename, $_, $., ':');+ }+ return 1;+ });+ }+ elsif ( $opt->{count} ) {+ my $matches_for_this_file = App::Ack::count_matches_in_resource( $resource, $opt );++ 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 = App::Ack::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 += App::Ack::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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> 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<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are seperated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were recieving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=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?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head1 ACKRC LOCATION SEMANTICS++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++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems, and+C<C:\Documents and Settings\All Users\Application Data> on Windows.+This can be omitted using C<--noenv>.++=item * User ackrc++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>. If a different+ackrc is desired, it may be overriden with the C<$ACKRC> environment+variable.+This can be omitted using C<--noenv>.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * ACK_OPTIONS++Options are then loaded from the enviroment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by constrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-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.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.12';++++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 @_ },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ 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 from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{error_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!" );+ return undef;+ }+ }+ my $filter = $parms->{file_filter};++ 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;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!" );+ 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( $dirname, $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, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;++use Getopt::Long 2.35 ();+++our $VERSION;+our $GIT_REVISION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.03_03';+ $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';+ $GIT_REVISION = q{eeb9d44};+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();+use File::Glob 1.00015 ':glob';++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}+++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 = 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;+}++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;+}+++sub create_ignore_rules {+ my $what = shift;+ my $where = shift;+ my $opts = shift;++ my @opts = @{$opts};++ my %rules;++ for my $opt ( @opts ) {+ if ( $opt =~ /^(is|ext|regex),(.+)$/ ) {+ my $method = $1;+ my $arg = $2;+ if ( $method eq 'regex' ) {+ push( @{$rules{regex}}, qr/$arg/ );+ }+ else {+ ++$rules{$method}{$arg};+ }+ }+ else {+ App::Ack::die( "Invalid argument for --$what: $opt" );+ }+ }++ return \%rules;+}+++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++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;++}+++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 show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after matching+ lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before matching+ lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without searching.+ The PATTERN must not be specified.+ -g Same as -f, but only select files matching PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/Remove directory from the 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 (ack's default behavior)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files with 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 of type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore the default definitions that ack includes.+ --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++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 OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';++ return <<"END_OF_VERSION";+ack ${VERSION}${git_revision}+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 1.10 ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}+++# print subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++{++my @capture_indices;+my $match_column_number;++# does_match() MUST have an $opt->{regex} set.++sub does_match {+ my ( $opt, $line ) = @_;++ $match_column_number = undef;+ @capture_indices = ();++ 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;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++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 ( App::Ack::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 );+ }+ }+ App::Ack::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();+ }+ App::Ack::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 ( App::Ack::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 );+ }+ }+ App::Ack::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();+ }+ App::Ack::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 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 resource_has_match {+ my ( $resource, $opt ) = @_;++ 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 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,+ );+}++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 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 ) {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ 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]);++ 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 ));+ }+ $line .= "\033[0m\033[K" if $matched;+ }+ }++ push @line_parts, $line;+ App::Ack::print( join( $separator, @line_parts ), $ors );+ }++ return;+}++{++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;+ }+ }++ 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};++ $matching_line =~ s/[\r\n]+$//g;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ App::Ack::print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ App::Ack::print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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} && App::Ack::does_match( $opt, $line )) ? ':' : '-';+ App::Ack::print_line_with_options($opt, $filename, $line, $. + $offset, $separator);+ $previous_line_printed = $. + $offset;+ $offset++;+ }+ }++ $is_first_match = 0;++ return;+}++}++# inefficient, but functional+sub filetypes {+ my ( $resource ) = @_;++ my @matches;++ foreach my $k (keys %mappings) {+ my $filters = $mappings{$k};++ foreach my $filter (@{$filters}) {+ # clone the resource+ my $clone = $resource->clone;+ if ( $filter->filter($clone) ) {+ push @matches, $k;+ last;+ }+ }+ }++ return sort @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 ( $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;+ }+ }+}++sub create_ackrc {+ print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() );+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ return FAIL();+}+++sub name {+ return FAIL();+}+++sub is_binary {+ return FAIL();+}+++sub open {+ return FAIL();+}+++sub needs_line_scan {+ return FAIL();+}+++sub reset {+ return FAIL();+}+++sub close {+ return FAIL();+}+++sub clone {+ return FAIL();+}+++sub firstliney {+ return FAIL();+}++1;+package App::Ack::Resources;++++use warnings;+use strict;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => 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;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource::Basic->new( $file );+}++1;+package App::Ack::Resource::Basic;+++use warnings;+use strict;++use Fcntl ();++BEGIN {+ our @ISA = 'App::Ack::Resource';+}+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => 0,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ $self->{opened} = 1;+ }++ return $self;+}+++sub name {+ return $_[0]->{filename};+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++sub close {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ $self->{opened} = 0;++ return;+}+++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}++sub firstliney {+ my ( $self ) = @_;++ my $fh = $self->open();++ 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;+ }++ $self->close;++ return $self->{firstliney};+}++sub open {+ my ( $self ) = @_;++ return $self->{fh} if $self->{opened};++ unless ( open $self->{fh}, '<', $self->{filename} ) {+ return;+ }++ $self->{opened} = 1;++ return $self->{fh};+}++1;+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,+ }, $class;+}++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,+ }, $class;+}++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'};+}++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::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++use if ($^O =~ /MSWin32/ ? 1 : 0), "Win32";+++our $is_win = 0;++sub new {+ my ( $class ) = @_;++ $is_win = $^O =~ /MSWin32/,++ return bless {}, $class;+}++sub _remove_redundancies {+ my ( @configs ) = @_;++ if ( $is_win ) {+ # 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( $is_win ) {+ 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 );+}++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 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;+ }++ 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).+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 { App::Ack::create_ackrc(); 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' => \$opt->{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;++ 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',+ );++ 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;++ 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;+}++1; # End of App::Ack::ConfigLoader+package App::Ack::ConfigDefault;++use warnings;+use strict;++sub options {+ my @options = split( /\n/, _options_block() );+ @options = grep { /./ && !/^#/ } @options;++ return @options;+}++sub _options_block {+ return <<'HERE';+# This is the default ackrc for ack 2.0++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++# Directories to ignore+# Bazaar+--ignore-directory=is:.bzr++# Codeville+--ignore-directory=is:.cdv++# Interface Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+--ignore-directory=is:.git++# Mercurial+--ignore-directory=is:.hg++# quilt+--ignore-directory=is:.pc++# Subversion+--ignore-directory=is:.svn++# Monotone+--ignore-directory=is:_MTN++# CVS+--ignore-directory=is:CVS++# RCS+--ignore-directory=is:RCS++# SCCS+--ignore-directory=is:SCCS++# darcs+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+--ignore-directory=is:cover_db++++# Files to ignore+# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.]min[.]js$/+--ignore-file=match:/[.]js[.]min$/++# minified CSS+--ignore-file=match:/[.]min[.]css$/+--ignore-file=match:/[.]css[.]min$/+++# Filetypes defined++# Perl http://perl.org/+--type-add=perl:ext:pl,pm,pod,t+--type-add=perl:firstlinematch:/^#!.*\bperl/++# 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++# Rakefiles http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure http://clojure.org/+--type-add=clojure:ext:clj++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Delphi http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# 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++# Common Lisp http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua http://www.lua.org/+--type-add=lua:ext:lua++# 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++# 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++# 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+--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z)?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;
garage/ack-2.04
@@ -0,0 +1,4591 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++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.04';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ 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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ 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;+ foreach my $filter (@ifiles_filters) {+ return 0 if $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ 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 ()';++ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';++ return;+}++# inefficient, but functional+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;+ }+ }+ }++ return sort @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 undefinied (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 ) {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ 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]);++ 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 ));+ }+ $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;+ }+ }++ 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};++ $matching_line =~ s/[\r\n]+$//g;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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++;+ }+ }++ $is_first_match = 0;++ return;+}++}++{++my @capture_indices;+my $match_column_number;++# does_match() MUST have an $opt->{regex} set.++sub does_match {+ my ( $opt, $line ) = @_;++ $match_column_number = undef;+ @capture_indices = ();++ 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;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++sub resource_has_match {+ my ( $resource, $opt ) = @_;++ 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} ) {+ $opt->{color} = !App::Ack::output_to_pipe() && !$App::Ack::is_windows;+ }+ 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 );++ 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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> 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<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are seperated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were recieving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)++=head2 Use F<-Q> when in doubt about metacharacters++If you're searching for something with a regular expression+metacharacter, most often a period in a filename or IP address, add+the -Q to avoid false positives without all the backslashing. See+the following example for more...++=head2 Use ack to watch log files++Here's one I used the other day to find trouble spots for a website+visitor. The user had a problem loading F<troublesome.gif>, so I+took the access log and scanned it with ack twice.++ ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif++The first ack finds only the lines in the Apache log for the given+IP. The second finds the match on my troublesome GIF, and shows+the previous five lines from the log in each case.++=head2 Examples of F<--output>++Following variables are useful in the expansion string:++=over 4++=item C<$&>++The whole string matched by PATTERN.++=item C<$1>, C<$2>, ...++The contents of the 1st, 2nd ... bracketed group in PATTERN.++=item C<$`>++The string before the match.++=item C<$'>++The string after the match.++=back++For more details and other variables see+L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.++This example shows how to add text around a particular pattern+(in this case adding _ around word with "e")++ ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"+ _The_ quick brown fox jumps over the lazy dog+ The quick brown fox jumps _over_ the lazy dog+ The quick brown fox jumps over _the_ lazy dog++This shows how to pick out particular parts of a match using ( ) within regular expression.++ ack '=head(\d+)\s+(.*)' --output=' $1 : $2'+ input file contains "=head1 NAME"+ output "1 : NAME"++=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?++Yes! Please see L</"Defining your own types">. If you think+that F<ack> should recognize a type by default, please see+L</"ENHANCEMENTS">.++=head2 There's already a program/package called ack.++Yes, I know.++=head2 Why is it called ack if it's called ack-grep?++The name of the program is "ack". Some packagers have called it+"ack-grep" when creating packages because there's already a package+out there called "ack" that has nothing to do with this ack.++I suggest you make a symlink named F<ack> that points to F<ack-grep>+because one of the crucial benefits of ack is having a name that's+so short and simple to type.++To do that, run this with F<sudo> or as root:++ ln -s /usr/bin/ack-grep /usr/bin/ack++Alternatively, you could use a shell alias:++ # bash/zsh+ alias ack=ack-grep++ # csh+ alias ack ack-grep++=head2 What does F<ack> mean?++Nothing. I wanted a name that was easy to type and that you could+pronounce as a single syllable.++=head2 Can I do multi-line regexes?++No, ack does not support regexes that match multiple lines. Doing+so would require reading in the entire file at a time.++If you want to see lines near your match, use the C<--A>, C<--B>+and C<--C> switches for displaying context.++=head2 Why is ack telling me I have an invalid option when searching for C<+foo>?++ack treats command line options beginning with C<+> or C<-> as options; if you+would like to search for these, you may prefix your search term with C<--> or+use the C<--match> option. (However, don't forget that C<+> is a regular+expression metacharacter!)++=head1 ACKRC LOCATION SEMANTICS++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++=item *++Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted+using C<--ignore-ack-defaults>.++=item * Global ackrc++Options are then loaded from the global ackrc. This is located at+C</etc/ackrc> on Unix-like systems, and+C<C:\Documents and Settings\All Users\Application Data> on Windows.+This can be omitted using C<--noenv>.++=item * User ackrc++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>. If a different+ackrc is desired, it may be overriden with the C<$ACKRC> environment+variable.+This can be omitted using C<--noenv>.++=item * Project ackrc++Options are then loaded from the project ackrc. The project ackrc is+the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching+in the current directory, then the parent directory, then the grandparent+directory, etc. This can be omitted using C<--noenv>.++=item * ACK_OPTIONS++Options are then loaded from the enviroment variable C<ACK_OPTIONS>. This can+be omitted using C<--noenv>.++=item * Command line++Options are then loaded from the command line.++=back++=head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X++A lot of changes were made for ack 2; here is a list of them.++=head2 GENERAL CHANGES++=over 4++=item *++When no selectors are specified, ack 1.x only searches through files that+it can map to a file type. ack 2.x, by constrast, will search through+every regular, non-binary file that is not explicitly ignored via+B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the+B<-a/--all> option in ack 1.x.++=item *++A more flexible filter system has been added, so that more powerful file types+may be created by the user. For details, please consult+L</"Defining your own types">.++=item *++ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for+details.++=item *++ack's default filter definitions aren't special; you may tell ack to+completely disregard them if you don't like them.++=back++=head2 REMOVED OPTIONS++=over 4++=item *++Because of the change in default search behavior, the B<-a/--all> and+B<-u/--unrestricted> options have been removed. In addition, the+B<-k/--known-types> option was added to cause ack to behave with+the default search behavior of ack 1.x.++=item *++The B<-G> option has been removed. Two regular expressions on the+command line was considered too confusing; to simulate B<-G>'s functionality,+you may use the new B<-x> option to pipe filenames from one invocation of+ack into another.++=item *++The B<--binary> option has been removed.++=item *++The B<--skipped> option has been removed.++=item *++The B<--text> option has been removed.++=item *++The B<--invert-file-match> option has been removed. Instead, you may+use B<-v> with B<-g>.++=back++=head2 CHANGED OPTIONS++=over 4++=item *++The options that modify the regular expression's behavior (B<-i>, B<-w>,+B<-Q>, and B<-v>) may now be used with B<-g>.++=back++=head2 ADDED OPTIONS++=over 4++=item *++B<--files-from> was added so that a user may submit a list of filenames as+a list of files to search.++=item *++B<-x> was added to tell ack to accept a list of filenames via standard input;+this list is the list of filenames that will be used for the search.++=item *++B<-s> was added to tell ack to suppress error messages about non-existent or+unreadable files.++=item *++B<--ignore-directory> and B<--noignore-directory> were added as aliases for+B<--ignore-dir> and B<--noignore-dir> respectively.++=item *++B<--ignore-file> was added so that users may specify patterns of files to+ignore (ex. /.*~$/).++=item *++B<--dump> was added to allow users to easily find out which options are+set where.++=item *++B<--create-ackrc> was added so that users may create custom ackrc files based+on the default settings loaded by ack, and so that users may easily view those+defaults.++=item *++B<--type-del> was added to selectively remove file type definitions.++=item *++B<--ignore-ack-defaults> was added so that users may ignore ack's default+options in favor of their own.++=item *++B<--bar> was added so ack users may consult Admiral Ackbar.++=back++=head1 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 BUGS++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/petdance/ack2/issues>++=head1 ENHANCEMENTS++All enhancement requests MUST first be posted to the ack-users+mailing list at L<http://groups.google.com/group/ack-users>. I+will not consider a request without it first getting seen by other+ack users. This includes requests for new filetypes.++There is a list of enhancements I want to make to F<ack> in the ack+issues list at Github: L<https://github.com/petdance/ack2/issues>++Patches are always welcome, but patches with tests get the most+attention.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<http://beyondgrep.com/>++=item * The ack-users mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack issues list at Github++L<https://github.com/petdance/ack2/issues>++=item * AnnoCPAN: Annotated CPAN documentation++L<http://annocpan.org/dist/ack>++=item * CPAN Ratings++L<http://cpanratings.perl.org/d/ack>++=item * Search CPAN++L<http://search.cpan.org/dist/ack>++=item * Git source repository++L<https://github.com/petdance/ack2>++=back++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+Dale Sedivic,+Michael McClimon,+Andrew Black,+Ralph Bodenner,+Shaun Patterson,+Ryan Olson,+Shlomi Fish,+Karen Etheridge,+Olivier Mengue,+Matthew Wild,+Scott Kyle,+Nick Hooey,+Bo Borgerson,+Mark Szymanski,+Marq Schneider,+Packy Anderson,+JR Boyens,+Dan Sully,+Ryan Niebur,+Kent Fredric,+Mike Morearty,+Ingmar Vanhassel,+Eric Van Dewoestine,+Sitaram Chamarty,+Adam James,+Richard Carlsson,+Pedro Melo,+AJ Schuster,+Phil Jackson,+Michael Schwern,+Jan Dubois,+Christopher J. Madsen,+Matthew Wickline,+David Dyck,+Jason Porritt,+Jjgod Jiang,+Thomas Klausner,+Uri Guttman,+Peter Lewis,+Kevin Riggle,+Ori Avtalion,+Torsten Blix,+Nigel Metheringham,+GE<aacute>bor SzabE<oacute>,+Tod Hagan,+Michael Hendricks,+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,+Piers Cawley,+Stephen Steneker,+Elias Lutfallah,+Mark Leighton Fisher,+Matt Diephouse,+Christian Jaeger,+Bill Sully,+Bill Ricker,+David Golden,+Nilson Santos F. Jr,+Elliot Shank,+Merijn Broeren,+Uwe Voelker,+Rick Scott,+Ask BjE<oslash>rn Hansen,+Jerry Gay,+Will Coleda,+Mike O'Regan,+Slaven ReziE<0x107>,+Mark Stosberg,+David Alan Pisoni,+Adriano Ferreira,+James Keenan,+Leland Johnson,+Ricardo Signes,+Pete Krawczyk and+Rob Hoelz.++=head1 COPYRIGHT & LICENSE++Copyright 2005-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.++=cut+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.12';++++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 @_ },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ 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 from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{error_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!" );+ return undef;+ }+ }+ my $filter = $parms->{file_filter};++ 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;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!" );+ 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( $dirname, $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, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next+package App::Ack;++use warnings;+use strict;+++our $VERSION;+our $GIT_REVISION;+our $COPYRIGHT;+BEGIN {+ $VERSION = '2.04';+ $COPYRIGHT = 'Copyright 2005-2013 Andy Lester.';+ $GIT_REVISION = q{8f405b7};+}++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $dir_sep_chars;+our $is_cygwin;+our $is_windows;++use File::Spec 1.00015 ();++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_cygwin = ($^O eq 'cygwin');+ $is_windows = ($^O =~ /MSWin32/);+ $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );+}++++sub remove_dir_sep {+ my $path = shift;+ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}++++sub warn {+ return CORE::warn( _my_program(), ': ', @_, "\n" );+}+++sub die {+ return CORE::die( _my_program(), ': ', @_, "\n" );+}++sub _my_program {+ require File::Basename;+ return File::Basename::basename( $0 );+}++++sub filetypes_supported {+ return keys %mappings;+}++sub _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 show_help {+ my $help_arg = shift || 0;++ return show_help_types() if $help_arg =~ /^types?/;++ App::Ack::print( <<"END_OF_HELP" );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++Search for PATTERN in each source file in the tree from the current+directory on down. If any files or directories are specified, then+only those files and directories are checked. ack may also search+STDIN, but only if no file or directory arguments are specified,+or if one of them is "-".++Default switches may be specified in ACK_OPTIONS environment variable or+an .ackrc file. If you want no dependency on the environment, turn it+off with --noenv.++Example: ack -i select++Searching:+ -i, --ignore-case Ignore case distinctions in PATTERN+ --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i is specified+ -v, --invert-match Invert match: select non-matching lines+ -w, --word-regexp Force PATTERN to match only whole words+ -Q, --literal Quote all metacharacters; PATTERN is literal++Search output:+ --lines=NUM Only print line(s) NUM of each file+ -l, --files-with-matches Only print filenames containing matches+ -L, --files-without-matches Only print filenames with no matches+ --output=expr Output the evaluation of expr for each line+ (turns off text highlighting)+ -o Show only the part of a line matching PATTERN+ Same as --output='\$&'+ --passthru Print all lines, whether matching or not+ --match PATTERN Specify PATTERN explicitly.+ -m, --max-count=NUM Stop searching in each file after NUM matches+ -1 Stop searching after one match of any kind+ -H, --with-filename Print the filename for each match (default:+ on unless explicitly searching a single file)+ -h, --no-filename Suppress the prefixing filename on output+ -c, --count Show number of lines matching per file+ --[no]column Show the column number of the first match++ -A NUM, --after-context=NUM Print NUM lines of trailing context after matching+ lines.+ -B NUM, --before-context=NUM Print NUM lines of leading context before matching+ lines.+ -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.++ --print0 Print null byte as separator between filenames,+ only works with -f, -g, -l, -L or -c.++ -s Suppress error messages about nonexistent or+ unreadable files.+++File presentation:+ --pager=COMMAND Pipes all ack output through COMMAND. For example,+ --pager="less -R". Ignored if output is redirected.+ --nopager Do not send output through a pager. Cancels any+ setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.+ --[no]heading Print a filename heading above each file's results.+ (default: on when used interactively)+ --[no]break Print a break between results from different files.+ (default: on when used interactively)+ --group Same as --heading --break+ --nogroup Same as --noheading --nobreak+ --[no]color Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --[no]colour Same as --[no]color+ --color-filename=COLOR+ --color-match=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, and line numbers.+ --flush Flush output immediately, even when ack is used+ non-interactively (when output goes to a pipe or+ file).+++File finding:+ -f Only print the files selected, without searching.+ The PATTERN must not be specified.+ -g Same as -f, but only select files matching PATTERN.+ --sort-files Sort the found files lexically.+ --show-types Show which types each file has.+ --files-from=FILE Read the list of files to search from FILE.+ -x Read the list of files to search from STDIN.++File inclusion/exclusion:+ --[no]ignore-dir=name Add/Remove directory from the 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 (ack's default behavior)+ -n, --no-recurse No descending into subdirectories+ --[no]follow Follow symlinks. Default is off.+ -k, --known-types Include only files with 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 of type TYPE.+ --type-del TYPE Removes all filters associated with TYPE.+++Miscellaneous:+ --[no]env Ignore environment variables and global ackrc files. --env is legal but redundant.+ --ackrc=filename Specify an ackrc file to use+ --ignore-ack-defaults Ignore the default definitions that ack includes.+ --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++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 OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = filetypes_supported();+ my $maxlen = 0;+ for ( @types ) {+ $maxlen = length if $maxlen < length;+ }+ for my $type ( sort @types ) {+ next if $type =~ /^-/; # Stuff to not show+ my $ext_list = $mappings{$type};++ if ( ref $ext_list ) {+ $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );+ }+ App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );+ }++ return;+}++sub show_man {+ require Pod::Usage;++ Pod::Usage::pod2usage({+ -input => $App::Ack::orig_program_name,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}+++sub get_version_statement {+ require Config;++ my $copyright = get_copyright();+ my $this_perl = $Config::Config{perlpath};+ if ($^O ne 'VMS') {+ my $ext = $Config::Config{_exe};+ $this_perl .= $ext unless $this_perl =~ m/$ext$/i;+ }+ my $ver = sprintf( '%vd', $^V );++ my $git_revision = $GIT_REVISION ? " (git commit $GIT_REVISION)" : '';++ return <<"END_OF_VERSION";+ack ${VERSION}${git_revision}+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;+}+++# print subs added in order to make it easy for a third party+# module (such as App::Wack) to redefine the display methods+# and show the results in a different way.+sub print { print {$fh} @_; return; }+sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }+sub print_blank_line { App::Ack::print( "\n" ); return; }+sub print_separator { App::Ack::print( "--\n" ); return; }+sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }+sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }+sub print_count {+ my $filename = shift;+ my $nmatches = shift;+ my $ors = shift;+ my $count = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename );+ App::Ack::print( ':', $nmatches ) if $count;+ }+ else {+ App::Ack::print( $nmatches ) if $count;+ }+ App::Ack::print( $ors );++ return;+}++sub print_count0 {+ my $filename = shift;+ my $ors = shift;+ my $show_filename = shift;++ if ($show_filename) {+ App::Ack::print( $filename, ':0', $ors );+ }+ else {+ App::Ack::print( '0', $ors );+ }++ return;+}++sub set_up_pager {+ my $command = shift;++ return if App::Ack::output_to_pipe();++ my $pager;+ if ( not open( $pager, '|-', $command ) ) {+ App::Ack::die( qq{Unable to pipe to pager "$command": $!} );+ }+ $fh = $pager;++ return;+}+++sub output_to_pipe {+ return $output_to_pipe;+}+++sub exit_from_ack {+ my $nmatches = shift;++ my $rc = $nmatches ? 0 : 1;+ exit $rc;+}++++1; # End of App::Ack+package App::Ack::Resource;+++use warnings;+use strict;+use overload+ '""' => 'name';++sub FAIL {+ require Carp;+ Carp::confess( 'Must be overloaded' );+}+++sub new {+ return FAIL();+}+++sub name {+ return FAIL();+}+++sub is_binary {+ return FAIL();+}+++sub open {+ return FAIL();+}+++sub needs_line_scan {+ return FAIL();+}+++sub reset {+ return FAIL();+}+++sub close {+ return FAIL();+}+++sub clone {+ return FAIL();+}+++sub firstliney {+ return FAIL();+}++1;+package App::Ack::Resources;++++use warnings;+use strict;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ my $file_filter = undef;+ my $descend_filter = $opt->{descend_filter};++ if( $opt->{n} ) {+ $descend_filter = sub {+ return 0;+ };+ }++ $self->{iter} =+ File::Next::files( {+ file_filter => $opt->{file_filter},+ descend_filter => $descend_filter,+ error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $iter =+ File::Next::from_file( {+ error_handler => 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;++ return bless {+ iter => $iter,+ }, $class;+}++# This is for reading input lines from STDIN, not the list of files from STDIN+sub from_stdin {+ my $class = shift;+ my $opt = shift;++ my $self = bless {}, $class;++ my $has_been_called = 0;++ $self->{iter} = sub {+ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}++sub next {+ my $self = shift;++ my $file = $self->{iter}->() or return;++ return App::Ack::Resource::Basic->new( $file );+}++1;+package App::Ack::Resource::Basic;+++use warnings;+use strict;++use Fcntl ();++BEGIN {+ our @ISA = 'App::Ack::Resource';+}+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ opened => 0,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ $self->{opened} = 1;+ }++ return $self;+}+++sub name {+ return $_[0]->{filename};+}++++sub needs_line_scan {+ my $self = shift;+ my $opt = shift;++ return 1 if $opt->{v};++ my $size = -s $self->{fh};+ if ( $size == 0 ) {+ return 0;+ }+ elsif ( $size > 100_000 ) {+ return 1;+ }++ my $buffer;+ my $rc = sysread( $self->{fh}, $buffer, $size );+ if ( !defined($rc) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ return 1;+ }+ return 0 unless $rc && ( $rc == $size );++ my $regex = $opt->{regex};+ return $buffer =~ /$regex/m;+}+++sub reset {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }++ return;+}+++sub close {+ my $self = shift;++ # return if we haven't opened the file yet+ if ( !defined($self->{fh}) ) {+ return;+ }++ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }++ $self->{opened} = 0;++ return;+}+++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}++sub firstliney {+ my ( $self ) = @_;++ my $fh = $self->open();++ 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;+ }++ $self->close;++ return $self->{firstliney};+}++sub open {+ my ( $self ) = @_;++ return $self->{fh} if $self->{opened};++ unless ( open $self->{fh}, '<', $self->{filename} ) {+ return;+ }++ $self->{opened} = 1;++ return $self->{fh};+}++1;+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,+ }, $class;+}++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,+ }, $class;+}++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'};+}++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::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00;++use if ($^O =~ /MSWin32/ ? 1 : 0), "Win32";+++our $is_win = 0;++sub new {+ my ( $class ) = @_;++ $is_win = $^O =~ /MSWin32/,++ return bless {}, $class;+}++sub _remove_redundancies {+ my ( @configs ) = @_;++ if ( $is_win ) {+ # 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( $is_win ) {+ 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 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;+ }++ 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).+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' => \$opt->{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;++ 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',+ );++ 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;++ 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::ConfigDefault;++use warnings;+use strict;++sub options {+ my @options = split( /\n/, _options_block() );+ @options = grep { /./ && !/^#/ } @options;++ return @options;+}++sub _options_block {+ return <<'HERE';+# This is the default ackrc for ack 2.0++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++# Directories to ignore+# Bazaar+--ignore-directory=is:.bzr++# Codeville+--ignore-directory=is:.cdv++# Interface Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+--ignore-directory=is:.git++# Mercurial+--ignore-directory=is:.hg++# quilt+--ignore-directory=is:.pc++# Subversion+--ignore-directory=is:.svn++# Monotone+--ignore-directory=is:_MTN++# CVS+--ignore-directory=is:CVS++# RCS+--ignore-directory=is:RCS++# SCCS+--ignore-directory=is:SCCS++# darcs+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+--ignore-directory=is:cover_db++++# Files to ignore+# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.]min[.]js$/+--ignore-file=match:/[.]js[.]min$/++# minified CSS+--ignore-file=match:/[.]min[.]css$/+--ignore-file=match:/[.]css[.]min$/+++# Filetypes defined++# Perl http://perl.org/+--type-add=perl:ext:pl,pm,pod,t+--type-add=perl:firstlinematch:/^#!.*\bperl/++# 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++# Rakefiles http://rake.rubyforge.org/+--type-add=rake:is:Rakefile++# CMake http://www.cmake.org/+--type-add=cmake:is:CMakeLists.txt+--type-add=cmake:ext:cmake++# Actionscript+--type-add=actionscript:ext:as,mxml++# Ada http://www.adaic.org/+--type-add=ada:ext:ada,adb,ads++# ASP http://msdn.microsoft.com/en-us/library/aa286483.aspx+--type-add=asp:ext:asp++# ASP.Net http://www.asp.net/+--type-add=aspx:ext:master,ascx,asmx,aspx,svc++# Assembly+--type-add=asm:ext:asm,s++# Batch+--type-add=batch:ext:bat,cmd++# ColdFusion http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure http://clojure.org/+--type-add=clojure:ext:clj++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Dart http://www.dartlang.org/+--type-add=dart:ext:dart++# Delphi http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# 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++# Common Lisp http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua http://www.lua.org/+--type-add=lua:ext:lua++# 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++# 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++# 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;
garage/ack-2.05_01
@@ -0,0 +1,4635 @@+#!/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+# http://github.com/petdance/ack2+# and submit patches against the individual files+# that build ack.+#++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.05_01';+# 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 ( @ARGV ) {+ last if ( $_ eq '--' );++ # 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 ) {+ 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 = map {+ my $filter;++ if ( /^(\w+):(.+)/ ) {+ my ($how,$what) = ($1,$2);+ $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));+ }+ else {+ Carp::croak( qq{Invalid filter specification "$_"} );+ }+ $filter+ } @{$ifiles};++ my $filters = $opt->{'filters'} || [];+ my $inverse_filters = [ grep { $_->is_inverted() } @{$filters} ];+ @{$filters} = grep { !$_->is_inverted() } @{$filters};++ 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;+ foreach my $filter (@ifiles_filters) {+ return 0 if $filter->filter($resource);+ }+ my $match_found = 1;+ if ( @{$filters} ) {+ $match_found = 0;++ foreach my $filter (@{$filters}) {+ if ($filter->filter($resource)) {+ $match_found = 1;+ last;+ }+ }+ }+ # Don't bother invoking inverse filters unless we consider the current resource a match+ if ( $match_found && @{$inverse_filters} ) {+ foreach my $filter ( @{$inverse_filters} ) {+ if ( not $filter->filter( $resource ) ) {+ $match_found = 0;+ last;+ }+ }+ }+ 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;+}++# inefficient, but functional+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;+ }+ }+ }++ return sort @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 ) {+ my @capture_indices = get_capture_indices();+ if( @capture_indices ) {+ my $offset = 0; # additional offset for when we add stuff++ foreach my $index_pair ( @capture_indices ) {+ my ( $match_start, $match_end ) = @{$index_pair};++ next unless defined($match_start);++ 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 );++ $offset += length( $substitution ) - length( $substring );+ }+ }+ 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 ));+ }+ $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;+ }+ }++ 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};++ $matching_line =~ s/[\r\n]+$//g;++ my ( $before_context, $after_context ) = get_context();++ if ( $before_context ) {+ my $first_line = $. - @{$before_context};++ 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};++ 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;+ }++ chomp $line;+ print_line_with_options($opt, $filename, $line, $context_line_no, '-');+ $previous_line_printed = $context_line_no;+ $offset--;+ }+ }+ }++ if ( $. > $previous_line_printed ) {+ if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {+ App::Ack::print('--', $ors);+ }++ print_line_with_options($opt, $filename, $matching_line, $line_no, ':');+ $previous_line_printed = $.;+ }++ 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++;+ }+ }++ $is_first_match = 0;++ return;+}++}++{++my @capture_indices;+my $match_column_number;++# does_match() MUST have an $opt->{regex} set.++sub does_match {+ my ( $opt, $line ) = @_;++ $match_column_number = undef;+ @capture_indices = ();++ 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;++ if ( @- > 1 ) {+ @capture_indices = map {+ [ $-[$_], $+[$_] ]+ } ( 1 .. $#- );+ }+ return 1;+ }+ else {+ return;+ }+ }+}++sub get_capture_indices {+ return @capture_indices;+}++sub get_match_column {+ return $match_column_number;+}++}++sub resource_has_match {+ my ( $resource, $opt ) = @_;++ 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 );++ 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++Run I<ack> with the C<--dump> option to see what settings are set.++However, I<ack> always searches the files given on the command line,+no matter what type. If you tell I<ack> to search in a coredump,+it will search in a coredump.++=head1 DIRECTORY SELECTION++I<ack> descends through the directory tree of the starting directories+specified. If no directories are specified, the current working directory is+used. However, it will ignore the shadow directories used by+many version control systems, and the build directories used by the+Perl MakeMaker system. You may add or remove a directory from this+list with the B<--[no]ignore-dir> option. The option may be repeated+to add/remove multiple directories from the ignore list.++For a complete list of directories that do not get searched, run+C<ack --dump>.++=head1 WHEN TO USE GREP++I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't+throw I<grep> away, because there are times you'll still need it.++E.g., searching through huge files looking for regexes that can be+expressed with I<grep> syntax should be quicker with I<grep>.++If your script or parent program uses I<grep> C<--quiet> or C<--silent>+or needs exit 2 on IO error, use I<grep>.++=head1 OPTIONS++=over 4++=item B<-A I<NUM>>, B<--after-context=I<NUM>>++Print I<NUM> lines of trailing context after matching lines.++=item B<-B I<NUM>>, B<--before-context=I<NUM>>++Print I<NUM> lines of leading context before matching lines.++=item B<--[no]break>++Print a break between results from different files. On by default+when used interactively.++=item B<-C [I<NUM>]>, B<--context[=I<NUM>]>++Print I<NUM> lines (default 2) of context around matching lines.++=item B<-c>, B<--count>++Suppress normal output; instead print a count of matching lines for+each input file. If B<-l> is in effect, it will only show the+number of lines for each file that has lines matching. Without+B<-l>, some line counts may be zeroes.++If combined with B<-h> (B<--no-filename>) ack outputs only one total+count.++=item B<--[no]color>, B<--[no]colour>++B<--color> highlights the matching text. B<--nocolor> suppresses+the color. This is on by default unless the output is redirected.++On Windows, this option is off by default unless the+L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>+environment variable is used.++=item B<--color-filename=I<color>>++Sets the color to be used for filenames.++=item B<--color-match=I<color>>++Sets the color to be used for matches.++=item B<--color-lineno=I<color>>++Sets the color to be used for line numbers.++=item B<--[no]column>++Show the column number of the first match. This is helpful for+editors that can place your cursor at a given position.++=item B<--create-ackrc>++Dumps the default ack options to standard output. This is useful for+when you want to customize the defaults.++=item B<--dump>++Writes the list of options loaded and where they came from to standard+output. Handy for debugging.++=item B<--[no]env>++B<--noenv> disables all environment processing. No F<.ackrc> is+read and all environment variables are ignored. By default, F<ack>+considers F<.ackrc> and settings in the environment.++=item B<--flush>++B<--flush> flushes output immediately. This is off by default+unless ack is running interactively (when output goes to a pipe or+file).++=item B<-f>++Only print the files that would be searched, without actually doing+any searching. PATTERN must not be specified, or it will be taken+as a path to search.++=item B<--files-from=I<FILE>>++The list of files to be searched is specified in I<FILE>. The list of+files are separated by newlines. If I<FILE> is C<->, the list is loaded+from standard input.++=item B<--[no]filter>++Forces ack to act as if it were receiving input via a pipe.++=item B<--[no]follow>++Follow or don't follow symlinks, other than whatever starting files+or directories were specified on the command line.++This is off by default.++=item B<-g I<PATTERN>>++Print files where the relative path + filename matches I<PATTERN>.++=item B<--[no]group>++B<--group> groups matches by file name. This is the default+when used interactively.++B<--nogroup> prints one result per line, like grep. This is the+default when output is redirected.++=item B<-H>, B<--with-filename>++Print the filename for each match. This is the default unless searching+a single explicitly specified file.++=item B<-h>, B<--no-filename>++Suppress the prefixing of filenames on output when multiple files are+searched.++=item B<--[no]heading>++Print a filename heading above each file's results. This is the default+when used interactively.++=item B<--help>, B<-?>++Print a short help statement.++=item B<--help-types>, B<--help=types>++Print all known types.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN++=item B<--ignore-ack-defaults>++Tells ack to completely ignore the default definitions provided with ack.+This is useful in combination with B<--create-ackrc> if you I<really> want+to customize ack.++=item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>++Ignore directory (as CVS, .svn, etc are ignored). May be used+multiple times to ignore multiple directories. For example, mason+users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>+option allows users to search directories which would normally be+ignored (perhaps to research the contents of F<.svn/props> directories).++The I<DIRNAME> must always be a simple directory name. Nested+directories like F<foo/bar> are NOT supported. You would need to+specify B<--ignore-dir=foo> and then no files from any foo directory+are taken into account by ack unless given explicitly on the command+line.++=item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>++Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified+identically to file type filters as seen in L</"Defining your own types">.++=item B<-k>, B<--known-types>++Limit selected files to those with types that ack knows about. This is+equivalent to the default behavior found in ack 1.++=item B<--lines=I<NUM>>++Only print line I<NUM> of each file. Multiple lines can be given with multiple+B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>+also works. The lines are always output in ascending order, no matter the+order given on the command line.++=item B<-l>, B<--files-with-matches>++Only print the filenames of matching files, instead of the matching text.++=item B<-L>, B<--files-without-matches>++Only print the filenames of files that do I<NOT> match.++=item B<--match I<PATTERN>>++Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the+regex as your first argument, e.g. when executing multiple searches over the+same set of files.++ # search for foo and bar in given files+ ack file1 t/file* --match foo+ ack file1 t/file* --match bar++=item B<-m=I<NUM>>, B<--max-count=I<NUM>>++Stop reading a file after I<NUM> matches.++=item B<--man>++Print this manual page.++=item B<-n>, B<--no-recurse>++No descending into subdirectories.++=item B<-o>++Show only the part of each line matching PATTERN (turns off text+highlighting)++=item B<--output=I<expr>>++Output the evaluation of I<expr> for each line (turns off text+highlighting)+If PATTERN matches more than once then a line is output for each non-overlapping match.+For more information please see the section L</"Examples of F<--output>">.++=item B<--pager=I<program>>, B<--nopager>++B<--pager> directs ack's output through I<program>. This can also be specified+via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.++Using --pager does not suppress grouping and coloring like piping+output on the command-line does.++B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.+No output will be sent through a pager.++=item B<--passthru>++Prints all lines, whether or not they match the expression. Highlighting+will still work, though, so it can be used to highlight matches while+still seeing the entire file, as in:++ # Watch a log file, and highlight a certain IP address+ $ tail -f ~/access.log | ack --passthru 123.45.67.89++=item B<--print0>++Only works in conjunction with -f, -g, -l or -c (filename output). The filenames+are output separated with a null byte instead of the usual newline. This is+helpful when dealing with filenames that contain whitespace, e.g.++ # remove all files of type html+ ack -f --html --print0 | xargs -0 rm -f++=item B<-Q>, B<--literal>++Quote all metacharacters in PATTERN, it is treated as a literal.++=item B<-r>, B<-R>, B<--recurse>++Recurse into sub-directories. This is the default and just here for+compatibility with grep. You can also use it for turning B<--no-recurse> off.++=item B<-s>++Suppress error messages about nonexistent or unreadable files. This is taken+from fgrep.++=item B<--[no]smart-case>, B<--no-smart-case>++Ignore case in the search strings if PATTERN contains no uppercase+characters. This is similar to C<smartcase> in vim. This option is+off by default, and ignored if C<-i> is specified.++B<-i> always overrides this option.++=item B<--sort-files>++Sorts the found files lexicographically. Use this if you want your file+listings to be deterministic between runs of I<ack>.++=item B<--show-types>++Outputs the filetypes that ack associates with each file.++Works with B<-f> and B<-g> options.++=item B<--type=[no]TYPE>++Specify the types of files to include or exclude from a search.+TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can+also be specified as B<--perl>, and B<--type=noperl> can be done+as B<--noperl>.++If a file is of both type "foo" and "bar", specifying --foo and+--nobar will exclude the file, because an exclusion takes precedence+over an inclusion.++Type specifications can be repeated and are ORed together.++See I<ack --help=types> for a list of valid types.++=item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER+are recognized as being of (the existing) type TYPE.+See also L</"Defining your own types">.+++=item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>++Files with the given FILTERARGS applied to the given FILTER are recognized as+being of type TYPE. This replaces an existing definition for type TYPE. See+also L</"Defining your own types">.++=item B<--type-del I<TYPE>>++The filters associated with TYPE are removed from Ack, and are no longer considered+for searches.++=item B<-v>, B<--invert-match>++Invert match: select non-matching lines++=item B<--version>++Display version and copyright information.++=item B<-w>, B<--word-regexp>++Force PATTERN to match only whole words. The PATTERN is wrapped with+C<\b> metacharacters.++=item B<-x>++An abbreviation for B<--files-from=->; the list of files to search are read+from standard input, with one line per file.++=item B<-1>++Stops after reporting first match of any kind. This is different+from B<--max-count=1> or B<-m1>, where only one match per file is+shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does+not.++=item B<--thpppt>++Display the all-important Bill The Cat logo. Note that the exact+spelling of B<--thpppppt> is not important. It's checked against+a regular expression.++=item B<--bar>++Check with the admiral for traps.++=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 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.++If you want to define a new type, or completely redefine an existing+type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines+the type I<eiffel> to include files with+the extensions .e or .eiffel. So to search for all eiffel files+containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.+As usual, you can also write B<--type=eiffel>+instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes+all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>+and I<.xs> files no longer belong to the type I<cc>.++When defining your own types in the F<.ackrc> file you have to use+the following:++ --type-set=eiffel:ext:e,eiffel++or writing on separate lines++ --type-set+ eiffel:ext:e,eiffel++The following does B<NOT> work in the F<.ackrc> file:++ --type-set eiffel:ext:e,eiffel+++In order to see all currently defined types, use I<--help-types>, e.g.+I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>++In addition to filtering based on extension (like ack 1.x allowed), ack 2+offers additional filter types. The generic syntax is+I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value+of I<FILTER>.++=over 4++=item is:I<FILENAME>++I<is> filters match the target filename exactly. It takes exactly one+argument, which is the name of the file to match.++Example:++ --type-set make:is:Makefile++=item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]++I<ext> filters match the extension of the target file against a list+of extensions. No leading dot is needed for the extensions.++Example:++ --type-set perl:ext:pl,pm,t++=item match:I<PATTERN>++I<match> filters match the target filename against a regular expression.+The regular expression is made case insensitive for the search.++Example:++ --type-set make:match:/(gnu)?makefile/++=item firstlinematch:I<PATTERN>++I<firstlinematch> matches the first line of the target file against a+regular expression. Like I<match>, the regular expression is made+case insensitive.++Example:++ --type-add perl:firstlinematch:/perl/++=back++More filter types may be made available in the future.++=head1 ENVIRONMENT VARIABLES++For commonly-used ack options, environment variables can make life+much easier. These variables are ignored if B<--noenv> is specified+on the command line.++=over 4++=item ACKRC++Specifies the location of the user's F<.ackrc> file. If this file doesn't+exist, F<ack> looks in the default location.++=item ACK_OPTIONS++This variable specifies default options to be placed in front of+any explicit options on the command line.++=item ACK_COLOR_FILENAME++Specifies the color of the filename when it's printed in B<--group>+mode. By default, it's "bold green".++The recognized attributes are clear, reset, dark, bold, underline,+underscore, blink, reverse, concealed black, red, green, yellow,+blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,+on_magenta, on_cyan, and on_white. Case is not significant.+Underline and underscore are equivalent, as are clear and reset.+The color alone sets the foreground color, and on_color sets the+background color.++This option can also be set with B<--color-filename>.++=item ACK_COLOR_MATCH++Specifies the color of the matching text when printed in B<--color>+mode. By default, it's "black on_yellow".++This option can also be set with B<--color-match>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_COLOR_LINENO++Specifies the color of the line number when printed in B<--color>+mode. By default, it's "bold yellow".++This option can also be set with B<--color-lineno>.++See B<ACK_COLOR_FILENAME> for the color specifications.++=item ACK_PAGER++Specifies a pager program, such as C<more>, C<less> or C<most>, to which+ack will send its output.++Using C<ACK_PAGER> does not suppress grouping and coloring like+piping output on the command-line does, except that on Windows+ack will assume that C<ACK_PAGER> does not support color.++C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.++=item ACK_PAGER_COLOR++Specifies a pager program that understands ANSI color sequences.+Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring+like piping output on the command-line does.++If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.++=back++=head1 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\ -k++That example uses C<-k> to search through only files of the types ack+knows about, but you may use other default flags. Now you can search+with F<ack> and easily step through the results in Vim:++ :grep Dumper perllib++Miles Sterrett has written a Vim plugin for F<ack> which allows you to use+C<:Ack> instead of C<:grep>, as well as several other advanced features.++L<https://github.com/mileszs/ack.vim>++=head2 Emacs integration++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 have been selected++Ack's B<-f> was originally added as a debugging tool. If ack is+not finding matches you think it should find, run F<ack -f> to see+what files have been selected. You can also add the C<--show-types>+options to show the type of each file selected.++=head2 Use B<--dump>++This lists the ackrc files that are loaded and the options loaded+from them.+So for example you can find a list of directories that do not get searched or where filetypes are defined.++=head1 TIPS++=head2 Use the F<.ackrc> file.++The F<.ackrc> is the place to put all your options you use most of+the time but don't want to remember. Put all your --type-add and+--type-set definitions in it. If you like --smart-case, set it+there, too. I also set --sort-files there.++=head2 Use F<-f> for working with big codesets++Ack does more than search files. C<ack -f --perl> will create a+list of all the Perl files in a tree, ideal for sending into F<xargs>.+For example:++ # Change all "this" to "that" in all Perl files in a tree.+ ack -f --perl | xargs perl -p -i -e's/this/that/g'++or if you prefer:++ perl -p -i -e's/this/that/g' $(ack -f --perl)+
The diff stops at 1024 KiB. git show d0cd000 shows the rest.