190625.6 · 5b1d66d0b1d91d28a9acdece8cb781dc7b7301a2
Added 3.0.1 to the garage
reading declarations
Changes to garage/ack-v3.0.1. All files
@@ -0,0 +1,5565 @@+#!/usr/bin/env perl+#+# This file, ack, is generated code.+# Please DO NOT EDIT or send patches for it.+#+# Please take a look at the source from+# https://github.com/beyondgrep/ack3+# and submit patches against the individual files+# that build ack.+#++$App::Ack::STANDALONE = 1;+package main;++use strict;+use warnings;++our $VERSION = v3.0.1; # Check https://beyondgrep.com/ for updates++use 5.010001;++use File::Spec ();++++# Global command-line options+our $opt_1;+our $opt_A;+our $opt_B;+our $opt_break;+our $opt_color;+our $opt_column;+our $opt_c;+our $opt_f;+our $opt_g;+our $opt_heading;+our $opt_L;+our $opt_l;+our $opt_m;+our $opt_output;+our $opt_passthru;+our $opt_p;+our $opt_regex;+our $opt_show_filename;+our $opt_show_types;+our $opt_underline;+our $opt_v;++# Flag if we need any context tracking.+our $is_tracking_context;++our @special_vars_used_by_opt_output;++MAIN: {+ $App::Ack::ORIGINAL_PROGRAM_NAME = $0;+ $0 = join(' ', 'ack', $0);+ $App::Ack::ors = "\n";+ 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 and --man 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 ) {+ if ( $ENV{ACK_OPTIONS} ) {+ App::Ack::warn( 'WARNING: ack no longer uses the ACK_OPTIONS environment variable. Use an ackrc file instead.' );+ }+ }+ else {+ my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );+ delete @ENV{@keys};+ }++ # Load colors+ my $modules_loaded_ok = eval 'use Term::ANSIColor 1.10 (); 1;';+ if ( $modules_loaded_ok && $App::Ack::is_windows ) {+ $modules_loaded_ok = eval 'use Win32::Console::ANSI; 1;';+ }+ if ( $modules_loaded_ok ) {+ $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';+ $ENV{ACK_COLOR_FILENAME} ||= 'bold green';+ $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';+ $ENV{ACK_COLOR_COLNO} ||= 'bold yellow';+ }++ my $p = App::Ack::ConfigLoader::opt_parser( 'no_auto_abbrev', 'pass_through' );+ $p->getoptions(+ help => sub { App::Ack::show_help(); exit; },+ version => sub { App::Ack::print( App::Ack::get_version_statement() ); exit; },+ man => sub { App::Ack::show_man(); },+ );++ if ( !@ARGV ) {+ App::Ack::show_help();+ exit 1;+ }++ my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();++ my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );++ $opt_1 = $opt->{1};+ $opt_A = $opt->{A};+ $opt_B = $opt->{B};+ $opt_break = $opt->{break};+ $opt_c = $opt->{c};+ $opt_color = $opt->{color};+ $opt_column = $opt->{column};+ $opt_f = $opt->{f};+ $opt_g = $opt->{g};+ $opt_heading = $opt->{heading};+ $opt_L = $opt->{L};+ $opt_l = $opt->{l};+ $opt_m = $opt->{m};+ $opt_output = $opt->{output};+ $opt_p = $opt->{p};+ $opt_passthru = $opt->{passthru};+ $opt_regex = $opt->{regex};+ $opt_show_filename = $opt->{show_filename};+ $opt_show_types = $opt->{show_types};+ $opt_underline = $opt->{underline};+ $opt_v = $opt->{v};++ $App::Ack::report_bad_filenames = !$opt->{s};+ $App::Ack::ors = $opt->{print0} ? "\0" : "\n";++ 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 ( defined $opt_output ) {+ # Expand out \t, \n and \r.+ $opt_output =~ s/\\n/\n/g;+ $opt_output =~ s/\\r/\r/g;+ $opt_output =~ s/\\t/\t/g;++ my @supported_special_variables = ( 1..9, qw( _ . ` & ' + f ) );+ @special_vars_used_by_opt_output = grep { $opt_output =~ /\$$_/ } @supported_special_variables;++ # If the $opt_output contains $&, $` or $', those vars won't be+ # captured until they're used at least once in the program.+ # Do the eval to make this happen.+ for my $i ( @special_vars_used_by_opt_output ) {+ if ( $i eq q{&} || $i eq q{'} || $i eq q{`} ) {+ no warnings; # They will be undef, so don't warn.+ eval qq{"\$$i"};+ }+ }+ }++ # Set up file filters.+ my $files;+ if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x+ $files = App::Ack::Files->from_stdin();+ $opt_regex = shift @ARGV if not defined $opt_regex;+ $opt_regex = build_regex( $opt_regex, $opt );+ }+ else {+ if ( $opt_f ) {+ # No need to check for regex, since mutex options are handled elsewhere.+ }+ else {+ $opt_regex = shift @ARGV if not defined $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} ) {+ $files = App::Ack::Files->from_file( $opt, $opt->{files_from} );+ exit 1 unless $files;+ }+ 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);++ $files = App::Ack::Files->from_argv( $opt, \@start );+ }+ }+ App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};++ my $nmatches = 0;+ my $total_count = 0;++ set_up_line_context();++FILES:+ while ( defined(my $file = $files->next) ) {+ if ($is_tracking_context) {+ set_up_line_context_for_file();+ }++ # ack -f+ if ( $opt_f ) {+ if ( $opt_show_types ) {+ App::Ack::show_types( $file );+ }+ else {+ App::Ack::say( $file->name );+ }+ ++$nmatches;+ last FILES if defined($opt_m) && $nmatches >= $opt_m;+ }+ # ack -g+ elsif ( $opt_g ) {+ if ( $opt_show_types ) {+ App::Ack::show_types( $file );+ }+ else {+ local $opt_show_filename = 0; # XXX Why is this local?++ print_line_with_options( '', $file->name, 0, $App::Ack::ors );+ }+ ++$nmatches;+ last FILES if defined($opt_m) && $nmatches >= $opt_m;+ }+ # ack -c+ elsif ( $opt_c ) {+ my $matches_for_this_file = count_matches_in_file( $file );++ if ( not $opt_show_filename ) {+ $total_count += $matches_for_this_file;+ next FILES;+ }++ if ( !$opt_l || $matches_for_this_file > 0) {+ if ( $opt_show_filename ) {+ App::Ack::say( $file->name, ':', $matches_for_this_file );+ }+ else {+ App::Ack::say( $matches_for_this_file );+ }+ }+ }+ # ack -l, ack -L+ elsif ( $opt_l || $opt_L ) {+ my $is_match = file_has_match( $file );++ if ( $opt_L ? !$is_match : $is_match ) {+ App::Ack::say( $file->name );+ ++$nmatches;++ last FILES if $opt_1;+ last FILES if defined($opt_m) && $nmatches >= $opt_m;+ }+ }+ # Normal match-showing ack+ else {+ # 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 file at all, then there's no need to do the+ # line-by-line iteration.+ # Slurp up an entire file up to 10M, see if there are any matches+ # in it, and if so, let us know so we can iterate over it directly.+ my $needs_line_scan = 1;+ if ( $opt_regex && !$opt_passthru && !$opt_v ) {+ if ( $file->open() && -f $file->{fh} ) {+ my $buffer;+ my $size = 10_000_000;+ my $rc = sysread( $file->{fh}, $buffer, $size );+ if ( !defined($rc) ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $file->name . ": $!" );+ }+ $needs_line_scan = 0;+ }+ else {+ # If we read all 10M, then we need to scan the rest.+ if ( $rc == $size ) {+ $needs_line_scan = 1;+ }+ else {+ # Check for the pattern in what we got.+ $needs_line_scan = ($buffer =~ /$opt_regex/mo);+ }+ if ( $needs_line_scan ) {+ $file->reset();+ }+ }+ }+ }+ if ( $needs_line_scan ) {+ $nmatches += print_matches_in_file( $file );+ }+ if ( $opt_1 && $nmatches ) {+ last FILES;+ }+ }+ }++ if ( $opt_c && !$opt_show_filename ) {+ App::Ack::print( $total_count, "\n" );+ }++ close $App::Ack::fh;++ App::Ack::exit_from_ack( $nmatches );+}++# End of 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 $file = App::Ack::File->new($File::Next::dir);+ return !grep { $_->filter($file) } @{$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::File->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 $file = App::Ack::File->new($File::Next::name);++ if ( $ifiles_filters && $ifiles_filters->filter($file) ) {+ return 0;+ }++ my $match_found = $direct_filters->filter($file);++ # Don't bother invoking inverse filters unless we consider the current file a match.+ if ( $match_found && $inverse_filters->filter( $file ) ) {+ $match_found = 0;+ }+ return $match_found;+ };+}+++# 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.' );++ # Check for lowercaseness before we do any modifications.+ my $regex_is_lc = $str eq lc $str;++ $str = quotemeta( $str ) if $opt->{Q};++ # Whole words only.+ if ( $opt->{w} ) {+ my $ok = 1;++ if ( $str =~ /^\\[wd]/ ) {+ # Explicit \w is good.+ }+ else {+ # Can start with \w, (, [ or dot.+ if ( $str !~ /^[\w\(\[\.]/ ) {+ $ok = 0;+ }+ }++ # Can end with \w, }, ), ], +, *, or dot.+ if ( $str !~ /[\w\}\)\]\+\*\?\.]$/ ) {+ $ok = 0;+ }+ # ... unless it's escaped.+ elsif ( $str =~ /\\[\}\)\]\+\*\?\.]$/ ) {+ $ok = 0;+ }++ if ( !$ok ) {+ App::Ack::die( '-w will not do the right thing if your regex does not begin and end with a word character.' );+ }++ if ( $str =~ /^\w+$/ ) {+ # No need for fancy regex if it's a simple word.+ $str = sprintf( '\b(?:%s)\b', $str );+ }+ else {+ $str = sprintf( '(?:^|\b|\s)\K(?:%s)(?=\s|\b|$)', $str );+ }+ }++ if ( $opt->{i} || ($opt->{S} && $regex_is_lc) ) {+ $str = "(?i)$str";+ }++ my $re = eval { qr/$str/m };+ if ( !$re ) {+ my $err = $@;+ chomp $err;+ App::Ack::die( "Invalid regex '$str':\n $err" );+ }++ return $re;++}++my $match_colno;++{++# 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_lineno;++my $is_first_match;+state $has_printed_something = 0;++# Set up context tracking variables.+sub set_up_line_context {+ $n_before_ctx_lines = $opt_output ? 0 : ($opt_B || 0);+ $n_after_ctx_lines = $opt_output ? 0 : ($opt_A || 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_lineno = 0;+ $after_context_pending = 0;+ if ( $opt_heading ) {+ $is_first_match = 1;+ }++ return;+}++sub print_matches_in_file {+ my $file = shift;++ my $max_count = $opt_m || -1; # Go negative for no limit so it can never reduce to 0.+ my $nmatches = 0;+ my $filename = $file->name;++ my $has_printed_for_this_file = 0;++ my $fh = $file->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> ) {+ chomp;+ my $does_match;+ $match_colno = undef;++ if ( $opt_v ) {+ $does_match = !/$opt_regex/o;+ }+ else {+ if ( $does_match = /$opt_regex/o ) {+ # @- = @LAST_MATCH_START+ # @+ = @LAST_MATCH_END+ $match_colno = $-[0] + 1;+ }+ }+ if ( $does_match && $max_count ) {+ if ( !$has_printed_for_this_file ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::say( $display_filename );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_file = 1;+ $nmatches++;+ $max_count--;+ }+ else {+ 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, $_, $., '-' );+ --$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;+ }+ }++ last if ($max_count == 0) && ($after_context_pending == 0);+ }+ }+ elsif ( $opt_passthru ) {+ local $_ = undef;++ while ( <$fh> ) {+ chomp;+ $match_colno = undef;+ if ( $opt_v ? !/$opt_regex/o : /$opt_regex/o ) {+ if ( !$opt_v ) {+ $match_colno = $-[0] + 1;+ }+ if ( !$has_printed_for_this_file ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::say( $display_filename );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_file = 1;+ $nmatches++;+ $max_count--;+ }+ else {+ if ( $opt_break && !$has_printed_for_this_file && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_file = 1;+ }+ last if $max_count == 0;+ }+ }+ elsif ( $opt_v ) {+ local $_ = undef;++ $match_colno = undef;+ while ( <$fh> ) {+ chomp;+ if ( !/$opt_regex/o ) {+ if ( !$has_printed_for_this_file ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::say( $display_filename );+ }+ }+ print_line_with_context( $filename, $_, $. );+ $has_printed_for_this_file = 1;+ $nmatches++;+ $max_count--;+ }+ last if $max_count == 0;+ }+ }+ else {+ local $_ = undef;++ my $last_match_lineno;+ while ( <$fh> ) {+ chomp;+ $match_colno = undef;+ if ( /$opt_regex/o ) {+ $match_colno = $-[0] + 1;+ if ( !$has_printed_for_this_file ) {+ if ( $opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ if ( $opt_show_filename && $opt_heading ) {+ App::Ack::say( $display_filename );+ }+ }+ if ( $opt_p ) {+ if ( $last_match_lineno ) {+ if ( $. > $last_match_lineno + $opt_p ) {+ App::Ack::print_blank_line();+ }+ }+ elsif ( !$opt_break && $has_printed_something ) {+ App::Ack::print_blank_line();+ }+ }+ s/[\r\n]+$//;+ print_line_with_options( $filename, $_, $., ':' );+ $has_printed_for_this_file = 1;+ $nmatches++;+ $max_count--;+ $last_match_lineno = $.;+ }+ last if $max_count == 0;+ }+ }++ return $nmatches;+}+++sub print_line_with_options {+ my ( $filename, $line, $lineno, $separator ) = @_;++ $has_printed_something = 1;+ $printed_lineno = $lineno;++ my @line_parts;++ # Figure out how many spaces are used per line for the ANSI coloring.+ state $chars_used_by_coloring;+ if ( !defined($chars_used_by_coloring) ) {+ $chars_used_by_coloring = 0;+ if ( $opt_color ) {+ my $filename_uses = length( Term::ANSIColor::colored( 'x', $ENV{ACK_COLOR_FILENAME} ) ) - 1;+ my $lineno_uses = length( Term::ANSIColor::colored( 'x', $ENV{ACK_COLOR_LINENO} ) ) - 1;+ if ( $opt_heading ) {+ $chars_used_by_coloring = $lineno_uses;+ }+ else {+ $chars_used_by_coloring = $filename_uses + $lineno_uses;+ }+ if ( $opt_column ) {+ $chars_used_by_coloring += length( Term::ANSIColor::colored( 'x', $ENV{ACK_COLOR_LINENO} ) ) - 1;+ }+ }+ }++ if ( $opt_show_filename ) {+ my $colno;+ $colno = get_match_colno() if $opt_column;+ if ( $opt_color ) {+ $filename = Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} );+ $lineno = Term::ANSIColor::colored( $lineno, $ENV{ACK_COLOR_LINENO} );+ $colno = Term::ANSIColor::colored( $colno, $ENV{ACK_COLOR_COLNO} ) if $opt_column;+ }+ if ( $opt_heading ) {+ push @line_parts, $lineno;+ push @line_parts, $colno if $opt_column;+ }+ else {+ push @line_parts, $filename, $lineno;+ push @line_parts, $colno if $opt_column;+ }+ }++ if ( $opt_output ) {+ while ( $line =~ /$opt_regex/og ) {+ my $output = $opt_output;+ if ( @special_vars_used_by_opt_output ) {+ no strict;++ # Stash copies of the special variables because we can't rely+ # on them not changing in the process of doing the s///.++ my %keep = map { ($_ => ${$_} // '') } @special_vars_used_by_opt_output;+ $keep{_} = $line if exists $keep{_}; # Manually set it because $_ gets reset in a map.+ $keep{f} = $filename if exists $keep{f};+ my $special_vars_used_by_opt_output = join( '', @special_vars_used_by_opt_output );+ $output =~ s/\$([$special_vars_used_by_opt_output])/$keep{$1}/ego;+ }+ App::Ack::say( join( $separator, @line_parts, $output ) );+ }+ }+ else {+ my $underline = '';++ # We have to do underlining before any highlighting because highlighting modifies string length.+ if ( $opt_underline ) {+ while ( $line =~ /$opt_regex/og ) {+ my $match_start = $-[0];+ next unless defined($match_start);++ my $match_end = $+[0];+ my $match_length = $match_end - $match_start;+ last if $match_length <= 0;++ my $spaces_needed = $match_start - length $underline;++ $underline .= (' ' x $spaces_needed);+ $underline .= ('^' x $match_length);+ }+ }+ if ( $opt_color ) {+ my $highlighted = 0; # If highlighted, need to escape afterwards.++ while ( $line =~ /$opt_regex/og ) {+ my $match_start = $-[0];+ next unless defined($match_start);++ my $match_end = $+[0];+ my $match_length = $match_end - $match_start;+ last if $match_length <= 0;++ if ( $opt_color ) {+ my $substring = substr( $line, $match_start, $match_length );+ my $substitution = Term::ANSIColor::colored( $substring, $ENV{ACK_COLOR_MATCH} );++ # Fourth argument replaces the string specified by the first three.+ substr( $line, $match_start, $match_length, $substitution );++ # Move the offset of where /g left off forward the number of spaces of highlighting.+ pos($line) = $match_end + (length( $substitution ) - length( $substring ));+ $highlighted = 1;+ }+ }+ # Reset formatting and delete everything to the end of the line.+ $line .= "\e[0m\e[K" if $highlighted;+ }++ push @line_parts, $line;+ App::Ack::say( join( $separator, @line_parts ) );++ if ( $underline ne '' ) {+ pop @line_parts; # Leave only the stuff on the left.+ if ( @line_parts ) {+ my $stuff_on_the_left = join( $separator, @line_parts );+ my $spaces_needed = length($stuff_on_the_left) - $chars_used_by_coloring + 1;++ App::Ack::print( ' ' x $spaces_needed );+ }+ App::Ack::say( $underline );+ }+ }++ return;+}++sub print_line_with_context {+ my ( $filename, $matching_line, $lineno ) = @_;++ $matching_line =~ s/[\r\n]+$//;++ # Check if we need to print context lines first.+ if ( $opt_A || $opt_B ) {+ my $before_unprinted = $lineno - $printed_lineno - 1;+ if ( !$is_first_match && ( !$printed_lineno || $before_unprinted > $n_before_ctx_lines ) ) {+ App::Ack::say( '--' );+ }++ # 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, $lineno-$before_unprinted, '-' );+ $before_unprinted--;+ }+ }++ print_line_with_options( $filename, $matching_line, $lineno, ':' );++ # We want to get the next $n_after_ctx_lines printed.+ $after_context_pending = $n_after_ctx_lines;++ $is_first_match = 0;++ return;+}++}++sub get_match_colno {+ return $match_colno;+}++# Same as C<count_matches_in_file>, but exits as soon as there is a match.+sub file_has_match {+ my ( $file ) = @_;++ my $has_match = 0;+ my $fh = $file->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $file->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ chomp;+ if (/$opt_regex/o xor $opt_v) {+ $has_match = 1;+ last;+ }+ }+ $file->close;+ }++ return $has_match;+}++sub count_matches_in_file {+ my ( $file ) = @_;++ my $nmatches = 0;+ my $fh = $file->open;+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $file->name . ': ' . $! );+ }+ }+ else {+ while ( <$fh> ) {+ ++$nmatches if (/$opt_regex/o xor $opt_v);+ }+ $file->close;+ }++ return $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 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-colno=I<color>>++Sets the color to be used for column numbers.++=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.++Note that the list of files is B<not> filtered in any way. If you+add C<--type=html> in addition to C<--files-from>, the C<--type> will+be ignored.+++=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>++Print a short help statement.++=item B<--help-types>++Print all known types.++=item B<--help-colors>++Print a chart of various color combinations.++=item B<--help-rgb-colors>++Like B<--help-colors> but with more precise RGB colors.++=item B<-i>, B<--ignore-case>++Ignore case distinctions in PATTERN. Overrides B<--smart-case> and B<-I>.++=item B<-I>, B<--no-ignore-case>++Turns on case distinctions in PATTERN. Overrides B<--smart-case> and B<-i>.++=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<FILTER:ARGS>>++Ignore files matching I<FILTER:ARGS>. 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.++=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>>++Print only I<NUM> matches out of each file. If you want to stop ack+after printing the first match of any kind, use the B<-1> options.++=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). This is exactly the same as C<--output=$&>.++=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.++I<expr> may contain the strings "\n", "\r" and "\t", which will be+expanded to their corresponding characters line feed, carriage return+and tab, respectively.++I<expr> may also contain the following Perl special variables:++=over 4++=item C<$1> through C<$9>++The subpattern from the corresponding set of capturing parentheses.+If your pattern is C<(.+) and (.+)>, and the string is "this and+that', then C<$1> is "this" and C<$2> is "that".++=item C<$_>++The contents of the line in the file.++=item C<$.>++The number of the line in the file.++=item C<$&>, C<$`> and C<$'>++C<$&> is the the string matched by the pattern, C<$`> is what+precedes the match, and C<$'> is what follows it. If the pattern+is C<gra(ph|nd)> and the string is "lexicographic", then C<$&> is+"graph", C<$`> is "lexico" and C<$'> is "ic".++Use of these variables in your output will slow down the pattern+matching.++=item C<$+>++The match made by the last parentheses that matched in the pattern.+For example, if your pattern is C<Version: (.+)|Revision: (.+)>,+then C<$+> will contain whichever set of parentheses matched.++=item C<$f>++C<$f> is available, in C<--output> only, to insert the filename.+This is a stand-in for the discovered C<$filename> usage in old C<< ack2 --output >>,+which is disallowed with C<ack3> improved security.++The intended usage is to provide the grep or compile-error syntax needed for editor/IDE go-to-line integration,+e.g. C<--output=$f:$.:$_> or C<--output=$f\t$.\t$&>++=back++=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 F<~/.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 B<-f>, B<-g>, B<-l> or B<-c>, options+that only list filenames. 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<-p[N]>, B<--proximate[=N]>++Groups together match lines that are within N lines of each other.+This is useful for visually picking out matches that appear close+to other matches.++For example, if you got these results without the C<--proximate> option,++ 15: First match+ 18: Second match+ 19: Third match+ 37: Fourth match++they would look like this with C<--proximate=1>++ 15: First match++ 18: Second match+ 19: Third match++ 37: Fourth match++and this with C<--proximate=3>.++ 15: First match+ 18: Second match+ 19: Third match++ 37: Fourth match++If N is omitted, N is set to 1.++=item B<-P>++Negates the effect of the B<--proximate> option. Shortcut for B<--proximate=0>.++=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<-S>, 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 the vim text editor.+The options overrides B<-i> and B<-I>.++B<-S> is a synonym for B<--smart-case>.++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<ARGS>>++Files with the given ARGS 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<ARGS>>++Files with the given ARGS 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<--[no]underline>++Turns on underlining of matches, where "underlining" is printing a line of+carets under the match.++ $ ack -u foo+ peanuts.txt+ 17: Come kick the football you fool+ ^^^ ^^^+ 623: Price per square foot+ ^^^++This is useful if you're dumping the results of an ack run into a text+file or printer that doesn't support ANSI color codes.++The setting of underline does not affect highlighting of matches.++=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.++=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.++Note that the list of files is B<not> filtered in any way. If you add+C<--type=html> in addition to C<-x>, the C<--type> will be ignored.++=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, ack offers additional+filter types. The generic syntax is+I<--type-set TYPE:FILTER:ARGS>; I<ARGS> 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++=head1 ACK COLORS++ack allows customization of the colors it uses when presenting matches+onscreen. It 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.++There are four different colors ack uses:++ Aspect Option Env. variable Default+ -------- ----------------- ------------------ ---------------+ filename --color-filename ACK_COLOR_FILENAME black on_yellow+ match --color-match ACK_COLOR_MATCH bold green+ line no. --color-lineno ACK COLOR_LINENO bold yellow+ column no. --color-colno ACK COLOR_COLNO bold yellow++The column number column is only used if the column number is shown because+of the --column option.++Colors may be specified by command-line option, such as+C<ack --color-filename='red on_white'>, or by setting an environment+variable, such as C<ACK_COLOR_FILENAME='red on_white'>. Options for colors+can be set in your ACKRC file (See "THE .ackrc FILE").++ack can understand the following colors for the foreground:++ black red green yellow blue magenta cyan white++The optional background color is specified by prepending "on_" to one of+the foreground colors:++ on_black on_red on_green on_yellow on_blue on_magenta on_cyan on_white++Each of the foreground colors can be modified with the following+attributes, which may or may not be supported by your terminal:++ bold faint italic underline blink reverse concealed++Any combinations of modifiers can be added to the foreground color. If your+terminal supports it, and you enjoy visual punishment, you can specify:++ ack --color-filename="blink italic underline bold red on_yellow"++For charts of the colors and what they look like, run C<ack --help-colors>+and C<ack --help-rgb-colors>.++If the eight standard colors, in their bold, faint and unmodified states,+aren't enough for you to choose from, you can also specify colors by their+RGB values. They are specified as "rgbXYZ" where X, Y, and Z are values+between 0 and 5 giving the intensity of red, green and blue, respectively.+Therefore, "rgb500" is pure red, "rgb505" is purple, and so on.++Background colors can be specified with the "on_" prefix prepended on an+RGB color, so that "on_rgb505" would be a purple background.++The modifier attributes of blink, italic, underscore and so on may or may+not work on the RGB colors.++For a chart of the 216 possible RGB colors, run C<ack --help-rgb-colors>.++=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_COLOR_COLNO++Color specification for the column number in ack's output. By default, the+column number is not shown. You have to enable it with the B<--column>+option. See the section "ack Colors" above.++=item ACK_COLOR_FILENAME++Color specification for the filename in ack's output. See the section "ack+Colors" above.++=item ACK_COLOR_LINENO++Color specification for the line number in ack's output. See the section+"ack Colors" above.++=item ACK_COLOR_MATCH++Color specification for the matched text in ack's output. See the section+"ack Colors" above.++=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 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 Try it with 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 for searching++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. You may be loading an F<.ackrc> file that you didn't know+you were loading.++=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 * Command line++Options are then loaded from the command line.++=back++=head1 BUGS & ENHANCEMENTS++ack is based at GitHub at L<https://github.com/beyondgrep/ack3>++Please report any bugs or feature requests to the issues list at+Github: L<https://github.com/beyondgrep/ack3/issues>.++Please include the operating system that you're using; the output of+the command C<ack --version>; and any customizations in your F<.ackrc>+you may have.++To suggest enhancements, please submit an issue at+L<https://github.com/beyondgrep/ack3/issues>. Also read the+F<DEVELOPERS.md> file in the ack code repository.++Also, feel free to discuss your issues on the ack mailing+list at L<https://groups.google.com/group/ack-users>.++=head1 SUPPORT++Support for and information about F<ack> can be found at:++=over 4++=item * The ack homepage++L<https://beyondgrep.com/>++=item * Source repository++L<https://github.com/beyondgrep/ack3>++=item * The ack issues list at Github++L<https://github.com/beyondgrep/ack3/issues>++=item * The ack announcements mailing list++L<http://groups.google.com/group/ack-announcement>++=item * The ack users' mailing list++L<http://groups.google.com/group/ack-users>++=item * The ack development mailing list++L<http://groups.google.com/group/ack-users>++=back++=head1 COMMUNITY++There are ack mailing lists and a Slack channel for ack. See+L<https://beyondgrep.com/community/> for details.++=head1 FAQ++This is the Frequently Asked Questions list for ack.++=head2 Can I stop using grep now?++Many people find I<ack> to be better than I<grep> as an everyday tool+99% of the time, but don't throw I<grep> away, because there are times+you'll still need it. For example, you might be looking through huge+log files and not using regular expressions. In that case, I<grep>+will probably perform better.++=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.++=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"> in the ack manual.++=head2 Will you make ack recognize F<.xyz> files by default?++We might, depending on how widely-used the file format is.++Submit an issue at in the GitHub issue queue at+L<https://github.com/beyondgrep/ack3/issues>. Explain what the file format+is, where we can find out more about it, and what you have been using+in your F<.ackrc> to support it.++Please do not bother creating a pull request. The code for filetypes+is trivial compared to the rest of the process we go through.++=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 submit an issue to GitHub, and/or bring it up on the ack-users+mailing list.++=cut++=head1 ACKNOWLEDGEMENTS++How appropriate to have I<ack>nowledgements!++Thanks to everyone who has contributed to ack in any way, including+M. Scott Ford,+Anders Eriksson,+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 AUTHOR++Andy Lester, C<< <andy at petdance.com> >>++=head1 COPYRIGHT & LICENSE++Copyright 2005-2019 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++1;+package App::Ack;++use warnings;+use strict;+++our $VERSION;+our $COPYRIGHT;+BEGIN {+ $VERSION = v3.0.1;+ $COPYRIGHT = 'Copyright 2005-2019 Andy Lester.';+}+our $STANDALONE = 0;+our $ORIGINAL_PROGRAM_NAME;++our $fh;++BEGIN {+ $fh = *STDOUT;+}+++our %types;+our %type_wanted;+our %mappings;+our %ignore_dirs;++our $is_filter_mode;+our $output_to_pipe;++our $is_windows;++our $debug_nopens = 0;++# Line ending, changes to "\0" if --print0.+our $ors = "\n";++BEGIN {+ # These have to be checked before any filehandle diddling.+ $output_to_pipe = not -t *STDOUT;+ $is_filter_mode = -p STDIN;++ $is_windows = ($^O eq 'MSWin32');+}+++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 thpppt {+ my $y = q{_ /|,\\'!.x',=(www)=, U };+ $y =~ tr/,x!w/\nOo_/;++ App::Ack::print( "$y ack $_[0]!\n" );+ exit 0;+}++sub ackbar {+ my $x;+ $x = <<'_BAR';+ 6?!I'7!I"?%+!+ 3~!I#7#I"7#I!?!+!="+"="+!:!+ 2?#I!7!I!?#I!7!I"+"=%+"=#+ 1?"+!?*+!=#~"=!+#?"="+!+ 0?"+!?"I"?&+!="~!=!~"=!+%="+"+ /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!+ .?%I"?%+%='?!=#~$="+ ,,!?%I"?(+$=$~!=#:"~$:!~!+ ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!+ +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"+ *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!+ *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"+ )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!+ (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!+ (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!+ %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"+ $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/+ $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!+ $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/+ %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!+ %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!+ %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!+ %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!+ &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!+ &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!+ &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!+ &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!+ &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"+ &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!+ %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&+ %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)+ $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!+ $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"+ "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!+ !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!+ !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*+7+:!,!~#,"=!7'I!?#I"7/+!7++77I!+!7!?!7!I"71+!7,+_BAR++ return _pic_decode($x);+}++sub cathy {+ my $x = <<'CATHY';+ 0+!--+!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! "C!H!O!C!O!L!A!T!E!!! !|!+ 0|! $A"C!K!!! $|!+ 0+!--+!+ 6\! 1:!,!.! !+ 7\! /.!M!~!Z!M!~!+ 8\! /~!D! "M! !+ 4.! $\! /M!~!.!8! +.!M# 4+ 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3+ /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4+ ..! !D!Z!.!Z!.! '\! 9=!M".! 6+ /.! !.!~!M".! '\! 8~! 9+ 4M!.! /.!7!N!M!.! F+ 4.! &:!M! !N"M# !M"N!M! #D!M&=! =+ :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;+ 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9+ (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0+ &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! ++ &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *+ 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! ++ 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4+ 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3+ /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1+ #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0+ $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '+ #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (+ #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %+ *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &+ )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '+ )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! ++ (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! ++ 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *+ %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )+ !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (+ !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &+ !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $+ !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $+ (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #+ '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %+ $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! ++ ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )+ +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (+ 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )+ 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /+ 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2+ 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %+ 0?!$! &N! )." .,! %."M! ":!M!.! 0+ 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0+CATHY+ return _pic_decode($x);+}++sub _pic_decode {+ my($compressed) = @_;+ $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;+ App::Ack::print( $compressed );+ exit 0;+}+++sub show_help {+ my $ack_ver = sprintf( 'v%vd', $VERSION );+ 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 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+ -S, --[no]smart-case Ignore case distinctions in PATTERN,+ only if PATTERN contains no upper case.+ Ignored if -i or -I are specified.+ -I, --no-ignore-case Turns on case-sensitivity in PATTERN.+ Negates -i and --smart-case.+ -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+ --match PATTERN Specify PATTERN explicitly. Typically omitted.++Search output:+ -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+ -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+ -p, --proximate=LINES Separate match output with blank lines unless+ they are within LINES lines from each other.+ -P, --proximate=0 Negates --proximate.+ --[no]underline Print a line of carets under the matched text.+ --[no]color, --[no]colour Highlight the matching text (default: on unless+ output is redirected, or on Windows)+ --color-filename=COLOR+ --color-match=COLOR+ --color-colno=COLOR+ --color-lineno=COLOR Set the color for filenames, matches, line and+ column numbers.+ --help-colors Show a list of possible color combinations.+ --help-rgb-colors Show a list of advanced RGB colors.+ --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:ARGS 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.++File type inclusion/exclusion:+ --type=X Include only X files, where X is a recognized+ filetype, e.g. --php, --ruby+ --type=noX Exclude X files, e.g. --nophp, --no-ruby.+ -k, --known-types Include only files of types that ack recognizes.+ --help-types Display all known types, and how they're defined.++File type specification:+ --type-set=TYPE:FILTER:ARGS Files with the given ARGS applied to the given+ FILTER are recognized as being of type TYPE.+ This replaces an existing definition for TYPE.+ --type-add=TYPE:FILTER:ARGS Files with the given ARGS applied to the given+ FILTER are recognized as being type TYPE.+ --type-del=TYPE Removes all filters associated with TYPE.++Miscellaneous:+ --version Display version & copyright+ --[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.+ --dump Dump information on which options are loaded+ and where they're defined.+ --[no]filter Force ack to treat standard input as a pipe+ (--filter) or tty (--nofilter)+ --help This help+ --man Print the manual.+ --help-types Display all known types, and how they're defined.+ --help-colors Show a list of possible color combinations.+ --help-rgb-colors Show a list of advanced RGB colors.+ --thpppt Bill the Cat+ --bar The warning admiral+ --cathy Chocolate! Chocolate! Chocolate!++Filter specifications:+ If FILTER is "ext", ARGS is a list of extensions checked against the+ file's extension.+ If FILTER is "is", ARGS must match the file's name exactly.+ If FILTER is "match", ARGS is matched as a case-insensitive regex+ against the filename.+ If FILTER is "firstlinematch", ARGS is matched as a regex the first+ line of the file's contents.++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 $ack_ver of ack. Run "ack --version" for full version info.+END_OF_HELP++ return;+ }++++sub show_help_types {+ App::Ack::print( <<'END_OF_HELP' );+Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]++The following is the list of filetypes supported by ack. You can+specify a file type with the --type=TYPE format, or the --TYPE+format. For example, both --type=perl and --perl work.++Note that some extensions may appear in multiple types. For example,+.pod files are both Perl and Parrot.++END_OF_HELP++ my @types = keys %App::Ack::mappings;+ 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_help_colors {+ App::Ack::print( <<'END_OF_HELP' );+ack allows customization of the colors it uses when presenting matches+onscreen. See the "ACK COLORS" section of the ack manual (ack --man).++Here is a chart of how various color combinations appear: Each of the eight+foreground colors, on each of the eight background colors or no background+color, with and without the bold modifier.++Run ack --help-rgb-colors for a chart of the RGB colors.++END_OF_HELP++ _show_color_grid();++ return;+}++++sub show_help_rgb {+ App::Ack::print( <<'END_OF_HELP' );+ack allows customization of the colors it uses when presenting matches+onscreen. See the "ACK COLORS" section of the ack manual (ack --man).++Colors may be specified as "rggNNN" where "NNN" is a triplet of digits+from 0 to 5 specifying the intensity of red, green and blue, respectively.++Here is a grid of the 216 possible values for NNN.++END_OF_HELP++ _show_rgb_grid();++ App::Ack::say( 'Here are the 216 possible colors with the "reverse" modifier applied.', "\n" );++ _show_rgb_grid( 'reverse' );++ return;+}+++sub _show_color_grid {+ my $cell_width = 7;++ my @fg_colors = qw( black red green yellow blue magenta cyan white );+ my @bg_colors = map { "on_$_" } @fg_colors;++ App::Ack::say(+ _color_cell( '' ),+ map { _color_cell( $_ ) } @fg_colors+ );++ App::Ack::say(+ _color_cell( '' ),+ map { _color_cell( '-' x $cell_width ) } @fg_colors+ );++ for my $bg ( '', @bg_colors ) {+ App::Ack::say(+ _color_cell( '' ),+ ( map { _color_cell( $_, "$_ $bg" ) } @fg_colors ),+ $bg+ );++ App::Ack::say(+ _color_cell( 'bold' ),+ ( map { _color_cell( $_, "bold $_ $bg" ) } @fg_colors ),+ $bg+ );+ App::Ack::say();+ }++ return;+}+++sub _color_cell {+ my $text = shift;+ my $color = shift;++ my $cell_width = 7;+ $text = sprintf( '%-*s', $cell_width, $text );++ return ($color ? Term::ANSIColor::colored( $text, $color ) : $text) . ' ';+}+++sub _show_rgb_grid {+ my $modifier = shift // '';++ my $grid = <<'HERE';+544 544 544 544 544 554 554 554 554 554 454 454 454 454 454 455 455 455 455 455 445 445 445 445 445 545 545 545 545 545+533 533 533 543 543 553 553 553 453 453 353 353 353 354 354 355 355 355 345 345 335 335 335 435 435 535 535 535 534 534+511 521 531 531 541 551 451 451 351 251 151 152 152 153 154 155 145 145 135 125 115 215 215 315 415 515 514 514 513 512+500 510 520 530 540 550 450 350 250 150 050 051 052 053 054 055 045 035 025 015 005 105 205 305 405 505 504 503 502 501+400 410 410 420 430 440 340 340 240 140 040 041 041 042 043 044 034 034 024 014 004 104 104 204 304 404 403 403 402 401+300 300 310 320 320 330 330 230 130 130 030 030 031 032 032 033 033 023 013 013 003 003 103 203 203 303 303 302 301 301+200 200 200 210 210 220 220 220 120 120 020 020 020 021 021 022 022 022 012 012 002 002 002 102 102 202 202 202 201 201+100 100 100 100 100 110 110 110 110 110 010 010 010 010 010 011 011 011 011 011 001 001 001 001 001 101 101 101 101 101++522 522 532 542 542 552 552 452 352 352 252 252 253 254 254 255 255 245 235 235 225 225 325 425 425 525 525 524 523 523++411 411 421 431 431 441 441 341 241 241 141 141 142 143 143 144 144 134 124 124 114 114 214 314 314 414 414 413 412 412++422 422 432 432 432 442 442 442 342 342 242 242 242 243 243 244 244 244 234 234 224 224 224 324 324 424 424 424 423 423++311 311 311 321 321 331 331 331 231 231 131 131 131 132 132 133 133 133 123 123 113 113 113 213 213 313 313 313 312 312++433 433 433 433 433 443 443 443 443 443 343 343 343 343 343 344 344 344 344 344 334 334 334 334 334 434 434 434 434 434+211 211 211 211 211 221 221 221 221 221 121 121 121 121 121 122 122 122 122 122 112 112 112 112 112 212 212 212 212 212++322 322 322 322 322 332 332 332 332 332 232 232 232 232 232 233 233 233 233 233 223 223 223 223 223 323 323 323 323 323++555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555 555+444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444 444+333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333 333+222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222 222+111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111 111+000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000+HERE++ $grid =~ s/(\d\d\d)/Term::ANSIColor::colored( "$1", "$modifier rgb$1" )/eg;++ App::Ack::say( $grid );++ return;+}+++sub show_man {+ require Pod::Usage;+ Pod::Usage::pod2usage({+ -input => $App::Ack::ORIGINAL_PROGRAM_NAME,+ -verbose => 2,+ -exitval => 0,+ });++ return;+}++++sub get_version_statement {+ require Config;++ my $copyright = $App::Ack::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 $perl_ver = sprintf( 'v%vd', $^V );+ my $ack_ver = sprintf( 'v%vd', $VERSION );++ my $build_type = $App::Ack::STANDALONE ? 'standalone version' : 'standard build';++ return <<"END_OF_VERSION";+ack $ack_ver ($build_type)+Running under Perl $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 { print {$fh} @_; return; }+sub say { print {$fh} @_, $ors; return; }+sub print_blank_line { print {$fh} "\n"; 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;+}+++sub show_types {+ my $file = shift;++ my @types = filetypes( $file );+ my $arrow = @types ? ' => ' : ' =>';+ App::Ack::say( $file->name, $arrow, join( ',', @types ) );++ return;+}+++sub filetypes {+ my ( $file ) = @_;++ my @matches;++ foreach my $k (keys %App::Ack::mappings) {+ my $filters = $App::Ack::mappings{$k};++ foreach my $filter (@{$filters}) {+ # Clone the file.+ my $clone = $file->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;+}+++1; # End of App::Ack+package App::Ack::ConfigDefault;++use warnings;+use strict;+++++sub options {+ return split( /\n/, _options_block() );+}+++sub options_clean {+ return grep { /./ && !/^#/ } options();+}+++sub _options_block {+ my $lines = <<'HERE';+# This is the default ackrc for ack version ==VERSION==.++# There are four different ways to match+#+# is: Match the filename exactly+#+# ext: Match the extension of the filename exactly+#+# match: Match the filename against a Perl regular expression+#+# firstlinematch: Match the first 250 characters of the first line+# of text against a Perl regular expression. This is only for+# the --type-add option.+++### Directories to ignore++# Bazaar+# http://bazaar.canonical.com/+--ignore-directory=is:.bzr++# Codeville+# http://freecode.com/projects/codeville+--ignore-directory=is:.cdv++# Interface Builder (Xcode)+# http://en.wikipedia.org/wiki/Interface_Builder+--ignore-directory=is:~.dep+--ignore-directory=is:~.dot+--ignore-directory=is:~.nib+--ignore-directory=is:~.plst++# Git+# http://git-scm.com/+--ignore-directory=is:.git+# When submodules are used, .git is a file.+--ignore-file=is:.git++# Mercurial+# http://mercurial.selenic.com/+--ignore-directory=is:.hg++# quilt+# http://directory.fsf.org/wiki/Quilt+--ignore-directory=is:.pc++# Subversion+# http://subversion.tigris.org/+--ignore-directory=is:.svn++# Monotone+# http://www.monotone.ca/+--ignore-directory=is:_MTN++# CVS+# http://savannah.nongnu.org/projects/cvs+--ignore-directory=is:CVS++# RCS+# http://www.gnu.org/software/rcs/+--ignore-directory=is:RCS++# SCCS+# http://en.wikipedia.org/wiki/Source_Code_Control_System+--ignore-directory=is:SCCS++# darcs+# http://darcs.net/+--ignore-directory=is:_darcs++# Vault/Fortress+--ignore-directory=is:_sgbak++# autoconf+# http://www.gnu.org/software/autoconf/+--ignore-directory=is:autom4te.cache++# Perl module building+--ignore-directory=is:blib+--ignore-directory=is:_build++# Perl Devel::Cover module's output directory+# https://metacpan.org/release/Devel-Cover+--ignore-directory=is:cover_db++# Node modules created by npm+--ignore-directory=is:node_modules++# CMake cache+# http://www.cmake.org/+--ignore-directory=is:CMakeFiles++# Eclipse workspace folder+# http://eclipse.org/+--ignore-directory=is:.metadata++# Cabal (Haskell) sandboxes+# http://www.haskell.org/cabal/users-guide/installing-packages.html+--ignore-directory=is:.cabal-sandbox++### Files to ignore++# Backup files+--ignore-file=ext:bak+--ignore-file=match:/~$/++# Emacs swap files+--ignore-file=match:/^#.+#$/++# vi/vim swap files http://vim.org/+--ignore-file=match:/[._].*\.swp$/++# core dumps+--ignore-file=match:/core\.\d+$/++# minified Javascript+--ignore-file=match:/[.-]min[.]js$/+--ignore-file=match:/[.]js[.]min$/++# minified CSS+--ignore-file=match:/[.]min[.]css$/+--ignore-file=match:/[.]css[.]min$/++# JS and CSS source maps+--ignore-file=match:/[.]js[.]map$/+--ignore-file=match:/[.]css[.]map$/++# PDFs, because they pass Perl's -T detection+--ignore-file=ext:pdf++# Common graphics, just as an optimization+--ignore-file=ext:gif,jpg,jpeg,png++# Common archives, as an optimization+--ignore-file=ext:gz,tar,tgz,zip+++### Filetypes defined++# 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+--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++# DOS/Windows batch+--type-add=batch:ext:bat,cmd++# ColdFusion+# http://en.wikipedia.org/wiki/ColdFusion+--type-add=cfmx:ext:cfc,cfm,cfml++# Clojure+# http://clojure.org/+--type-add=clojure:ext:clj,cljs,edn,cljc++# C+# .xs are Perl C files+--type-add=cc:ext:c,h,xs++# C header files+--type-add=hh:ext:h++# CoffeeScript+# http://coffeescript.org/+--type-add=coffeescript:ext:coffee++# C+++--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx++# C++ header files+--type-add=hpp:ext:hpp,hh,h,hxx++# C#+--type-add=csharp:ext:cs++# CSS+# http://www.w3.org/Style/CSS/+--type-add=css:ext:css++# Dart+# http://www.dartlang.org/+--type-add=dart:ext:dart++# Delphi+# http://en.wikipedia.org/wiki/Embarcadero_Delphi+--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj++# Elixir+# http://elixir-lang.org/+--type-add=elixir:ext:ex,exs++# Emacs Lisp+# http://www.gnu.org/software/emacs+--type-add=elisp:ext:el++# Erlang+# http://www.erlang.org/+--type-add=erlang:ext:erl,hrl++# Fortran+# http://en.wikipedia.org/wiki/Fortran+--type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp++# Go+# http://golang.org/+--type-add=go:ext:go++# Groovy+# http://groovy.codehaus.org/+--type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle++# GSP+# http://groovy.codehaus.org/GSP+--type-add=gsp:ext:gsp++# Haskell+# http://www.haskell.org/+--type-add=haskell:ext:hs,lhs++# HTML+--type-add=html:ext:htm,html,xhtml++# Jade+# http://jade-lang.com/+--type-add=jade:ext:jade++# Java+# http://www.oracle.com/technetwork/java/index.html+--type-add=java:ext:java,properties++# JavaScript+--type-add=js:ext:js++# JSP+# http://www.oracle.com/technetwork/java/javaee/jsp/index.html+--type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml++# JSON+# http://www.json.org/+--type-add=json:ext:json++# Kotlin+# https://kotlinlang.org/+--type-add=kotlin:ext:kt,kts++# Less+# http://www.lesscss.org/+--type-add=less:ext:less++# Common Lisp+# http://common-lisp.net/+--type-add=lisp:ext:lisp,lsp++# Lua+# http://www.lua.org/+--type-add=lua:ext:lua+--type-add=lua:firstlinematch:/^#!.*\blua(jit)?/++# Markdown+# https://en.wikipedia.org/wiki/Markdown+--type-add=markdown:ext:md,markdown+# We understand that there are many ad hoc extensions for markdown+# that people use. .md and .markdown are the two that ack recognizes.+# You are free to add your own in your ackrc file.++# Matlab+# http://en.wikipedia.org/wiki/MATLAB+--type-add=matlab:ext:m++# 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++# Parrot+# http://www.parrot.org/+--type-add=parrot:ext:pir,pasm,pmc,ops,pod,pg,tg++# 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++# Perl's Plain Old Documentation format, POD+--type-add=pod:ext:pod++# 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++# SVG+# https://en.wikipedia.org/wiki/Scalable_Vector_Graphics+--type-add=svg:ext:svg++# Swift+# https://developer.apple.com/swift/+--type-add=swift:ext:swift+--type-add=swift:firstlinematch:/^#!.*\bswift/++# Tcl+# http://www.tcl.tk/+--type-add=tcl:ext:tcl,itcl,itk++# TeX & LaTeX+# http://www.latex-project.org/+--type-add=tex:ext:tex,cls,sty++# Template Toolkit (Perl)+# http://template-toolkit.org/+--type-add=ttml:ext:tt,tt2,ttml++# Typescript+# http://www.typescriptlang.org/+--type-add=ts:ext:ts,tsx++# Visual Basic+--type-add=vb:ext:bas,cls,frm,ctl,vb,resx++# Verilog+--type-add=verilog:ext:v,vh,sv++# VHDL+# http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome+--type-add=vhdl:ext:vhd,vhdl++# Vim+# http://www.vim.org/+--type-add=vim:ext:vim++# XML+# http://www.w3.org/TR/REC-xml/+--type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl+--type-add=xml:firstlinematch:/<[?]xml/++# YAML+# http://yaml.org/+--type-add=yaml:ext:yaml,yml+HERE+ $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;++ return $lines;+}++1;+package App::Ack::ConfigFinder;+++use strict;+use warnings;++use Cwd 3.00 ();+use File::Spec 3.00 ();++use if ($^O eq 'MSWin32'), 'Win32';+++sub new {+ my ( $class ) = @_;++ return bless {}, $class;+}+++sub _remove_redundancies {+ my @configs = @_;++ my %seen;+ my @uniq;+ foreach my $config (@configs) {+ my $path = $config->{path};+ my $key = -e $path ? Cwd::realpath( $path ) : $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;+ }+ push( @uniq, $config ) unless $seen{$key}++;+ }+ return @uniq;+}+++sub _check_for_ackrc {+ return unless defined $_[0];++ my @files = grep { -f }+ map { File::Spec->catfile(@_, $_) }+ qw(.ackrc _ackrc);++ App::Ack::die( File::Spec->catdir(@_) . ' contains both .ackrc and _ackrc. Please remove one of those files.' )+ if @files > 1;++ return wantarray ? @files : $files[0];+} # end _check_for_ackrc++++sub find_config_files {+ my @config_files;++ if ( $App::Ack::is_windows ) {+ push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (+ Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),+ Win32::GetFolderPath(Win32::CSIDL_APPDATA()),+ );+ }+ else {+ push @config_files, { path => '/etc/ackrc' };+ }+++ if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {+ push @config_files, { path => $ENV{'ACKRC'} };+ }+ else {+ push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});+ }++ my $cwd = Cwd::getcwd();+ return () unless defined $cwd;++ # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this.+ $cwd =~ /(.+)/;+ $cwd = $1;+ my @dirs = File::Spec->splitdir( $cwd );+ while ( @dirs ) {+ my $ackrc = _check_for_ackrc(@dirs);+ if ( defined $ackrc ) {+ push @config_files, { project => 1, path => $ackrc };+ last;+ }+ pop @dirs;+ }++ # We only test for existence here, so if the file is deleted out from under us, this will fail later.+ return _remove_redundancies( @config_files );+}++1;+package App::Ack::ConfigLoader;++use strict;+use warnings;+use 5.010;++use File::Spec 3.00 ();+use Getopt::Long 2.38 ();+use Text::ParseWords 3.1 ();++sub opt_parser {+ my @opts = @_;++ my @standard = qw(+ default+ bundling+ no_auto_help+ no_auto_version+ no_ignore_case+ );+ return Getopt::Long::Parser->new( config => [ @standard, @opts ] );+}++sub _generate_ignore_dir {+ my ( $option_name, $opt ) = @_;++ my $is_inverted = $option_name =~ /^--no/;++ return sub {+ my ( undef, $dir ) = @_;++ $dir = _remove_directory_separator( $dir );+ if ( $dir !~ /:/ ) {+ $dir = 'is:' . $dir;+ }++ my ( $filter_type, $args ) = split /:/, $dir, 2;++ if ( $filter_type eq 'firstlinematch' ) {+ App::Ack::die( 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 ) {+ # This relies on invert of an inverted filter to return the original.+ $collection = $collection->invert();+ }+ }+ else {+ $collection = App::Ack::Filter::Collection->new();+ push @{ $opt->{idirs} }, $is_inverted ? $collection->invert() : $collection;+ }++ $collection->add($filter);++ if ( $filter_type eq 'is' ) {+ $collection->add(App::Ack::Filter::IsPath->new($args));+ }+ };+}+++sub _remove_directory_separator {+ my $path = shift;++ state $dir_sep_chars = $App::Ack::is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );++ $path =~ s/[$dir_sep_chars]$//;++ return $path;+}+++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 {+ App::Ack::die( "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 ) = @_;++ 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,+ );++ my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );+ foreach my $source (@{$arg_sources}) {+ my $args = $source->{contents};++ if ( ref($args) ) {+ # $args are modified in place, so no need to munge $arg_sources+ $p->getoptionsfromarray( $args, %type_arg_specs );+ }+ else {+ ( undef, $source->{contents} ) =+ $p->getoptionsfromstring( $args, %type_arg_specs );+ }+ }++ $additional_specs{'k|known-types'} = sub {+ my @filters = map { @{$_} } values(%App::Ack::mappings);++ push @{ $opt->{'filters'} }, @filters;+ };++ return \%additional_specs;+}+++sub get_arg_spec {+ my ( $opt, $extra_specs ) = @_;+++ return {+ 1 => sub { $opt->{1} = $opt->{m} = 1 },+ 'A|after-context:-1' => sub { shift; $opt->{A} = _context_value(shift) },+ 'B|before-context:-1' => sub { shift; $opt->{B} = _context_value(shift) },+ 'C|context:-1' => sub { shift; $opt->{B} = $opt->{A} = _context_value(shift) },+ 'break!' => \$opt->{break},+ 'c|count' => \$opt->{c},+ 'color|colour!' => \$opt->{color},+ 'color-match=s' => \$ENV{ACK_COLOR_MATCH},+ 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},+ 'color-colno=s' => \$ENV{ACK_COLOR_COLNO},+ 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},+ 'column!' => \$opt->{column},+ 'create-ackrc' => sub { say 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 => sub { $| = 1 },+ 'follow!' => \$opt->{follow},+ g => \$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' => sub { $opt->{i} = 1; $opt->{S} = 0; },+ 'I|no-ignore-case' => sub { $opt->{i} = 0; $opt->{S} = 0; },+ '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);+ },+ '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},+ 'p|proximate:1' => \$opt->{p},+ 'P' => sub { $opt->{p} = 0 },+ 'Q|literal' => \$opt->{Q},+ 'r|R|recurse' => sub { $opt->{n} = 0 },+ 's' => \$opt->{s},+ 'show-types' => \$opt->{show_types},+ 'S|smart-case!' => sub { my (undef,$value) = @_; $opt->{S} = $value; $opt->{i} = 0 if $value; },+ '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 {+ App::Ack::die( "Unknown type '$value'" );+ }+ },+ 'underline!' => \$opt->{underline},+ 'v|invert-match' => \$opt->{v},+ 'w|word-regexp' => \$opt->{w},+ 'x' => sub { $opt->{files_from} = '-' },++ 'help' => sub { App::Ack::show_help(); exit; },+ 'help-types' => sub { App::Ack::show_help_types(); exit; },+ 'help-colors' => sub { App::Ack::show_help_colors(); exit; },+ 'help-rgb-colors' => sub { App::Ack::show_help_rgb(); exit; },+ $extra_specs ? %{$extra_specs} : (),+ }; # arg_specs+}+++sub _context_value {+ my $val = shift;++ # Contexts default to 2.+ return (!defined($val) || ($val < 0)) ? 2 : $val;+}+++sub _process_other {+ my ( $opt, $extra_specs, $arg_sources ) = @_;++ my $argv_source;+ my $is_help_types_active;++ foreach my $source (@{$arg_sources}) {+ if ( $source->{name} eq 'ARGV' ) {+ $argv_source = $source->{contents};+ last;+ }+ }++ if ( $argv_source ) { # This *should* always be true, but you never know...+ my $p = opt_parser( 'pass_through' );+ $p->getoptionsfromarray( [ @{$argv_source} ],+ 'help-types' => \$is_help_types_active,+ );+ }++ my $arg_specs = get_arg_spec( $opt, $extra_specs );++ my $p = opt_parser();+ foreach my $source (@{$arg_sources}) {+ my ( $source_name, $args ) = @{$source}{qw/name contents/};++ my $args_for_source = { %{$arg_specs} };++ if ( $source->{is_ackrc} ) {+ my $illegal = sub {+ my $name = shift;+ App::Ack::die( "Option --$name is forbidden in .ackrc files." );+ };++ $args_for_source = {+ %{$args_for_source},+ 'output=s' => $illegal,+ 'match=s' => $illegal,+ };+ }+ if ( $source->{project} ) {+ my $illegal = sub {+ my $name = shift;+ App::Ack::die( "Option --$name is forbidden in project .ackrc files." );+ };++ $args_for_source = {+ %{$args_for_source},+ 'pager:s' => $illegal,+ };+ }++ my $ret;+ if ( ref($args) ) {+ $ret = $p->getoptionsfromarray( $args, %{$args_for_source} );+ }+ else {+ ( $ret, $source->{contents} ) =+ $p->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 _explode_sources {+ my ( $sources ) = @_;++ my @new_sources;++ my %opt;+ my $arg_spec = get_arg_spec( \%opt, {} );++ my $dummy_sub = sub {};+ my $add_type = sub {+ my ( undef, $arg ) = @_;++ if ( $arg =~ /(\w+)=/) {+ $arg_spec->{$1} = $dummy_sub;+ }+ else {+ ( $arg ) = split /:/, $arg;+ $arg_spec->{$arg} = $dummy_sub;+ }+ };++ my $del_type = sub {+ my ( undef, $arg ) = @_;++ delete $arg_spec->{$arg};+ };++ my $p = opt_parser( 'pass_through' );+ 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;+ $p->getoptionsfromarray( [@chunk],+ 'type-add=s' => $add_type,+ 'type-set=s' => $add_type,+ 'type-del=s' => $del_type,+ %{$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 = $source->{name};+ if ( not $opts_by_source{$name} ) {+ $opts_by_source{$name} = [];+ push @source_names, $name;+ }+ push @{$opts_by_source{$name}}, $source->{contents};+ }++ foreach my $name (@source_names) {+ my $contents = $opts_by_source{$name};++ say $name;+ say '=' x length($name);+ say ' ', join(' ', @{$_}) for 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;++ my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );++ foreach my $index ( $default_index + 1 .. $#{$sources} ) {+ my $args = $sources->[$index]->{contents};++ if (ref($args)) {+ $p->getoptionsfromarray( $args,+ 'ignore-ack-defaults' => \$should_remove,+ );+ }+ else {+ ( undef, $sources->[$index]{contents} ) = $p->getoptionsfromstring( $args,+ 'ignore-ack-defaults' => \$should_remove,+ );+ }+ }++ return $sources unless $should_remove;++ my @copy = @{$sources};+ splice @copy, $default_index, 1;+ return \@copy;+}+++sub process_args {+ my $arg_sources = \@_;++ my %opt = (+ pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},+ );++ $arg_sources = _remove_default_options_if_needed($arg_sources);++ # Check for --dump early.+ foreach my $source (@{$arg_sources}) {+ if ( $source->{name} eq 'ARGV' ) {+ my $dump;+ my $p = opt_parser( 'pass_through' );+ $p->getoptionsfromarray( $source->{contents},+ 'dump' => \$dump,+ );+ if ( $dump ) {+ _dump_options($arg_sources);+ exit(0);+ }+ }+ }++ my $type_specs = _process_filetypes(\%opt, $arg_sources);++ _check_for_mutually_exclusive_options( $type_specs );++ _process_other(\%opt, $type_specs, $arg_sources);+ while ( @{$arg_sources} ) {+ my $source = shift @{$arg_sources};+ my $args = $source->{contents};++ # All of our sources should be transformed into an array ref+ if ( ref($args) ) {+ my $source_name = $source->{name};+ if ( $source_name eq 'ARGV' ) {+ @ARGV = @{$args};+ }+ elsif (@{$args}) {+ App::Ack::die( "Source '$source_name' has extra arguments!" );+ }+ }+ else {+ App::Ack::die( '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;++ my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );+ $p->getoptions(+ 'noenv' => \$noenv,+ 'ackrc=s' => \$ackrc,+ );++ 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 {+ App::Ack::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 = read_rcfile($file->{path});+ if ( @lines ) {+ push @arg_sources, {+ name => $file->{path},+ contents => \@lines,+ project => $file->{project},+ is_ackrc => 1,+ };+ }+ }++ push @arg_sources, {+ name => 'ARGV',+ contents => [ @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 ( defined( 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;+}+++# Verifies no mutually-exclusive options were passed. Dies if they were.+sub _check_for_mutually_exclusive_options {+ my $type_specs = shift;++ my $mutex = mutex_options();++ my ($raw,$used) = _options_used( $type_specs );++ my @used = sort { lc $a cmp lc $b } keys %{$used};++ for my $i ( @used ) {+ for my $j ( @used ) {+ next if $i eq $j;+ if ( $mutex->{$i}{$j} ) {+ my $x = $raw->[ $used->{$i} ];+ my $y = $raw->[ $used->{$j} ];+ App::Ack::die( "Options '$x' and '$y' are mutually exclusive" );+ }+ }+ }++ return;+}+++# Processes the command line option and returns a hash of the options that were+# used on the command line, using their full name. "--prox" shows up in the hash as "--proximate".+sub _options_used {+ my $type_specs = shift;++ my %dummy_opt;+ my $real_spec = get_arg_spec( \%dummy_opt, $type_specs );++ # The real argument parsing doesn't check for --type-add, --type-del or --type-set because+ # they get removed by the argument processing. We have to account for them here.+ my $sub_dummy = sub {};+ $real_spec = {+ %{$real_spec},+ 'type-add=s' => $sub_dummy,+ 'type-del=s' => $sub_dummy,+ 'type-set=s' => $sub_dummy,+ 'ignore-ack-defaults' => $sub_dummy,+ };++ my %parsed;+ my @raw;+ my %spec_capture_parsed;+ my %spec_capture_raw;+++ # Capture the %parsed hash.+ CAPTURE_PARSED: {+ my $parsed_pos = 0;+ my $sub_count = sub {+ my $arg = shift;+ $arg = "$arg";+ $parsed{$arg} = $parsed_pos++;+ };+ %spec_capture_parsed = (+ '<>' => sub { $parsed_pos++ }, # Bump forward one pos for non-options.+ map { $_ => $sub_count } keys %{$real_spec}+ );+ }++ # Capture the @raw array.+ CAPTURE_RAW: {+ my $raw_pos = 0;+ %spec_capture_raw = (+ '<>' => sub { $raw_pos++ }, # Bump forward one pos for non-options.+ );++ my $sub_count = sub {+ my $arg = shift;++ $arg = "$arg";+ $raw[$raw_pos] = length($arg) == 1 ? "-$arg" : "--$arg";+ $raw_pos++;+ };++ for my $opt_spec ( keys %{$real_spec} ) {+ my $negatable;+ my $type;+ my $default;++ $negatable = ($opt_spec =~ s/!$//);++ if ( $opt_spec =~ s/(=[si])$// ) {+ $type = $1;+ }+ if ( $opt_spec =~ s/(:.+)$// ) {+ $default = $1;+ }++ my @aliases = split( /\|/, $opt_spec );+ for my $alias ( @aliases ) {+ $alias .= $type if defined $type;+ $alias .= $default if defined $default;+ $alias .= '!' if $negatable;++ $spec_capture_raw{$alias} = $sub_count;+ }+ }+ }++ # Parse @ARGV twice, once with each capture spec.+ my $p = opt_parser( 'pass_through' ); # Ignore invalid options.+ $p->getoptionsfromarray( [@ARGV], %spec_capture_raw );+ $p->getoptionsfromarray( [@ARGV], %spec_capture_parsed );++ return (\@raw,\%parsed);+}+++sub mutex_options {+ # This list is machine-generated by crank-mutex. Do not modify it by hand.++ return {+ 1 => {+ m => 1,+ passthru => 1,+ },+ A => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ },+ B => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ },+ C => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ },+ H => {+ L => 1,+ f => 1,+ g => 1,+ l => 1,+ },+ L => {+ A => 1,+ B => 1,+ C => 1,+ H => 1,+ L => 1,+ break => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ group => 1,+ h => 1,+ heading => 1,+ l => 1,+ 'no-filename' => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ 'show-types' => 1,+ v => 1,+ 'with-filename' => 1,+ },+ break => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ },+ c => {+ A => 1,+ B => 1,+ C => 1,+ L => 1,+ break => 1,+ column => 1,+ f => 1,+ g => 1,+ group => 1,+ heading => 1,+ m => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ },+ column => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ passthru => 1,+ v => 1,+ },+ f => {+ A => 1,+ B => 1,+ C => 1,+ H => 1,+ L => 1,+ break => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ group => 1,+ h => 1,+ heading => 1,+ l => 1,+ m => 1,+ match => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ u => 1,+ v => 1,+ x => 1,+ },+ g => {+ A => 1,+ B => 1,+ C => 1,+ H => 1,+ L => 1,+ break => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ group => 1,+ h => 1,+ heading => 1,+ l => 1,+ m => 1,+ match => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ u => 1,+ x => 1,+ },+ group => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ },+ h => {+ L => 1,+ f => 1,+ g => 1,+ l => 1,+ },+ heading => {+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ },+ l => {+ A => 1,+ B => 1,+ C => 1,+ H => 1,+ L => 1,+ break => 1,+ column => 1,+ f => 1,+ g => 1,+ group => 1,+ h => 1,+ heading => 1,+ l => 1,+ 'no-filename' => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ 'show-types' => 1,+ 'with-filename' => 1,+ },+ m => {+ 1 => 1,+ c => 1,+ f => 1,+ g => 1,+ passthru => 1,+ },+ match => {+ f => 1,+ g => 1,+ },+ 'no-filename' => {+ L => 1,+ l => 1,+ },+ o => {+ A => 1,+ B => 1,+ C => 1,+ L => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ 'show-types' => 1,+ },+ output => {+ A => 1,+ B => 1,+ C => 1,+ L => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ 'show-types' => 1,+ u => 1,+ },+ p => {+ A => 1,+ B => 1,+ C => 1,+ L => 1,+ c => 1,+ f => 1,+ g => 1,+ l => 1,+ o => 1,+ output => 1,+ p => 1,+ passthru => 1,+ },+ passthru => {+ 1 => 1,+ A => 1,+ B => 1,+ C => 1,+ L => 1,+ c => 1,+ column => 1,+ f => 1,+ g => 1,+ l => 1,+ m => 1,+ o => 1,+ output => 1,+ p => 1,+ },+ 'show-types' => {+ L => 1,+ l => 1,+ o => 1,+ output => 1,+ },+ u => {+ f => 1,+ g => 1,+ output => 1,+ },+ v => {+ L => 1,+ column => 1,+ f => 1,+ },+ 'with-filename' => {+ L => 1,+ l => 1,+ },+ x => {+ f => 1,+ g => 1,+ },+ };+} # End of mutex_options()+++1; # End of App::Ack::ConfigLoader+package App::Ack::File;++use warnings;+use strict;++use File::Spec ();+++sub new {+ my $class = shift;+ my $filename = shift;++ my $self = bless {+ filename => $filename,+ fh => undef,+ }, $class;++ if ( $self->{filename} eq '-' ) {+ $self->{fh} = *STDIN;+ }++ return $self;+}++++sub name {+ return $_[0]->{filename};+}++++sub basename {+ my ( $self ) = @_;++ # XXX Definedness? Pre-populate the slot with an undef?+ unless ( exists $self->{basename} ) {+ $self->{basename} = (File::Spec->splitpath($self->name))[2];+ }++ return $self->{basename};+}++++sub open {+ my ( $self ) = @_;++ if ( !$self->{fh} ) {+ if ( open $self->{fh}, '<', $self->{filename} ) {+ # Do nothing.+ }+ else {+ $self->{fh} = undef;+ }+ }++ return $self->{fh};+}++++sub reset {+ my $self = shift;++ if ( defined($self->{fh}) ) {+ return unless -f $self->{fh};++ if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( "$self->{filename}: $!" );+ }+ }++ return;+}++++sub close {+ my $self = shift;++ if ( $self->{fh} ) {+ if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name() . ": $!" );+ }+ $self->{fh} = undef;+ }++ return;+}++++sub clone {+ my ( $self ) = @_;++ return __PACKAGE__->new($self->name);+}++++sub firstliney {+ my ( $self ) = @_;++ if ( !exists $self->{firstliney} ) {+ my $fh = $self->open();+ if ( !$fh ) {+ if ( $App::Ack::report_bad_filenames ) {+ App::Ack::warn( $self->name . ': ' . $! );+ }+ $self->{firstliney} = '';+ }+ else {+ my $buffer;+ my $rc = sysread( $fh, $buffer, 250 );+ if ( $rc ) {+ $buffer =~ s/[\r\n].*//s;+ }+ else {+ if ( !defined($rc) ) {+ App::Ack::warn( $self->name . ': ' . $! );+ }+ $buffer = '';+ }+ $self->{firstliney} = $buffer;+ $self->reset;+ }+ }++ return $self->{firstliney};+}++1;+package App::Ack::Files;++++use warnings;+use strict;+use 5.010;+++sub from_argv {+ my $class = shift;+ my $opt = shift;+ my $start = shift;++ my $self = bless {}, $class;++ 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(),+ warning_handler => sub {},+ sort_files => $opt->{sort_files},+ follow_symlinks => $opt->{follow},+ }, @{$start} );++ return $self;+}+++sub from_file {+ my $class = shift;+ my $opt = shift;+ my $file = shift;++ my $error_handler = _generate_error_handler();+ my $iter =+ File::Next::from_file( {+ error_handler => $error_handler,+ warning_handler => $error_handler,+ sort_files => $opt->{sort_files},+ }, $file ) or return undef;++ return bless {+ iter => $iter,+ }, $class;+}+++++sub from_stdin {+ my $class = shift;++ my $self = bless {}, $class;++ $self->{iter} = sub {+ state $has_been_called = 0;++ if ( !$has_been_called ) {+ $has_been_called = 1;+ return '-';+ }+ return;+ };++ return $self;+}+++sub next {+ my $self = shift;++ my $file = $self->{iter}->();++ return unless defined($file);++ return App::Ack::File->new( $file );+}+++sub _generate_error_handler {+ if ( $App::Ack::report_bad_filenames ) {+ return sub {+ my $msg = shift;+ App::Ack::warn( $msg );+ };+ }+ else {+ return sub {};+ }+}++1;+package App::Ack::Filter;++use strict;+use warnings;+++my %filter_types;+++sub create_filter {+ my ( undef, $type, @args ) = @_;++ if ( my $package = $filter_types{$type} ) {+ return $package->new(@args);+ }+ my $allowed_types = join( ', ', sort keys %filter_types );+ App::Ack::die( "Unknown filter type '$type'. Type must be one of: $allowed_types." );+}+++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 {+ return '(unimplemented to_string)';+}+++sub inspect {+ my ( $self ) = @_;++ return ref($self);+}++1;+package App::Ack::Filter::Collection;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ groups => {},+ ungrouped => [],+ }, $class;+}++sub filter {+ my ( $self, $file ) = @_;++ for my $group (values %{$self->{groups}}) {+ return 1 if $group->filter($file);+ }++ for my $filter (@{$self->{ungrouped}}) {+ return 1 if $filter->filter($file);+ }++ 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 ) = @_;++ return join(', ', map { "($_)" } @{$self->{ungrouped}});+}++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 ( undef, $file ) = @_;++ return -T $file->name;+}++1;+package App::Ack::Filter::Extension;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}+++sub new {+ my ( $class, @extensions ) = @_;++ my $exts = join('|', map { "\Q$_\E"} @extensions);+ my $re = qr/[.](?:$exts)$/i;++ return bless {+ extensions => \@extensions,+ regex => $re,+ groupname => 'ExtensionGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::ExtensionGroup->new();+}++sub filter {+ my ( $self, $file ) = @_;++ return $file->name =~ /$self->{regex}/;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . ' - ' . $self->{regex};+}++sub to_string {+ my ( $self ) = @_;++ return join( ' ', map { ".$_" } @{$self->{extensions}} );+}++BEGIN {+ App::Ack::Filter->register_filter(ext => __PACKAGE__);+}++1;+package App::Ack::Filter::ExtensionGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ foreach my $ext (@{$filter->{extensions}}) {+ $self->{data}->{lc $ext} = 1;+ }++ return;+}++sub filter {+ my ( $self, $file ) = @_;++ if ($file->name =~ /[.]([^.]*)$/) {+ return exists $self->{'data'}->{lc $1};+ }++ return 0;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', map { ".$_" } sort keys %{$self->{data}});+}++1;+package App::Ack::Filter::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, $file ) = @_;++ return $file->firstliney =~ /$self->{regex}/;+}++sub inspect {+ my ( $self ) = @_;+++ return ref($self) . ' - ' . $self->{regex};+}++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::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, $file ) = @_;++ return !$self->{filter}->filter( $file );+}++sub invert {+ my $self = shift;++ return $self->{'filter'};+}++sub is_inverted {+ return 1;+}++sub inspect {+ my ( $self ) = @_;++ my $filter = $self->{'filter'};++ return "!$filter";+}++1;+package App::Ack::Filter::Is;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++use File::Spec 3.00 ();++sub new {+ my ( $class, $filename ) = @_;++ return bless {+ filename => $filename,+ groupname => 'IsGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::IsGroup->new();+}++sub filter {+ my ( $self, $file ) = @_;++ return (File::Spec->splitpath($file->name))[2] eq $self->{filename};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . ' - ' . $self->{filename};+}++sub to_string {+ my ( $self ) = @_;++ return $self->{filename};+}++BEGIN {+ App::Ack::Filter->register_filter(is => __PACKAGE__);+}++1;+package App::Ack::Filter::IsGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $file ) = @_;++ return exists $self->{data}->{ $file->basename };+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;+package App::Ack::Filter::IsPath;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}+++sub new {+ my ( $class, $filename ) = @_;++ return bless {+ filename => $filename,+ groupname => 'IsPathGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::IsPathGroup->new();+}++sub filter {+ my ( $self, $file ) = @_;++ return $file->name eq $self->{filename};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . ' - ' . $self->{filename};+}++sub to_string {+ my ( $self ) = @_;++ return $self->{filename};+}++1;+package App::Ack::Filter::IsPathGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ data => {},+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ $self->{data}->{ $filter->{filename} } = 1;++ return;+}++sub filter {+ my ( $self, $file ) = @_;++ return exists $self->{data}->{$file->name};+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . " - $self";+}++sub to_string {+ my ( $self ) = @_;++ return join(' ', keys %{$self->{data}});+}++1;+package App::Ack::Filter::Match;++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,+ groupname => 'MatchGroup',+ }, $class;+}++sub create_group {+ return App::Ack::Filter::MatchGroup->new;+}++sub filter {+ my ( $self, $file ) = @_;++ return $file->basename =~ /$self->{regex}/;+}++sub inspect {+ my ( $self ) = @_;++ return ref($self) . ' - ' . $self->{regex};+}++sub to_string {+ my ( $self ) = @_;++ return "Filename matches $self->{regex}";+}++BEGIN {+ App::Ack::Filter->register_filter(match => __PACKAGE__);+}++1;+package App::Ack::Filter::MatchGroup;+++use strict;+use warnings;+BEGIN {+ our @ISA = 'App::Ack::Filter';+}++sub new {+ my ( $class ) = @_;++ return bless {+ matches => [],+ big_re => undef,+ }, $class;+}++sub add {+ my ( $self, $filter ) = @_;++ push @{ $self->{matches} }, $filter->{regex};++ my $re = join('|', map { "(?:$_)" } @{ $self->{matches} });+ $self->{big_re} = qr/$re/;++ return;+}++sub filter {+ my ( $self, $file ) = @_;++ return $file->basename =~ /$self->{big_re}/;+}++# This class has no inspect() or to_string() method.+# It will just use the default one unless someone writes something useful.++1;+package File::Next;++use strict;+use warnings;+++our $VERSION = '1.16';++++use File::Spec ();++our $name; # name of the current file+our $dir; # dir of the current file++our %files_defaults;+our %skip_dirs;++BEGIN {+ %files_defaults = (+ file_filter => undef,+ descend_filter => undef,+ error_handler => sub { CORE::die $_[0] },+ warning_handler => sub { CORE::warn @_ },+ sort_files => undef,+ follow_symlinks => 1,+ nul_separated => 0,+ );+ %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);+}+++sub files {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );++ return sub {+ my $filter = $parms->{file_filter};+ while (@queue) {+ my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );+ if ( -f $fullpath || -p _ || $fullpath =~ m{^/dev/fd} ) {+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ }+ if ( -d _ ) {+ unshift( @queue, _candidate_files( $parms, $fullpath ) );+ }+ } # while++ return;+ }; # iterator+}+++++++sub from_file {+ die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);++ my ($parms,@queue) = _setup( \%files_defaults, @_ );+ my $err = $parms->{error_handler};+ my $warn = $parms->{warning_handler};++ my $filename = $queue[1];++ if ( !defined($filename) ) {+ $err->( 'Must pass a filename to from_file()' );+ return undef;+ }++ my $fh;+ if ( $filename eq '-' ) {+ $fh = \*STDIN;+ }+ else {+ if ( !open( $fh, '<', $filename ) ) {+ $err->( "Unable to open $filename: $!", $! + 0 );+ return undef;+ }+ }++ return sub {+ my $filter = $parms->{file_filter};+ local $/ = $parms->{nul_separated} ? "\x00" : $/;+ while ( my $fullpath = <$fh> ) {+ chomp $fullpath;+ next unless $fullpath =~ /./;+ if ( not ( -f $fullpath || -p _ ) ) {+ $warn->( "$fullpath: No such file" );+ next;+ }++ my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );+ if ( $filter ) {+ local $_ = $file;+ local $File::Next::dir = $dirname;+ local $File::Next::name = $fullpath;+ next if not $filter->();+ }+ return wantarray ? ($dirname,$file,$fullpath) : $fullpath;+ } # while+ close $fh;++ return;+ }; # iterator+}++sub _bad_invocation {+ my $good = (caller(1))[3];+ my $bad = $good;+ $bad =~ s/(.+)::/$1->/;+ return "$good must not be invoked as $bad";+}++sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }+sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }++sub reslash {+ my $path = shift;++ my @parts = split( /\//, $path );++ return $path if @parts < 2;++ return File::Spec->catfile( @parts );+}++++sub _setup {+ my $defaults = shift;+ my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash++ my %passed_parms = %{$passed_parms};++ my $parms = {};+ for my $key ( keys %{$defaults} ) {+ $parms->{$key} =+ exists $passed_parms{$key}+ ? delete $passed_parms{$key}+ : $defaults->{$key};+ }++ # Any leftover keys are bogus+ for my $badkey ( keys %passed_parms ) {+ my $sub = (caller(1))[3];+ $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );+ }++ # If it's not a code ref, assume standard sort+ if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {+ $parms->{sort_files} = \&sort_standard;+ }+ my @queue;++ for ( @_ ) {+ my $start = reslash( $_ );+ if (-d $start) {+ push @queue, ($start,undef,$start);+ }+ else {+ push @queue, (undef,$start,$start);+ }+ }++ return ($parms,@queue);+}+++sub _candidate_files {+ my $parms = shift;+ my $dirname = shift;++ my $dh;+ if ( !opendir $dh, $dirname ) {+ $parms->{error_handler}->( "$dirname: $!", $! + 0 );+ return;+ }++ my @newfiles;+ my $descend_filter = $parms->{descend_filter};+ my $follow_symlinks = $parms->{follow_symlinks};+ my $sort_sub = $parms->{sort_files};++ for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {+ my $has_stat;++ my $fullpath = File::Spec->catdir( $dirname, $file );+ if ( !$follow_symlinks ) {+ next if -l $fullpath;+ $has_stat = 1;+ }++ # Only do directory checking if we have a descend_filter+ if ( $descend_filter ) {+ if ( $has_stat ? (-d _) : (-d $fullpath) ) {+ local $File::Next::dir = $fullpath;+ local $_ = $file;+ next if not $descend_filter->();+ }+ }+ if ( $sort_sub ) {+ push( @newfiles, [ $dirname, $file, $fullpath ] );+ }+ else {+ push( @newfiles, $dirname, $file, $fullpath );+ }+ }+ closedir $dh;++ if ( $sort_sub ) {+ return map { @{$_} } sort $sort_sub @newfiles;+ }++ return @newfiles;+}+++1; # End of File::Next