yd: Use yt-dlp instead of youtube-dl
[sunny256-utils.git] / ack
bloba54c9288ed67be52dfeffb744412fa8b9f193129
1 #!/usr/bin/env perl
3 # This file, ack, is generated code.
4 # Please DO NOT EDIT or send patches for it.
6 # Please take a look at the source from
7 # https://github.com/beyondgrep/ack3
8 # and submit patches against the individual files
9 # that build ack.
12 $App::Ack::STANDALONE = 1;
13 package main;
15 use strict;
16 use warnings;
18 our $VERSION = 'v3.2.0'; # Check https://beyondgrep.com/ for updates
20 use 5.010001;
22 use File::Spec ();
26 # Global command-line options
27 our $opt_1;
28 our $opt_A;
29 our $opt_B;
30 our $opt_break;
31 our $opt_color;
32 our $opt_column;
33 our $opt_debug;
34 our $opt_c;
35 our $opt_f;
36 our $opt_g;
37 our $opt_heading;
38 our $opt_L;
39 our $opt_l;
40 our $opt_m;
41 our $opt_output;
42 our $opt_passthru;
43 our $opt_p;
44 our $opt_range_start;
45 our $opt_range_end;
46 our $opt_range_invert;
47 our $opt_regex;
48 our $opt_show_filename;
49 our $opt_show_types;
50 our $opt_underline;
51 our $opt_v;
53 # Flag if we need any context tracking.
54 our $is_tracking_context;
56 # The regex that we search for in each file.
57 our $search_re;
59 # Special /m version of our $search_re.
60 our $scan_re;
62 our @special_vars_used_by_opt_output;
64 our $using_ranges;
66 # Internal stats for debugging.
67 our %stats;
69 MAIN: {
70 $App::Ack::ORIGINAL_PROGRAM_NAME = $0;
71 $0 = join(' ', 'ack', $0);
72 $App::Ack::ors = "\n";
73 if ( $App::Ack::VERSION ne $main::VERSION ) {
74 App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );
77 # Do preliminary arg checking;
78 my $env_is_usable = 1;
79 for my $arg ( @ARGV ) {
80 last if ( $arg eq '--' );
82 # Get the --thpppt, --bar, --cathy and --man checking out of the way.
83 $arg =~ /^--th[pt]+t+$/ and App::Ack::thpppt($arg);
84 $arg eq '--bar' and App::Ack::ackbar();
85 $arg eq '--cathy' and App::Ack::cathy();
87 # See if we want to ignore the environment. (Don't tell Al Gore.)
88 $arg eq '--env' and $env_is_usable = 1;
89 $arg eq '--noenv' and $env_is_usable = 0;
92 if ( $env_is_usable ) {
93 if ( $ENV{ACK_OPTIONS} ) {
94 App::Ack::warn( 'WARNING: ack no longer uses the ACK_OPTIONS environment variable. Use an ackrc file instead.' );
97 else {
98 my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
99 delete @ENV{@keys};
102 # Load colors
103 my $modules_loaded_ok = eval 'use Term::ANSIColor 1.10 (); 1;';
104 if ( $modules_loaded_ok && $App::Ack::is_windows ) {
105 $modules_loaded_ok = eval 'use Win32::Console::ANSI; 1;';
107 if ( $modules_loaded_ok ) {
108 $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
109 $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
110 $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
111 $ENV{ACK_COLOR_COLNO} ||= 'bold yellow';
114 my $p = App::Ack::ConfigLoader::opt_parser( 'no_auto_abbrev', 'pass_through' );
115 $p->getoptions(
116 help => sub { App::Ack::show_help(); exit; },
117 version => sub { App::Ack::print( App::Ack::get_version_statement() ); exit; },
118 man => sub { App::Ack::show_man(); },
121 if ( !@ARGV ) {
122 App::Ack::show_help();
123 exit 1;
126 my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();
128 my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );
130 $opt_1 = $opt->{1};
131 $opt_A = $opt->{A};
132 $opt_B = $opt->{B};
133 $opt_break = $opt->{break};
134 $opt_c = $opt->{c};
135 $opt_color = $opt->{color};
136 $opt_column = $opt->{column};
137 $opt_debug = $opt->{debug};
138 $opt_f = $opt->{f};
139 $opt_g = $opt->{g};
140 $opt_heading = $opt->{heading};
141 $opt_L = $opt->{L};
142 $opt_l = $opt->{l};
143 $opt_m = $opt->{m};
144 $opt_output = $opt->{output};
145 $opt_p = $opt->{p};
146 $opt_passthru = $opt->{passthru};
147 $opt_range_start = $opt->{range_start};
148 $opt_range_end = $opt->{range_end};
149 $opt_range_invert = $opt->{range_invert};
150 $opt_regex = $opt->{regex};
151 $opt_show_filename = $opt->{show_filename};
152 $opt_show_types = $opt->{show_types};
153 $opt_underline = $opt->{underline};
154 $opt_v = $opt->{v};
157 if ( $opt_range_start ) {
158 ($opt_range_start, undef) = build_regex( $opt_range_start, {} );
160 if ( $opt_range_end ) {
161 ($opt_range_end, undef) = build_regex( $opt_range_end, {} );
163 $using_ranges = $opt_range_start || $opt_range_end;
165 $App::Ack::report_bad_filenames = !$opt->{s};
166 $App::Ack::ors = $opt->{print0} ? "\0" : "\n";
168 if ( !defined($opt_color) && !$opt_g ) {
169 my $windows_color = 1;
170 if ( $App::Ack::is_windows ) {
171 $windows_color = eval { require Win32::Console::ANSI; };
173 $opt_color = !App::Ack::output_to_pipe() && $windows_color;
175 if ( not defined $opt_heading and not defined $opt_break ) {
176 $opt_heading = $opt_break = $opt->{break} = !App::Ack::output_to_pipe();
179 if ( defined($opt->{H}) || defined($opt->{h}) ) {
180 $opt_show_filename = $opt->{show_filename} = $opt->{H} && !$opt->{h};
183 if ( defined $opt_output ) {
184 # Expand out \t, \n and \r.
185 $opt_output =~ s/\\n/\n/g;
186 $opt_output =~ s/\\r/\r/g;
187 $opt_output =~ s/\\t/\t/g;
189 my @supported_special_variables = ( 1..9, qw( _ . ` & ' + f ) );
190 @special_vars_used_by_opt_output = grep { $opt_output =~ /\$$_/ } @supported_special_variables;
192 # If the $opt_output contains $&, $` or $', those vars won't be
193 # captured until they're used at least once in the program.
194 # Do the eval to make this happen.
195 for my $i ( @special_vars_used_by_opt_output ) {
196 if ( $i eq q{&} || $i eq q{'} || $i eq q{`} ) {
197 no warnings; # They will be undef, so don't warn.
198 eval qq{"\$$i"};
203 # Set up file filters.
204 my $files;
205 if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x
206 $files = App::Ack::Files->from_stdin();
207 $opt_regex //= shift @ARGV;
208 ($search_re, $scan_re) = build_regex( $opt_regex, $opt );
209 $stats{search_re} = $search_re;
210 $stats{scan_re} = $scan_re;
212 else {
213 if ( $opt_f ) {
214 # No need to check for regex, since mutex options are handled elsewhere.
216 else {
217 $opt_regex //= shift @ARGV;
218 ($search_re, $scan_re) = build_regex( $opt_regex, $opt );
219 $stats{search_re} = $search_re;
220 $stats{scan_re} = $scan_re;
222 # XXX What is this checking for?
223 if ( $search_re && $search_re =~ /\n/ ) {
224 App::Ack::exit_from_ack( 0 );
226 my @start;
227 if ( not defined $opt->{files_from} ) {
228 @start = @ARGV;
230 if ( !exists($opt->{show_filename}) ) {
231 unless(@start == 1 && !(-d $start[0])) {
232 $opt_show_filename = $opt->{show_filename} = 1;
236 if ( defined $opt->{files_from} ) {
237 $files = App::Ack::Files->from_file( $opt, $opt->{files_from} );
238 exit 1 unless $files;
240 else {
241 @start = ('.') unless @start;
242 foreach my $target (@start) {
243 if ( !-e $target && $App::Ack::report_bad_filenames) {
244 App::Ack::warn( "$target: No such file or directory" );
248 $opt->{file_filter} = _compile_file_filter($opt, \@start);
249 $opt->{descend_filter} = _compile_descend_filter($opt);
251 $files = App::Ack::Files->from_argv( $opt, \@start );
254 App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
256 my $nmatches;
257 if ( $opt_f || $opt_g ) {
258 $nmatches = file_loop_fg( $files );
260 elsif ( $opt_c ) {
261 $nmatches = file_loop_c( $files );
263 elsif ( $opt_l || $opt_L ) {
264 $nmatches = file_loop_lL( $files );
266 else {
267 $nmatches = file_loop_normal( $files );
270 if ( $opt_debug ) {
271 require List::Util;
272 my @stats = qw( search_re scan_re prescans linescans filematches linematches );
273 my $width = List::Util::max( map { length } @stats );
275 for my $stat ( @stats ) {
276 App::Ack::warn( sprintf( '%-*.*s = %s', $width, $width, $stat, $stats{$stat} // 'undef' ) );
280 close $App::Ack::fh;
282 App::Ack::exit_from_ack( $nmatches );
285 # End of MAIN
287 sub file_loop_fg {
288 my $files = shift;
290 my $nmatches = 0;
291 while ( defined( my $file = $files->next ) ) {
292 if ( $opt_show_types ) {
293 App::Ack::show_types( $file );
295 elsif ( $opt_g ) {
296 print_line_with_options( undef, $file->name, 0, $App::Ack::ors );
298 else {
299 App::Ack::say( $file->name );
301 ++$nmatches;
302 last if defined($opt_m) && ($nmatches >= $opt_m);
305 return $nmatches;
309 sub file_loop_c {
310 my $files = shift;
312 my $total_count = 0;
313 while ( defined( my $file = $files->next ) ) {
314 my $matches_for_this_file = count_matches_in_file( $file );
316 if ( not $opt_show_filename ) {
317 $total_count += $matches_for_this_file;
318 next;
321 if ( !$opt_l || $matches_for_this_file > 0 ) {
322 if ( $opt_show_filename ) {
323 App::Ack::say( $file->name, ':', $matches_for_this_file );
325 else {
326 App::Ack::say( $matches_for_this_file );
331 if ( !$opt_show_filename ) {
332 App::Ack::say( $total_count );
335 return;
339 sub file_loop_lL {
340 my $files = shift;
342 my $nmatches = 0;
343 while ( defined( my $file = $files->next ) ) {
344 my $is_match = count_matches_in_file( $file, 1 );
346 if ( $opt_L ? !$is_match : $is_match ) {
347 App::Ack::say( $file->name );
348 ++$nmatches;
350 last if $opt_1;
351 last if defined($opt_m) && ($nmatches >= $opt_m);
355 return $nmatches;
359 sub _compile_descend_filter {
360 my ( $opt ) = @_;
362 my $idirs = 0;
363 my $dont_ignore_dirs = 0;
365 for my $filter (@{$opt->{idirs} || []}) {
366 if ($filter->is_inverted()) {
367 $dont_ignore_dirs++;
369 else {
370 $idirs++;
374 # If we have one or more --noignore-dir directives, we can't ignore
375 # entire subdirectory hierarchies, so we return an "accept all"
376 # filter and scrutinize the files more in _compile_file_filter.
377 return if $dont_ignore_dirs;
378 return unless $idirs;
380 $idirs = $opt->{idirs};
382 return sub {
383 my $file = App::Ack::File->new($File::Next::dir);
384 return !grep { $_->filter($file) } @{$idirs};
388 sub _compile_file_filter {
389 my ( $opt, $start ) = @_;
391 my $ifiles_filters = $opt->{ifiles};
393 my $filters = $opt->{'filters'} || [];
394 my $direct_filters = App::Ack::Filter::Collection->new();
395 my $inverse_filters = App::Ack::Filter::Collection->new();
397 foreach my $filter (@{$filters}) {
398 if ($filter->is_inverted()) {
399 # We want to check if files match the uninverted filters
400 $inverse_filters->add($filter->invert());
402 else {
403 $direct_filters->add($filter);
407 my %is_member_of_starting_set = map { (get_file_id($_) => 1) } @{$start};
409 my @ignore_dir_filter = @{$opt->{idirs} || []};
410 my @is_inverted = map { $_->is_inverted() } @ignore_dir_filter;
411 # This depends on InverseFilter->invert returning the original filter (for optimization).
412 @ignore_dir_filter = map { $_->is_inverted() ? $_->invert() : $_ } @ignore_dir_filter;
413 my $dont_ignore_dir_filter = grep { $_ } @is_inverted;
414 my $previous_dir = '';
415 my $previous_dir_ignore_result;
417 return sub {
418 if ( $opt_g ) {
419 if ( $File::Next::name =~ /$search_re/o ) {
420 return 0 if $opt_v;
422 else {
423 return 0 if !$opt_v;
426 # ack always selects files that are specified on the command
427 # line, regardless of filetype. If you want to ack a JPEG,
428 # and say "ack foo whatever.jpg" it will do it for you.
429 return 1 if $is_member_of_starting_set{ get_file_id($File::Next::name) };
431 if ( $dont_ignore_dir_filter ) {
432 if ( $previous_dir eq $File::Next::dir ) {
433 if ( $previous_dir_ignore_result ) {
434 return 0;
437 else {
438 my @dirs = File::Spec->splitdir($File::Next::dir);
440 my $is_ignoring = 0;
442 for ( my $i = 0; $i < @dirs; $i++) {
443 my $dir_rsrc = App::Ack::File->new(File::Spec->catfile(@dirs[0 .. $i]));
445 my $j = 0;
446 for my $filter (@ignore_dir_filter) {
447 if ( $filter->filter($dir_rsrc) ) {
448 $is_ignoring = !$is_inverted[$j];
450 $j++;
454 $previous_dir = $File::Next::dir;
455 $previous_dir_ignore_result = $is_ignoring;
457 if ( $is_ignoring ) {
458 return 0;
463 # Ignore named pipes found in directory searching. Named
464 # pipes created by subprocesses get specified on the command
465 # line, so the rule of "always select whatever is on the
466 # command line" wins.
467 return 0 if -p $File::Next::name;
469 # We can't handle unreadable filenames; report them.
470 if ( not -r _ ) {
471 use filetest 'access';
473 if ( not -R $File::Next::name ) {
474 if ( $App::Ack::report_bad_filenames ) {
475 App::Ack::warn( "${File::Next::name}: cannot open file for reading" );
477 return 0;
481 my $file = App::Ack::File->new($File::Next::name);
483 if ( $ifiles_filters && $ifiles_filters->filter($file) ) {
484 return 0;
487 my $match_found = $direct_filters->filter($file);
489 # Don't bother invoking inverse filters unless we consider the current file a match.
490 if ( $match_found && $inverse_filters->filter( $file ) ) {
491 $match_found = 0;
493 return $match_found;
498 # Returns a (fairly) unique identifier for a file.
499 # Use this function to compare two files to see if they're
500 # equal (ie. the same file, but with a different path/links/etc).
501 sub get_file_id {
502 my ( $filename ) = @_;
504 if ( $App::Ack::is_windows ) {
505 return File::Next::reslash( $filename );
507 else {
508 # XXX Is this the best method? It always hits the FS.
509 if ( my ( $dev, $inode ) = (stat($filename))[0, 1] ) {
510 return join(':', $dev, $inode);
512 else {
513 # XXX This could be better.
514 return $filename;
519 # Returns a regex object based on a string and command-line options.
520 # Dies when the regex $str is undefined (i.e. not given on command line).
522 sub build_regex {
523 my $str = shift;
524 my $opt = shift;
526 defined $str or App::Ack::die( 'No regular expression found.' );
528 # Check for lowercaseness before we do any modifications.
529 my $regex_is_lc = App::Ack::is_lowercase( $str );
531 $str = quotemeta( $str ) if $opt->{Q};
533 my $scan_str = $str;
535 # Whole words only.
536 if ( $opt->{w} ) {
537 my $ok = 1;
539 if ( $str =~ /^\\[wd]/ ) {
540 # Explicit \w is good.
542 else {
543 # Can start with \w, (, [ or dot.
544 if ( $str !~ /^[\w\(\[\.]/ ) {
545 $ok = 0;
549 # Can end with \w, }, ), ], +, *, or dot.
550 if ( $str !~ /[\w\}\)\]\+\*\?\.]$/ ) {
551 $ok = 0;
553 # ... unless it's escaped.
554 elsif ( $str =~ /\\[\}\)\]\+\*\?\.]$/ ) {
555 $ok = 0;
558 if ( !$ok ) {
559 App::Ack::die( '-w will not do the right thing if your regex does not begin and end with a word character.' );
562 if ( $str =~ /^\w+$/ ) {
563 # No need for fancy regex if it's a simple word.
564 $str = sprintf( '\b(?:%s)\b', $str );
566 else {
567 $str = sprintf( '(?:^|\b|\s)\K(?:%s)(?=\s|\b|$)', $str );
571 if ( $opt->{i} || ($opt->{S} && $regex_is_lc) ) {
572 $_ = "(?i)$_" for ( $str, $scan_str );
575 my $scan_regex = undef;
576 my $regex = eval { qr/$str/ };
577 if ( $regex ) {
578 if ( $scan_str !~ /\$/ ) {
579 # No line_scan is possible if there's a $ in the regex.
580 $scan_regex = eval { qr/$scan_str/m };
583 else {
584 my $err = $@;
585 chomp $err;
586 App::Ack::die( "Invalid regex '$str':\n $err" );
589 return ($regex, $scan_regex);
592 my $match_colno;
596 # Number of context lines
597 my $n_before_ctx_lines;
598 my $n_after_ctx_lines;
600 # Array to keep track of lines that might be required for a "before" context
601 my @before_context_buf;
602 # Position to insert next line in @before_context_buf
603 my $before_context_pos;
605 # Number of "after" context lines still pending
606 my $after_context_pending;
608 # Number of latest line that got printed
609 my $printed_lineno;
611 my $is_first_match;
612 state $has_printed_something = 0;
615 sub file_loop_normal {
616 my $files = shift;
618 $n_before_ctx_lines = $opt_output ? 0 : ($opt_B || 0);
619 $n_after_ctx_lines = $opt_output ? 0 : ($opt_A || 0);
621 @before_context_buf = (undef) x $n_before_ctx_lines;
622 $before_context_pos = 0;
624 $is_tracking_context = $n_before_ctx_lines || $n_after_ctx_lines;
626 $is_first_match = 1;
628 my $nmatches = 0;
629 while ( defined( my $file = $files->next ) ) {
630 if ($is_tracking_context) {
631 $printed_lineno = 0;
632 $after_context_pending = 0;
633 if ( $opt_heading ) {
634 $is_first_match = 1;
637 my $needs_line_scan = 1;
638 if ( !$opt_passthru && !$opt_v ) {
639 $stats{prescans}++;
640 if ( $file->may_be_present( $scan_re ) ) {
641 $file->reset();
643 else {
644 $needs_line_scan = 0;
647 if ( $needs_line_scan ) {
648 $stats{linescans}++;
649 $nmatches += print_matches_in_file( $file );
651 last if $opt_1 && $nmatches;
654 return $nmatches;
658 sub print_matches_in_file {
659 my $file = shift;
661 my $max_count = $opt_m || -1; # Go negative for no limit so it can never reduce to 0.
662 my $nmatches = 0;
663 my $filename = $file->name;
665 my $has_printed_for_this_file = 0;
667 my $fh = $file->open;
668 if ( !$fh ) {
669 if ( $App::Ack::report_bad_filenames ) {
670 App::Ack::warn( "$filename: $!" );
672 return 0;
675 my $display_filename = $filename;
676 if ( $opt_show_filename && $opt_heading && $opt_color ) {
677 $display_filename = Term::ANSIColor::colored($display_filename, $ENV{ACK_COLOR_FILENAME});
680 # Check for context before the main loop, so we don't pay for it if we don't need it.
681 if ( $is_tracking_context ) {
682 local $_ = undef;
684 $after_context_pending = 0;
686 my $in_range = range_setup();
688 while ( <$fh> ) {
689 chomp;
690 $match_colno = undef;
692 $in_range = 1 if ( $using_ranges && !$in_range && $opt_range_start && /$opt_range_start/o );
694 my $does_match;
695 if ( $in_range ) {
696 if ( $opt_v ) {
697 $does_match = !/$search_re/o;
699 else {
700 if ( $does_match = /$search_re/o ) {
701 # @- = @LAST_MATCH_START
702 # @+ = @LAST_MATCH_END
703 $match_colno = $-[0] + 1;
708 if ( $does_match && $max_count ) {
709 if ( !$has_printed_for_this_file ) {
710 $stats{filematches}++;
711 if ( $opt_break && $has_printed_something ) {
712 App::Ack::print_blank_line();
714 if ( $opt_show_filename && $opt_heading ) {
715 App::Ack::say( $display_filename );
718 print_line_with_context( $filename, $_, $. );
719 $has_printed_for_this_file = 1;
720 $stats{linematches}++;
721 $nmatches++;
722 $max_count--;
724 else {
725 if ( $after_context_pending ) {
726 # Disable $opt_column since there are no matches in the context lines.
727 local $opt_column = 0;
728 print_line_with_options( $filename, $_, $., '-' );
729 --$after_context_pending;
731 elsif ( $n_before_ctx_lines ) {
732 # Save line for "before" context.
733 $before_context_buf[$before_context_pos] = $_;
734 $before_context_pos = ($before_context_pos+1) % $n_before_ctx_lines;
738 $in_range = 0 if ( $using_ranges && $in_range && $opt_range_end && /$opt_range_end/o );
740 last if ($max_count == 0) && ($after_context_pending == 0);
743 elsif ( $opt_passthru ) {
744 local $_ = undef;
746 my $in_range = range_setup();
748 while ( <$fh> ) {
749 chomp;
751 $in_range = 1 if ( $using_ranges && !$in_range && $opt_range_start && /$opt_range_start/o );
753 $match_colno = undef;
754 if ( $in_range && ($opt_v xor /$search_re/o) ) {
755 if ( !$opt_v ) {
756 $match_colno = $-[0] + 1;
758 if ( !$has_printed_for_this_file ) {
759 if ( $opt_break && $has_printed_something ) {
760 App::Ack::print_blank_line();
762 if ( $opt_show_filename && $opt_heading ) {
763 App::Ack::say( $display_filename );
766 print_line_with_context( $filename, $_, $. );
767 $has_printed_for_this_file = 1;
768 $nmatches++;
769 $max_count--;
771 else {
772 if ( $opt_break && !$has_printed_for_this_file && $has_printed_something ) {
773 App::Ack::print_blank_line();
775 print_line_with_options( $filename, $_, $., ':', 1 );
776 $has_printed_for_this_file = 1;
779 $in_range = 0 if ( $using_ranges && $in_range && $opt_range_end && /$opt_range_end/o );
781 last if $max_count == 0;
784 elsif ( $opt_v ) {
785 local $_ = undef;
787 $match_colno = undef;
788 my $in_range = range_setup();
790 while ( <$fh> ) {
791 chomp;
793 $in_range = 1 if ( $using_ranges && !$in_range && $opt_range_start && /$opt_range_start/o );
795 if ( $in_range ) {
796 if ( !/$search_re/o ) {
797 if ( !$has_printed_for_this_file ) {
798 if ( $opt_break && $has_printed_something ) {
799 App::Ack::print_blank_line();
801 if ( $opt_show_filename && $opt_heading ) {
802 App::Ack::say( $display_filename );
805 print_line_with_context( $filename, $_, $. );
806 $has_printed_for_this_file = 1;
807 $nmatches++;
808 $max_count--;
812 $in_range = 0 if ( $using_ranges && $in_range && $opt_range_end && /$opt_range_end/o );
814 last if $max_count == 0;
817 else { # Normal search: No context, no -v, no --passthru
818 local $_ = undef;
820 my $last_match_lineno;
821 my $in_range = range_setup();
823 while ( <$fh> ) {
824 chomp;
826 $in_range = 1 if ( $using_ranges && !$in_range && $opt_range_start && /$opt_range_start/o );
828 if ( $in_range ) {
829 $match_colno = undef;
830 if ( /$search_re/o ) {
831 $match_colno = $-[0] + 1;
832 if ( !$has_printed_for_this_file ) {
833 $stats{filematches}++;
834 if ( $opt_break && $has_printed_something ) {
835 App::Ack::print_blank_line();
837 if ( $opt_show_filename && $opt_heading ) {
838 App::Ack::say( $display_filename );
841 if ( $opt_p ) {
842 if ( $last_match_lineno ) {
843 if ( $. > $last_match_lineno + $opt_p ) {
844 App::Ack::print_blank_line();
847 elsif ( !$opt_break && $has_printed_something ) {
848 App::Ack::print_blank_line();
851 s/[\r\n]+$//;
852 print_line_with_options( $filename, $_, $., ':' );
853 $has_printed_for_this_file = 1;
854 $nmatches++;
855 $stats{linematches}++;
856 $max_count--;
857 $last_match_lineno = $.;
861 $in_range = 0 if ( $using_ranges && $in_range && $opt_range_end && /$opt_range_end/o );
863 last if $max_count == 0;
867 return $nmatches;
871 sub print_line_with_options {
872 my ( $filename, $line, $lineno, $separator, $skip_coloring ) = @_;
874 $has_printed_something = 1;
875 $printed_lineno = $lineno;
877 my @line_parts;
879 if ( $opt_show_filename && defined($filename) ) {
880 my $colno;
881 $colno = get_match_colno() if $opt_column;
882 if ( $opt_color ) {
883 $filename = Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} );
884 $lineno = Term::ANSIColor::colored( $lineno, $ENV{ACK_COLOR_LINENO} );
885 $colno = Term::ANSIColor::colored( $colno, $ENV{ACK_COLOR_COLNO} ) if $opt_column;
887 if ( $opt_heading ) {
888 push @line_parts, $lineno;
890 else {
891 push @line_parts, $filename, $lineno;
893 push @line_parts, $colno if $opt_column;
896 if ( $opt_output ) {
897 while ( $line =~ /$search_re/og ) {
898 my $output = $opt_output;
899 if ( @special_vars_used_by_opt_output ) {
900 no strict;
902 # Stash copies of the special variables because we can't rely
903 # on them not changing in the process of doing the s///.
905 my %keep = map { ($_ => ${$_} // '') } @special_vars_used_by_opt_output;
906 $keep{_} = $line if exists $keep{_}; # Manually set it because $_ gets reset in a map.
907 $keep{f} = $filename if exists $keep{f};
908 my $special_vars_used_by_opt_output = join( '', @special_vars_used_by_opt_output );
909 $output =~ s/\$([$special_vars_used_by_opt_output])/$keep{$1}/ego;
911 App::Ack::say( join( $separator, @line_parts, $output ) );
914 else {
915 my $underline = '';
917 # We have to do underlining before any highlighting because highlighting modifies string length.
918 if ( $opt_underline && !$skip_coloring ) {
919 while ( $line =~ /$search_re/og ) {
920 my $match_start = $-[0] // next;
921 my $match_end = $+[0];
922 my $match_length = $match_end - $match_start;
923 last if $match_length <= 0;
925 my $spaces_needed = $match_start - length $underline;
927 $underline .= (' ' x $spaces_needed);
928 $underline .= ('^' x $match_length);
931 if ( $opt_color && !$skip_coloring ) {
932 my $highlighted = 0; # If highlighted, need to escape afterwards.
934 while ( $line =~ /$search_re/og ) {
935 my $match_start = $-[0] // next;
936 my $match_end = $+[0];
937 my $match_length = $match_end - $match_start;
938 last if $match_length <= 0;
940 my $substring = substr( $line, $match_start, $match_length );
941 my $substitution = Term::ANSIColor::colored( $substring, $ENV{ACK_COLOR_MATCH} );
943 # Fourth argument replaces the string specified by the first three.
944 substr( $line, $match_start, $match_length, $substitution );
946 # Move the offset of where /g left off forward the number of spaces of highlighting.
947 pos($line) = $match_end + (length( $substitution ) - length( $substring ));
948 $highlighted = 1;
950 # Reset formatting and delete everything to the end of the line.
951 $line .= "\e[0m\e[K" if $highlighted;
954 push @line_parts, $line;
955 App::Ack::say( join( $separator, @line_parts ) );
957 # Print the underline, if appropriate.
958 if ( $underline ne '' ) {
959 # Figure out how many spaces are used per line for the ANSI coloring.
960 state $chars_used_by_coloring;
961 if ( !defined($chars_used_by_coloring) ) {
962 $chars_used_by_coloring = 0;
963 if ( $opt_color ) {
964 my $len_fn = sub { length( Term::ANSIColor::colored( 'x', $ENV{$_[0]} ) ) - 1 };
965 $chars_used_by_coloring += $len_fn->('ACK_COLOR_FILENAME') unless $opt_heading;
966 $chars_used_by_coloring += $len_fn->('ACK_COLOR_LINENO');
967 $chars_used_by_coloring += $len_fn->('ACK_COLOR_COLNO') if $opt_column;
971 pop @line_parts; # Leave only the stuff on the left.
972 if ( @line_parts ) {
973 my $stuff_on_the_left = join( $separator, @line_parts );
974 my $spaces_needed = length($stuff_on_the_left) - $chars_used_by_coloring + 1;
976 App::Ack::print( ' ' x $spaces_needed );
978 App::Ack::say( $underline );
982 return;
985 sub print_line_with_context {
986 my ( $filename, $matching_line, $lineno ) = @_;
988 $matching_line =~ s/[\r\n]+$//;
990 # Check if we need to print context lines first.
991 if ( $opt_A || $opt_B ) {
992 my $before_unprinted = $lineno - $printed_lineno - 1;
993 if ( !$is_first_match && ( !$printed_lineno || $before_unprinted > $n_before_ctx_lines ) ) {
994 App::Ack::say( '--' );
997 # We want at most $n_before_ctx_lines of context.
998 if ( $before_unprinted > $n_before_ctx_lines ) {
999 $before_unprinted = $n_before_ctx_lines;
1002 while ( $before_unprinted > 0 ) {
1003 my $line = $before_context_buf[($before_context_pos - $before_unprinted + $n_before_ctx_lines) % $n_before_ctx_lines];
1005 chomp $line;
1007 # Disable $opt->{column} since there are no matches in the context lines.
1008 local $opt_column = 0;
1010 print_line_with_options( $filename, $line, $lineno-$before_unprinted, '-' );
1011 $before_unprinted--;
1015 print_line_with_options( $filename, $matching_line, $lineno, ':' );
1017 # We want to get the next $n_after_ctx_lines printed.
1018 $after_context_pending = $n_after_ctx_lines;
1020 $is_first_match = 0;
1022 return;
1027 sub get_match_colno {
1028 return $match_colno;
1031 sub count_matches_in_file {
1032 my $file = shift;
1033 my $bail = shift; # True if we're just checking for existence.
1035 my $nmatches = 0;
1036 my $do_scan = 1;
1038 if ( !$file->open() ) {
1039 $do_scan = 0;
1040 if ( $App::Ack::report_bad_filenames ) {
1041 App::Ack::warn( $file->name . ": $!" );
1044 else {
1045 if ( !$opt_v ) {
1046 if ( !$file->may_be_present( $scan_re ) ) {
1047 $do_scan = 0;
1052 if ( $do_scan ) {
1053 $file->reset();
1055 my $in_range = range_setup();
1057 my $fh = $file->{fh};
1058 if ( $using_ranges ) {
1059 while ( <$fh> ) {
1060 chomp;
1061 $in_range = 1 if ( !$in_range && $opt_range_start && /$opt_range_start/o );
1062 if ( $in_range ) {
1063 if ( /$search_re/o xor $opt_v ) {
1064 ++$nmatches;
1065 last if $bail;
1068 $in_range = 0 if ( $in_range && $opt_range_end && /$opt_range_end/o );
1071 else {
1072 while ( <$fh> ) {
1073 chomp;
1074 if ( /$search_re/o xor $opt_v ) {
1075 ++$nmatches;
1076 last if $bail;
1081 $file->close;
1083 return $nmatches;
1087 sub range_setup {
1088 return !$using_ranges || (!$opt_range_start && $opt_range_end);
1092 =pod
1094 =encoding UTF-8
1096 =head1 NAME
1098 ack - grep-like text finder
1100 =head1 SYNOPSIS
1102 ack [options] PATTERN [FILE...]
1103 ack -f [options] [DIRECTORY...]
1105 =head1 DESCRIPTION
1107 ack is designed as an alternative to F<grep> for programmers.
1109 ack searches the named input FILEs or DIRECTORYs for lines containing a
1110 match to the given PATTERN. By default, ack prints the matching lines.
1111 If no FILE or DIRECTORY is given, the current directory will be searched.
1113 PATTERN is a Perl regular expression. Perl regular expressions
1114 are commonly found in other programming languages, but for the particulars
1115 of their behavior, please consult
1116 L<perlreref|https://perldoc.perl.org/perlreref.html>. If you don't know
1117 how to use regular expression but are interested in learning, you may
1118 consult L<perlretut|https://perldoc.perl.org/perlretut.html>. If you do not
1119 need or want ack to use regular expressions, please see the
1120 C<-Q>/C<--literal> option.
1122 Ack can also list files that would be searched, without actually
1123 searching them, to let you take advantage of ack's file-type filtering
1124 capabilities.
1126 =head1 FILE SELECTION
1128 If files are not specified for searching, either on the command
1129 line or piped in with the C<-x> option, I<ack> delves into
1130 subdirectories selecting files for searching.
1132 I<ack> is intelligent about the files it searches. It knows about
1133 certain file types, based on both the extension on the file and,
1134 in some cases, the contents of the file. These selections can be
1135 made with the B<--type> option.
1137 With no file selection, I<ack> searches through regular files that
1138 are not explicitly excluded by B<--ignore-dir> and B<--ignore-file>
1139 options, either present in F<ackrc> files or on the command line.
1141 The default options for I<ack> ignore certain files and directories. These
1142 include:
1144 =over 4
1146 =item * Backup files: Files matching F<#*#> or ending with F<~>.
1148 =item * Coredumps: Files matching F<core.\d+>
1150 =item * Version control directories like F<.svn> and F<.git>.
1152 =back
1154 Run I<ack> with the C<--dump> option to see what settings are set.
1156 However, I<ack> always searches the files given on the command line,
1157 no matter what type. If you tell I<ack> to search in a coredump,
1158 it will search in a coredump.
1160 =head1 DIRECTORY SELECTION
1162 I<ack> descends through the directory tree of the starting directories
1163 specified. If no directories are specified, the current working directory is
1164 used. However, it will ignore the shadow directories used by
1165 many version control systems, and the build directories used by the
1166 Perl MakeMaker system. You may add or remove a directory from this
1167 list with the B<--[no]ignore-dir> option. The option may be repeated
1168 to add/remove multiple directories from the ignore list.
1170 For a complete list of directories that do not get searched, run
1171 C<ack --dump>.
1173 =head1 MATCHING IN A RANGE OF LINES
1175 The C<--range-start> and C<--range-end> options let you specify ranges of
1176 lines to search within each file.
1178 Say you had the following file, called F<testfile>:
1180 # This function calls print on "foo".
1181 sub foo {
1182 print 'foo';
1184 my $print = 1;
1185 sub bar {
1186 print 'bar';
1188 my $task = 'print';
1190 Calling C<ack print> will give us five matches:
1192 $ ack print testfile
1193 # This function calls print on "foo".
1194 print 'foo';
1195 my $print = 1;
1196 print 'bar';
1197 my $task = 'print';
1199 What if we only want to search for C<print> within the subroutines? We can
1200 specify ranges of lines that we want ack to search. The range starts with
1201 any line that matches the pattern C<^sub \w+>, and stops with any line that
1202 matches C<^}>.
1204 $ ack --range-start='^sub \w+' --range-end='^}' print testfile
1205 print 'foo';
1206 print 'bar';
1208 Note that ack searched two ranges of lines. The listing below shows which
1209 lines were in a range and which were out of the range.
1211 Out # This function calls print on "foo".
1212 In sub foo {
1213 In print 'foo';
1214 In }
1215 Out my $print = 1;
1216 In sub bar {
1217 In print 'bar';
1218 In }
1219 Out my $task = 'print';
1221 You don't have to specify both C<--range-start> and C<--range-end>. IF
1222 C<--range-start> is omitted, then the range runs from the first line in the
1223 file unitl the first line that matches C<--range-end>. Similarly, if
1224 C<--range-end> is omitted, the range runs from the first line matching
1225 C<--range-start> to the end of the file.
1227 For example, if you wanted to search all HTML files up until the first
1228 instance of the C<< <body> >>, you could do
1230 ack foo --range-end='<body>'
1232 Or to search after Perl's `__DATA__` or `__END__` markers, you would do
1234 ack pattern --range-end='^__(END|DATA)__'
1236 It's possible for a range to start and stop on the same line. For example
1238 --range-start='<title>' --range-end='</title>'
1240 would match this line as both the start and end of the range, making a
1241 one-line range.
1243 <title>Page title</title>
1245 Note that the patterns in C<--range-start> and C<--range-end> are not
1246 affected by options like C<-i>, C<-w> and C<-Q> that modify the behavior of
1247 the main pattern being matched.
1249 Again, ranges only affect where matches are looked for. Everything else in
1250 ack works the same way. Using C<-c> option with a range will give a count
1251 of all the matches that appear within those ranges. The C<-l> shows those
1252 files that have a match within a range, and the C<-L> option shows files
1253 that do not have a match within a range.
1255 The C<-v> option for negating a match works inside the range, too.
1256 To see lines that don't match "google" within the "<head>" section of
1257 your HTML files, you could do:
1259 ack google -v --html --range-start='<head' --range-end='</head>'
1261 Specifying a range to search does not affect how matches are displayed.
1262 The context for a match will still be the same, and
1264 Using the context options work the same way, and will show context
1265 lines for matches even if the context lines fall outside the range.
1266 Similarly, C<--passthru> will show all lines in the file, but only show
1267 matches for lines within the range.
1269 =head1 OPTIONS
1271 =over 4
1273 =item B<--ackrc>
1275 Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.
1277 =item B<-A I<NUM>>, B<--after-context=I<NUM>>
1279 Print I<NUM> lines of trailing context after matching lines.
1281 =item B<-B I<NUM>>, B<--before-context=I<NUM>>
1283 Print I<NUM> lines of leading context before matching lines.
1285 =item B<--[no]break>
1287 Print a break between results from different files. On by default
1288 when used interactively.
1290 =item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
1292 Print I<NUM> lines (default 2) of context around matching lines.
1293 You can specify zero lines of context to override another context
1294 specified in an ackrc.
1296 =item B<-c>, B<--count>
1298 Suppress normal output; instead print a count of matching lines for
1299 each input file. If B<-l> is in effect, it will only show the
1300 number of lines for each file that has lines matching. Without
1301 B<-l>, some line counts may be zeroes.
1303 If combined with B<-h> (B<--no-filename>) ack outputs only one total
1304 count.
1306 =item B<--[no]color>, B<--[no]colour>
1308 B<--color> highlights the matching text. B<--nocolor> suppresses
1309 the color. This is on by default unless the output is redirected.
1311 On Windows, this option is off by default unless the
1312 L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>
1313 environment variable is used.
1315 =item B<--color-filename=I<color>>
1317 Sets the color to be used for filenames.
1319 =item B<--color-match=I<color>>
1321 Sets the color to be used for matches.
1323 =item B<--color-colno=I<color>>
1325 Sets the color to be used for column numbers.
1327 =item B<--color-lineno=I<color>>
1329 Sets the color to be used for line numbers.
1331 =item B<--[no]column>
1333 Show the column number of the first match. This is helpful for
1334 editors that can place your cursor at a given position.
1336 =item B<--create-ackrc>
1338 Dumps the default ack options to standard output. This is useful for
1339 when you want to customize the defaults.
1341 =item B<--dump>
1343 Writes the list of options loaded and where they came from to standard
1344 output. Handy for debugging.
1346 =item B<--[no]env>
1348 B<--noenv> disables all environment processing. No F<.ackrc> is
1349 read and all environment variables are ignored. By default, F<ack>
1350 considers F<.ackrc> and settings in the environment.
1352 =item B<--flush>
1354 B<--flush> flushes output immediately. This is off by default
1355 unless ack is running interactively (when output goes to a pipe or
1356 file).
1358 =item B<-f>
1360 Only print the files that would be searched, without actually doing
1361 any searching. PATTERN must not be specified, or it will be taken
1362 as a path to search.
1364 =item B<--files-from=I<FILE>>
1366 The list of files to be searched is specified in I<FILE>. The list of
1367 files are separated by newlines. If I<FILE> is C<->, the list is loaded
1368 from standard input.
1370 Note that the list of files is B<not> filtered in any way. If you
1371 add C<--type=html> in addition to C<--files-from>, the C<--type> will
1372 be ignored.
1375 =item B<--[no]filter>
1377 Forces ack to act as if it were receiving input via a pipe.
1379 =item B<--[no]follow>
1381 Follow or don't follow symlinks, other than whatever starting files
1382 or directories were specified on the command line.
1384 This is off by default.
1386 =item B<-g I<PATTERN>>
1388 Print searchable files where the relative path + filename matches
1389 I<PATTERN>.
1391 Note that
1393 ack -g foo
1395 is exactly the same as
1397 ack -f | ack foo
1399 This means that just as ack will not search, for example, F<.jpg>
1400 files, C<-g> will not list F<.jpg> files either. ack is not intended
1401 to be a general-purpose file finder.
1403 Note also that if you have C<-i> in your .ackrc that the filenames
1404 to be matched will be case-insensitive as well.
1406 This option can be combined with B<--color> to make it easier to
1407 spot the match.
1409 =item B<--[no]group>
1411 B<--group> groups matches by file name. This is the default
1412 when used interactively.
1414 B<--nogroup> prints one result per line, like grep. This is the
1415 default when output is redirected.
1417 =item B<-H>, B<--with-filename>
1419 Print the filename for each match. This is the default unless searching
1420 a single explicitly specified file.
1422 =item B<-h>, B<--no-filename>
1424 Suppress the prefixing of filenames on output when multiple files are
1425 searched.
1427 =item B<--[no]heading>
1429 Print a filename heading above each file's results. This is the default
1430 when used interactively.
1432 =item B<--help>
1434 Print a short help statement.
1436 =item B<--help-types>
1438 Print all known types.
1440 =item B<--help-colors>
1442 Print a chart of various color combinations.
1444 =item B<--help-rgb-colors>
1446 Like B<--help-colors> but with more precise RGB colors.
1448 =item B<-i>, B<--ignore-case>
1450 Ignore case distinctions in PATTERN. Overrides B<--smart-case> and B<-I>.
1452 =item B<-I>, B<--no-ignore-case>
1454 Turns on case distinctions in PATTERN. Overrides B<--smart-case> and B<-i>.
1456 =item B<--ignore-ack-defaults>
1458 Tells ack to completely ignore the default definitions provided with ack.
1459 This is useful in combination with B<--create-ackrc> if you I<really> want
1460 to customize ack.
1462 =item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>
1464 Ignore directory (as CVS, .svn, etc are ignored). May be used
1465 multiple times to ignore multiple directories. For example, mason
1466 users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>
1467 option allows users to search directories which would normally be
1468 ignored (perhaps to research the contents of F<.svn/props> directories).
1470 The I<DIRNAME> must always be a simple directory name. Nested
1471 directories like F<foo/bar> are NOT supported. You would need to
1472 specify B<--ignore-dir=foo> and then no files from any foo directory
1473 are taken into account by ack unless given explicitly on the command
1474 line.
1476 =item B<--ignore-file=I<FILTER:ARGS>>
1478 Ignore files matching I<FILTER:ARGS>. The filters are specified
1479 identically to file type filters as seen in L</"Defining your own types">.
1481 =item B<-k>, B<--known-types>
1483 Limit selected files to those with types that ack knows about.
1485 =item B<-l>, B<--files-with-matches>
1487 Only print the filenames of matching files, instead of the matching text.
1489 =item B<-L>, B<--files-without-matches>
1491 Only print the filenames of files that do I<NOT> match.
1493 =item B<--match I<PATTERN>>
1495 Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the
1496 regex as your first argument, e.g. when executing multiple searches over the
1497 same set of files.
1499 # search for foo and bar in given files
1500 ack file1 t/file* --match foo
1501 ack file1 t/file* --match bar
1503 =item B<-m=I<NUM>>, B<--max-count=I<NUM>>
1505 Print only I<NUM> matches out of each file. If you want to stop ack
1506 after printing the first match of any kind, use the B<-1> options.
1508 =item B<--man>
1510 Print this manual page.
1512 =item B<-n>, B<--no-recurse>
1514 No descending into subdirectories.
1516 =item B<-o>
1518 Show only the part of each line matching PATTERN (turns off text
1519 highlighting). This is exactly the same as C<--output=$&>.
1521 =item B<--output=I<expr>>
1523 Output the evaluation of I<expr> for each line (turns off text
1524 highlighting). If PATTERN matches more than once then a line is
1525 output for each non-overlapping match.
1527 I<expr> may contain the strings "\n", "\r" and "\t", which will be
1528 expanded to their corresponding characters line feed, carriage return
1529 and tab, respectively.
1531 I<expr> may also contain the following Perl special variables:
1533 =over 4
1535 =item C<$1> through C<$9>
1537 The subpattern from the corresponding set of capturing parentheses.
1538 If your pattern is C<(.+) and (.+)>, and the string is "this and
1539 that', then C<$1> is "this" and C<$2> is "that".
1541 =item C<$_>
1543 The contents of the line in the file.
1545 =item C<$.>
1547 The number of the line in the file.
1549 =item C<$&>, C<$`> and C<$'>
1551 C<$&> is the the string matched by the pattern, C<$`> is what
1552 precedes the match, and C<$'> is what follows it. If the pattern
1553 is C<gra(ph|nd)> and the string is "lexicographic", then C<$&> is
1554 "graph", C<$`> is "lexico" and C<$'> is "ic".
1556 Use of these variables in your output will slow down the pattern
1557 matching.
1559 =item C<$+>
1561 The match made by the last parentheses that matched in the pattern.
1562 For example, if your pattern is C<Version: (.+)|Revision: (.+)>,
1563 then C<$+> will contain whichever set of parentheses matched.
1565 =item C<$f>
1567 C<$f> is available, in C<--output> only, to insert the filename.
1568 This is a stand-in for the discovered C<$filename> usage in old C<< ack2 --output >>,
1569 which is disallowed with C<ack3> improved security.
1571 The intended usage is to provide the grep or compile-error syntax needed for editor/IDE go-to-line integration,
1572 e.g. C<--output=$f:$.:$_> or C<--output=$f\t$.\t$&>
1574 =back
1576 =item B<--pager=I<program>>, B<--nopager>
1578 B<--pager> directs ack's output through I<program>. This can also be specified
1579 via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.
1581 Using --pager does not suppress grouping and coloring like piping
1582 output on the command-line does.
1584 B<--nopager> cancels any setting in F<~/.ackrc>, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.
1585 No output will be sent through a pager.
1587 =item B<--passthru>
1589 Prints all lines, whether or not they match the expression. Highlighting
1590 will still work, though, so it can be used to highlight matches while
1591 still seeing the entire file, as in:
1593 # Watch a log file, and highlight a certain IP address.
1594 $ tail -f ~/access.log | ack --passthru 123.45.67.89
1596 =item B<--print0>
1598 Only works in conjunction with B<-f>, B<-g>, B<-l> or B<-c>, options
1599 that only list filenames. The filenames are output separated with a
1600 null byte instead of the usual newline. This is helpful when dealing
1601 with filenames that contain whitespace, e.g.
1603 # Remove all files of type HTML.
1604 ack -f --html --print0 | xargs -0 rm -f
1606 =item B<-p[N]>, B<--proximate[=N]>
1608 Groups together match lines that are within N lines of each other.
1609 This is useful for visually picking out matches that appear close
1610 to other matches.
1612 For example, if you got these results without the C<--proximate> option,
1614 15: First match
1615 18: Second match
1616 19: Third match
1617 37: Fourth match
1619 they would look like this with C<--proximate=1>
1621 15: First match
1623 18: Second match
1624 19: Third match
1626 37: Fourth match
1628 and this with C<--proximate=3>.
1630 15: First match
1631 18: Second match
1632 19: Third match
1634 37: Fourth match
1636 If N is omitted, N is set to 1.
1638 =item B<-P>
1640 Negates the effect of the B<--proximate> option. Shortcut for B<--proximate=0>.
1642 =item B<-Q>, B<--literal>
1644 Quote all metacharacters in PATTERN, it is treated as a literal.
1646 =item B<-r>, B<-R>, B<--recurse>
1648 Recurse into sub-directories. This is the default and just here for
1649 compatibility with grep. You can also use it for turning B<--no-recurse> off.
1651 =item B<--range-start=PATTERN>, B<--range-end=PATTERN>
1653 Specifies patterns that mark the start and end of a range. See
1654 L<MATCHING IN A RANGE OF LINES> for details.
1656 =item B<-s>
1658 Suppress error messages about nonexistent or unreadable files. This is taken
1659 from fgrep.
1661 =item B<-S>, B<--[no]smart-case>, B<--no-smart-case>
1663 Ignore case in the search strings if PATTERN contains no uppercase
1664 characters. This is similar to C<smartcase> in the vim text editor.
1665 The options overrides B<-i> and B<-I>.
1667 B<-S> is a synonym for B<--smart-case>.
1669 B<-i> always overrides this option.
1671 =item B<--sort-files>
1673 Sorts the found files lexicographically. Use this if you want your file
1674 listings to be deterministic between runs of I<ack>.
1676 =item B<--show-types>
1678 Outputs the filetypes that ack associates with each file.
1680 Works with B<-f> and B<-g> options.
1682 =item B<-t TYPE>, B<--type=TYPE>, B<--TYPE>
1684 Specify the types of files to include in the search.
1685 TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can
1686 also be specified as B<--perl>, although this is deprecated.
1688 Type inclusions can be repeated and are ORed together.
1690 See I<ack --help=types> for a list of valid types.
1692 =item B<-T TYPE>, B<--type=noTYPE>, B<--noTYPE>
1694 Specifies the type of files to exclude from the search. B<--type=noperl>
1695 can be done as B<--noperl>, although this is deprecated.
1697 If a file is of both type "foo" and "bar", specifying both B<--type=foo>
1698 and B<--type=nobar> will exclude the file, because an exclusion takes
1699 precedence over an inclusion.
1701 =item B<--type-add I<TYPE>:I<FILTER>:I<ARGS>>
1703 Files with the given ARGS applied to the given FILTER
1704 are recognized as being of (the existing) type TYPE.
1705 See also L</"Defining your own types">.
1707 =item B<--type-set I<TYPE>:I<FILTER>:I<ARGS>>
1709 Files with the given ARGS applied to the given FILTER are recognized as
1710 being of type TYPE. This replaces an existing definition for type TYPE. See
1711 also L</"Defining your own types">.
1713 =item B<--type-del I<TYPE>>
1715 The filters associated with TYPE are removed from Ack, and are no longer considered
1716 for searches.
1718 =item B<--[no]underline>
1720 Turns on underlining of matches, where "underlining" is printing a line of
1721 carets under the match.
1723 $ ack -u foo
1724 peanuts.txt
1725 17: Come kick the football you fool
1726 ^^^ ^^^
1727 623: Price per square foot
1730 This is useful if you're dumping the results of an ack run into a text
1731 file or printer that doesn't support ANSI color codes.
1733 The setting of underline does not affect highlighting of matches.
1735 =item B<-v>, B<--invert-match>
1737 Invert match: select non-matching lines.
1739 =item B<--version>
1741 Display version and copyright information.
1743 =item B<-w>, B<--word-regexp>
1745 Force PATTERN to match only whole words.
1747 =item B<-x>
1749 An abbreviation for B<--files-from=->. The list of files to search are read
1750 from standard input, with one line per file.
1752 Note that the list of files is B<not> filtered in any way. If you add
1753 C<--type=html> in addition to C<-x>, the C<--type> will be ignored.
1755 =item B<-1>
1757 Stops after reporting first match of any kind. This is different
1758 from B<--max-count=1> or B<-m1>, where only one match per file is
1759 shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does
1760 not.
1762 =item B<--thpppt>
1764 Display the all-important Bill The Cat logo. Note that the exact
1765 spelling of B<--thpppppt> is not important. It's checked against
1766 a regular expression.
1768 =item B<--bar>
1770 Check with the admiral for traps.
1772 =item B<--cathy>
1774 Chocolate, Chocolate, Chocolate!
1776 =back
1778 =head1 THE .ackrc FILE
1780 The F<.ackrc> file contains command-line options that are prepended
1781 to the command line before processing. Multiple options may live
1782 on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>
1783 might look like this:
1785 # Always sort the files
1786 --sort-files
1788 # Always color, even if piping to another program
1789 --color
1791 # Use "less -r" as my pager
1792 --pager=less -r
1794 Note that arguments with spaces in them do not need to be quoted,
1795 as they are not interpreted by the shell. Basically, each I<line>
1796 in the F<.ackrc> file is interpreted as one element of C<@ARGV>.
1798 F<ack> looks in several locations for F<.ackrc> files; the searching
1799 process is detailed in L</"ACKRC LOCATION SEMANTICS">. These
1800 files are not considered if B<--noenv> is specified on the command line.
1802 =head1 Defining your own types
1804 ack allows you to define your own types in addition to the predefined
1805 types. This is done with command line options that are best put into
1806 an F<.ackrc> file - then you do not have to define your types over and
1807 over again. In the following examples the options will always be shown
1808 on one command line so that they can be easily copy & pasted.
1810 File types can be specified both with the the I<--type=xxx> option,
1811 or the file type as an option itself. For example, if you create
1812 a filetype of "cobol", you can specify I<--type=cobol> or simply
1813 I<--cobol>. File types must be at least two characters long. This
1814 is why the C language is I<--cc> and the R language is I<--rr>.
1816 I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>
1817 tells you, that perl files are files ending
1818 in .pl, .pm, .pod or .t. So what if you would like to include .xs
1819 files as well when searching for --perl files? I<ack --type-add perl:ext:xs --perl foo>
1820 does this for you. B<--type-add> appends
1821 additional extensions to an existing type.
1823 If you want to define a new type, or completely redefine an existing
1824 type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines
1825 the type I<eiffel> to include files with
1826 the extensions .e or .eiffel. So to search for all eiffel files
1827 containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.
1828 As usual, you can also write B<--type=eiffel>
1829 instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes
1830 all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>
1831 and I<.xs> files no longer belong to the type I<cc>.
1833 When defining your own types in the F<.ackrc> file you have to use
1834 the following:
1836 --type-set=eiffel:ext:e,eiffel
1838 or writing on separate lines
1840 --type-set
1841 eiffel:ext:e,eiffel
1843 The following does B<NOT> work in the F<.ackrc> file:
1845 --type-set eiffel:ext:e,eiffel
1847 In order to see all currently defined types, use I<--help-types>, e.g.
1848 I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>
1850 In addition to filtering based on extension, ack offers additional
1851 filter types. The generic syntax is
1852 I<--type-set TYPE:FILTER:ARGS>; I<ARGS> depends on the value
1853 of I<FILTER>.
1855 =over 4
1857 =item is:I<FILENAME>
1859 I<is> filters match the target filename exactly. It takes exactly one
1860 argument, which is the name of the file to match.
1862 Example:
1864 --type-set make:is:Makefile
1866 =item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]
1868 I<ext> filters match the extension of the target file against a list
1869 of extensions. No leading dot is needed for the extensions.
1871 Example:
1873 --type-set perl:ext:pl,pm,t
1875 =item match:I<PATTERN>
1877 I<match> filters match the target filename against a regular expression.
1878 The regular expression is made case-insensitive for the search.
1880 Example:
1882 --type-set make:match:/(gnu)?makefile/
1884 =item firstlinematch:I<PATTERN>
1886 I<firstlinematch> matches the first line of the target file against a
1887 regular expression. Like I<match>, the regular expression is made
1888 case insensitive.
1890 Example:
1892 --type-add perl:firstlinematch:/perl/
1894 =back
1896 =head1 ACK COLORS
1898 ack allows customization of the colors it uses when presenting matches
1899 onscreen. It uses the colors available in Perl's L<Term::ANSIColor>
1900 module, which provides the following listed values. Note that case does not
1901 matter when using these values.
1903 There are four different colors ack uses:
1905 Aspect Option Env. variable Default
1906 -------- ----------------- ------------------ ---------------
1907 filename --color-filename ACK_COLOR_FILENAME black on_yellow
1908 match --color-match ACK_COLOR_MATCH bold green
1909 line no. --color-lineno ACK COLOR_LINENO bold yellow
1910 column no. --color-colno ACK COLOR_COLNO bold yellow
1912 The column number column is only used if the column number is shown because
1913 of the --column option.
1915 Colors may be specified by command-line option, such as
1916 C<ack --color-filename='red on_white'>, or by setting an environment
1917 variable, such as C<ACK_COLOR_FILENAME='red on_white'>. Options for colors
1918 can be set in your ACKRC file (See "THE .ackrc FILE").
1920 ack can understand the following colors for the foreground:
1922 black red green yellow blue magenta cyan white
1924 The optional background color is specified by prepending "on_" to one of
1925 the foreground colors:
1927 on_black on_red on_green on_yellow on_blue on_magenta on_cyan on_white
1929 Each of the foreground colors can be modified with the following
1930 attributes, which may or may not be supported by your terminal:
1932 bold faint italic underline blink reverse concealed
1934 Any combinations of modifiers can be added to the foreground color. If your
1935 terminal supports it, and you enjoy visual punishment, you can specify:
1937 ack --color-filename="blink italic underline bold red on_yellow"
1939 For charts of the colors and what they look like, run C<ack --help-colors>
1940 and C<ack --help-rgb-colors>.
1942 If the eight standard colors, in their bold, faint and unmodified states,
1943 aren't enough for you to choose from, you can also specify colors by their
1944 RGB values. They are specified as "rgbXYZ" where X, Y, and Z are values
1945 between 0 and 5 giving the intensity of red, green and blue, respectively.
1946 Therefore, "rgb500" is pure red, "rgb505" is purple, and so on.
1948 Background colors can be specified with the "on_" prefix prepended on an
1949 RGB color, so that "on_rgb505" would be a purple background.
1951 The modifier attributes of blink, italic, underscore and so on may or may
1952 not work on the RGB colors.
1954 For a chart of the 216 possible RGB colors, run C<ack --help-rgb-colors>.
1956 =head1 ENVIRONMENT VARIABLES
1958 For commonly-used ack options, environment variables can make life
1959 much easier. These variables are ignored if B<--noenv> is specified
1960 on the command line.
1962 =over 4
1964 =item ACKRC
1966 Specifies the location of the user's F<.ackrc> file. If this file doesn't
1967 exist, F<ack> looks in the default location.
1969 =item ACK_COLOR_COLNO
1971 Color specification for the column number in ack's output. By default, the
1972 column number is not shown. You have to enable it with the B<--column>
1973 option. See the section "ack Colors" above.
1975 =item ACK_COLOR_FILENAME
1977 Color specification for the filename in ack's output. See the section "ack
1978 Colors" above.
1980 =item ACK_COLOR_LINENO
1982 Color specification for the line number in ack's output. See the section
1983 "ack Colors" above.
1985 =item ACK_COLOR_MATCH
1987 Color specification for the matched text in ack's output. See the section
1988 "ack Colors" above.
1990 =item ACK_PAGER
1992 Specifies a pager program, such as C<more>, C<less> or C<most>, to which
1993 ack will send its output.
1995 Using C<ACK_PAGER> does not suppress grouping and coloring like
1996 piping output on the command-line does, except that on Windows
1997 ack will assume that C<ACK_PAGER> does not support color.
1999 C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
2001 =item ACK_PAGER_COLOR
2003 Specifies a pager program that understands ANSI color sequences.
2004 Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring
2005 like piping output on the command-line does.
2007 If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
2009 =back
2011 =head1 ACK & OTHER TOOLS
2013 =head2 Simple vim integration
2015 F<ack> integrates easily with the Vim text editor. Set this in your
2016 F<.vimrc> to use F<ack> instead of F<grep>:
2018 set grepprg=ack\ -k
2020 That example uses C<-k> to search through only files of the types ack
2021 knows about, but you may use other default flags. Now you can search
2022 with F<ack> and easily step through the results in Vim:
2024 :grep Dumper perllib
2026 =head2 Editor integration
2028 Many users have integrated ack into their preferred text editors.
2029 For details and links, see L<https://beyondgrep.com/more-tools/>.
2031 =head2 Shell and Return Code
2033 For greater compatibility with I<grep>, I<ack> in normal use returns
2034 shell return or exit code of 0 only if something is found and 1 if
2035 no match is found.
2037 (Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
2039 The I<grep> code 2 for errors is not used.
2041 If C<-f> or C<-g> are specified, then 0 is returned if at least one
2042 file is found. If no files are found, then 1 is returned.
2044 =cut
2046 =head1 DEBUGGING ACK PROBLEMS
2048 If ack gives you output you're not expecting, start with a few simple steps.
2050 =head2 Try it with B<--noenv>
2052 Your environment variables and F<.ackrc> may be doing things you're
2053 not expecting, or forgotten you specified. Use B<--noenv> to ignore
2054 your environment and F<.ackrc>.
2056 =head2 Use B<-f> to see what files have been selected for searching
2058 Ack's B<-f> was originally added as a debugging tool. If ack is
2059 not finding matches you think it should find, run F<ack -f> to see
2060 what files have been selected. You can also add the C<--show-types>
2061 options to show the type of each file selected.
2063 =head2 Use B<--dump>
2065 This lists the ackrc files that are loaded and the options loaded
2066 from them. You may be loading an F<.ackrc> file that you didn't know
2067 you were loading.
2069 =head1 ACKRC LOCATION SEMANTICS
2071 Ack can load its configuration from many sources. The following list
2072 specifies the sources Ack looks for configuration files; each one
2073 that is found is loaded in the order specified here, and
2074 each one overrides options set in any of the sources preceding
2075 it. (For example, if I set --sort-files in my user ackrc, and
2076 --nosort-files on the command line, the command line takes
2077 precedence)
2079 =over 4
2081 =item *
2083 Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted
2084 using C<--ignore-ack-defaults>.
2086 =item * Global ackrc
2088 Options are then loaded from the global ackrc. This is located at
2089 C</etc/ackrc> on Unix-like systems.
2091 Under Windows XP and earlier, the global ackrc is at
2092 C<C:\Documents and Settings\All Users\Application Data\ackrc>
2094 Under Windows Vista/7, the global ackrc is at
2095 C<C:\ProgramData\ackrc>
2097 The C<--noenv> option prevents all ackrc files from being loaded.
2099 =item * User ackrc
2101 Options are then loaded from the user's ackrc. This is located at
2102 C<$HOME/.ackrc> on Unix-like systems.
2104 Under Windows XP and earlier, the user's ackrc is at
2105 C<C:\Documents and Settings\$USER\Application Data\ackrc>.
2107 Under Windows Vista/7, the user's ackrc is at
2108 C<C:\Users\$USER\AppData\Roaming\ackrc>.
2110 If you want to load a different user-level ackrc, it may be specified
2111 with the C<$ACKRC> environment variable.
2113 The C<--noenv> option prevents all ackrc files from being loaded.
2115 =item * Project ackrc
2117 Options are then loaded from the project ackrc. The project ackrc is
2118 the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching
2119 in the current directory, then the parent directory, then the grandparent
2120 directory, etc. This can be omitted using C<--noenv>.
2122 =item * --ackrc
2124 The C<--ackrc> option may be included on the command line to specify an
2125 ackrc file that can override all others. It is consulted even if C<--noenv>
2126 is present.
2128 =item * Command line
2130 Options are then loaded from the command line.
2132 =back
2134 =head1 BUGS & ENHANCEMENTS
2136 ack is based at GitHub at L<https://github.com/beyondgrep/ack3>
2138 Please report any bugs or feature requests to the issues list at
2139 Github: L<https://github.com/beyondgrep/ack3/issues>.
2141 Please include the operating system that you're using; the output of
2142 the command C<ack --version>; and any customizations in your F<.ackrc>
2143 you may have.
2145 To suggest enhancements, please submit an issue at
2146 L<https://github.com/beyondgrep/ack3/issues>. Also read the
2147 F<DEVELOPERS.md> file in the ack code repository.
2149 Also, feel free to discuss your issues on the ack mailing
2150 list at L<https://groups.google.com/group/ack-users>.
2152 =head1 SUPPORT
2154 Support for and information about F<ack> can be found at:
2156 =over 4
2158 =item * The ack homepage
2160 L<https://beyondgrep.com/>
2162 =item * Source repository
2164 L<https://github.com/beyondgrep/ack3>
2166 =item * The ack issues list at Github
2168 L<https://github.com/beyondgrep/ack3/issues>
2170 =item * The ack announcements mailing list
2172 L<https://groups.google.com/group/ack-announcement>
2174 =item * The ack users' mailing list
2176 L<https://groups.google.com/group/ack-users>
2178 =item * The ack development mailing list
2180 L<https://groups.google.com/group/ack-users>
2182 =back
2184 =head1 COMMUNITY
2186 There are ack mailing lists and a Slack channel for ack. See
2187 L<https://beyondgrep.com/community/> for details.
2189 =head1 FAQ
2191 This is the Frequently Asked Questions list for ack.
2193 =head2 Can I stop using grep now?
2195 Many people find I<ack> to be better than I<grep> as an everyday tool
2196 99% of the time, but don't throw I<grep> away, because there are times
2197 you'll still need it. For example, you might be looking through huge
2198 log files and not using regular expressions. In that case, I<grep>
2199 will probably perform better.
2201 =head2 Why isn't ack finding a match in (some file)?
2203 First, take a look and see if ack is even looking at the file. ack is
2204 intelligent in what files it will search and which ones it won't, but
2205 sometimes that can be surprising.
2207 Use the C<-f> switch, with no regex, to see a list of files that ack
2208 will search for you. If your file doesn't show up in the list of files
2209 that C<ack -f> shows, then ack never looks in it.
2211 =head2 Wouldn't it be great if F<ack> did search & replace?
2213 No, ack will always be read-only. Perl has a perfectly good way
2214 to do search & replace in files, using the C<-i>, C<-p> and C<-n>
2215 switches.
2217 You can certainly use ack to select your files to update. For
2218 example, to change all "foo" to "bar" in all PHP files, you can do
2219 this from the Unix shell:
2221 $ perl -i -p -e's/foo/bar/g' $(ack -f --php)
2223 =head2 Can I make ack recognize F<.xyz> files?
2225 Yes! Please see L</"Defining your own types"> in the ack manual.
2227 =head2 Will you make ack recognize F<.xyz> files by default?
2229 We might, depending on how widely-used the file format is.
2231 Submit an issue at in the GitHub issue queue at
2232 L<https://github.com/beyondgrep/ack3/issues>. Explain what the file format
2233 is, where we can find out more about it, and what you have been using
2234 in your F<.ackrc> to support it.
2236 Please do not bother creating a pull request. The code for filetypes
2237 is trivial compared to the rest of the process we go through.
2239 =head2 Why is it called ack if it's called ack-grep?
2241 The name of the program is "ack". Some packagers have called it
2242 "ack-grep" when creating packages because there's already a package
2243 out there called "ack" that has nothing to do with this ack.
2245 I suggest you make a symlink named F<ack> that points to F<ack-grep>
2246 because one of the crucial benefits of ack is having a name that's
2247 so short and simple to type.
2249 To do that, run this with F<sudo> or as root:
2251 ln -s /usr/bin/ack-grep /usr/bin/ack
2253 Alternatively, you could use a shell alias:
2255 # bash/zsh
2256 alias ack=ack-grep
2258 # csh
2259 alias ack ack-grep
2261 =head2 What does F<ack> mean?
2263 Nothing. I wanted a name that was easy to type and that you could
2264 pronounce as a single syllable.
2266 =head2 Can I do multi-line regexes?
2268 No, ack does not support regexes that match multiple lines. Doing
2269 so would require reading in the entire file at a time.
2271 If you want to see lines near your match, use the C<--A>, C<--B>
2272 and C<--C> switches for displaying context.
2274 =head2 Why is ack telling me I have an invalid option when searching for C<+foo>?
2276 ack treats command line options beginning with C<+> or C<-> as options; if you
2277 would like to search for these, you may prefix your search term with C<--> or
2278 use the C<--match> option. (However, don't forget that C<+> is a regular
2279 expression metacharacter!)
2281 =head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?
2283 The Perl language limits the repetition quantifier to 32K. You
2284 can search for C<.{32767}> but not C<.{32768}>.
2286 =head2 Ack does "X" and shouldn't, should it?
2288 We try to remain as close to grep's behavior as possible, so when in
2289 doubt, see what grep does! If there's a mismatch in functionality there,
2290 please submit an issue to GitHub, and/or bring it up on the ack-users
2291 mailing list.
2293 =cut
2295 =head1 ACKNOWLEDGEMENTS
2297 How appropriate to have I<ack>nowledgements!
2299 Thanks to everyone who has contributed to ack in any way, including
2300 Dan Book,
2301 Tomasz Konojacki,
2302 Salomon Smeke,
2303 M. Scott Ford,
2304 Anders Eriksson,
2305 H.Merijn Brand,
2306 Duke Leto,
2307 Gerhard Poul,
2308 Ethan Mallove,
2309 Marek Kubica,
2310 Ray Donnelly,
2311 Nikolaj Schumacher,
2312 Ed Avis,
2313 Nick Morrott,
2314 Austin Chamberlin,
2315 Varadinsky,
2316 SE<eacute>bastien FeugE<egrave>re,
2317 Jakub Wilk,
2318 Pete Houston,
2319 Stephen Thirlwall,
2320 Jonah Bishop,
2321 Chris Rebert,
2322 Denis Howe,
2323 RaE<uacute>l GundE<iacute>n,
2324 James McCoy,
2325 Daniel Perrett,
2326 Steven Lee,
2327 Jonathan Perret,
2328 Fraser Tweedale,
2329 RaE<aacute>l GundE<aacute>n,
2330 Steffen Jaeckel,
2331 Stephan Hohe,
2332 Michael Beijen,
2333 Alexandr Ciornii,
2334 Christian Walde,
2335 Charles Lee,
2336 Joe McMahon,
2337 John Warwick,
2338 David Steinbrunner,
2339 Kara Martens,
2340 Volodymyr Medvid,
2341 Ron Savage,
2342 Konrad Borowski,
2343 Dale Sedivic,
2344 Michael McClimon,
2345 Andrew Black,
2346 Ralph Bodenner,
2347 Shaun Patterson,
2348 Ryan Olson,
2349 Shlomi Fish,
2350 Karen Etheridge,
2351 Olivier Mengue,
2352 Matthew Wild,
2353 Scott Kyle,
2354 Nick Hooey,
2355 Bo Borgerson,
2356 Mark Szymanski,
2357 Marq Schneider,
2358 Packy Anderson,
2359 JR Boyens,
2360 Dan Sully,
2361 Ryan Niebur,
2362 Kent Fredric,
2363 Mike Morearty,
2364 Ingmar Vanhassel,
2365 Eric Van Dewoestine,
2366 Sitaram Chamarty,
2367 Adam James,
2368 Richard Carlsson,
2369 Pedro Melo,
2370 AJ Schuster,
2371 Phil Jackson,
2372 Michael Schwern,
2373 Jan Dubois,
2374 Christopher J. Madsen,
2375 Matthew Wickline,
2376 David Dyck,
2377 Jason Porritt,
2378 Jjgod Jiang,
2379 Thomas Klausner,
2380 Uri Guttman,
2381 Peter Lewis,
2382 Kevin Riggle,
2383 Ori Avtalion,
2384 Torsten Blix,
2385 Nigel Metheringham,
2386 GE<aacute>bor SzabE<oacute>,
2387 Tod Hagan,
2388 Michael Hendricks,
2389 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,
2390 Piers Cawley,
2391 Stephen Steneker,
2392 Elias Lutfallah,
2393 Mark Leighton Fisher,
2394 Matt Diephouse,
2395 Christian Jaeger,
2396 Bill Sully,
2397 Bill Ricker,
2398 David Golden,
2399 Nilson Santos F. Jr,
2400 Elliot Shank,
2401 Merijn Broeren,
2402 Uwe Voelker,
2403 Rick Scott,
2404 Ask BjE<oslash>rn Hansen,
2405 Jerry Gay,
2406 Will Coleda,
2407 Mike O'Regan,
2408 Slaven ReziE<0x107>,
2409 Mark Stosberg,
2410 David Alan Pisoni,
2411 Adriano Ferreira,
2412 James Keenan,
2413 Leland Johnson,
2414 Ricardo Signes,
2415 Pete Krawczyk and
2416 Rob Hoelz.
2418 =head1 AUTHOR
2420 Andy Lester, C<< <andy at petdance.com> >>
2422 =head1 COPYRIGHT & LICENSE
2424 Copyright 2005-2019 Andy Lester.
2426 This program is free software; you can redistribute it and/or modify
2427 it under the terms of the Artistic License v2.0.
2429 See https://www.perlfoundation.org/artistic-license-20.html or the LICENSE.md
2430 file that comes with the ack distribution.
2432 =cut
2435 package App::Ack;
2437 use warnings;
2438 use strict;
2441 our $VERSION;
2442 our $COPYRIGHT;
2443 BEGIN {
2444 $VERSION = 'v3.2.0'; # Check https://beyondgrep.com/ for updates
2445 $COPYRIGHT = 'Copyright 2005-2019 Andy Lester.';
2447 our $STANDALONE = 0;
2448 our $ORIGINAL_PROGRAM_NAME;
2450 our $fh;
2452 BEGIN {
2453 $fh = *STDOUT;
2457 our %types;
2458 our %type_wanted;
2459 our %mappings;
2460 our %ignore_dirs;
2462 our $is_filter_mode;
2463 our $output_to_pipe;
2465 our $is_windows;
2467 our $debug_nopens = 0;
2469 # Line ending, changes to "\0" if --print0.
2470 our $ors = "\n";
2472 BEGIN {
2473 # These have to be checked before any filehandle diddling.
2474 $output_to_pipe = not -t *STDOUT;
2475 $is_filter_mode = -p STDIN;
2477 $is_windows = ($^O eq 'MSWin32');
2481 sub warn {
2482 return CORE::warn( _my_program(), ': ', @_, "\n" );
2486 sub die {
2487 return CORE::die( _my_program(), ': ', @_, "\n" );
2490 sub _my_program {
2491 require File::Basename;
2492 return File::Basename::basename( $0 );
2496 sub thpppt {
2497 my $y = q{_ /|,\\'!.x',=(www)=, U };
2498 $y =~ tr/,x!w/\nOo_/;
2500 App::Ack::print( "$y ack $_[0]!\n" );
2501 exit 0;
2504 sub ackbar {
2505 my $x;
2506 $x = <<'_BAR';
2507 6?!I'7!I"?%+!
2508 3~!I#7#I"7#I!?!+!="+"="+!:!
2509 2?#I!7!I!?#I!7!I"+"=%+"=#
2510 1?"+!?*+!=#~"=!+#?"="+!
2511 0?"+!?"I"?&+!="~!=!~"=!+%="+"
2512 /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!
2513 .?%I"?%+%='?!=#~$="
2514 ,,!?%I"?(+$=$~!=#:"~$:!~!
2515 ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!
2516 +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"
2517 *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!
2518 *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"
2519 )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!
2520 (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!
2521 (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!
2522 %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"
2523 $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/
2524 $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!
2525 $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/
2526 %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!
2527 %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!
2528 %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!
2529 %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!
2530 &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!
2531 &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!
2532 &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!
2533 &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!
2534 &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"
2535 &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!
2536 %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&
2537 %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)
2538 $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!
2539 $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"
2540 "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!
2541 !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!
2542 !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*
2543 7+:!,!~#,"=!7'I!?#I"7/+!7+
2544 77I!+!7!?!7!I"71+!7,
2545 _BAR
2547 return _pic_decode($x);
2550 sub cathy {
2551 my $x = <<'CATHY';
2552 0+!--+!
2553 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2554 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2555 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2556 0|! $A"C!K!!! $|!
2557 0+!--+!
2558 6\! 1:!,!.! !
2559 7\! /.!M!~!Z!M!~!
2560 8\! /~!D! "M! !
2561 4.! $\! /M!~!.!8! +.!M# 4
2562 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3
2563 /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4
2564 ..! !D!Z!.!Z!.! '\! 9=!M".! 6
2565 /.! !.!~!M".! '\! 8~! 9
2566 4M!.! /.!7!N!M!.! F
2567 4.! &:!M! !N"M# !M"N!M! #D!M&=! =
2568 :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;
2569 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9
2570 (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0
2571 &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! +
2572 &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *
2573 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! +
2574 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4
2575 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3
2576 /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1
2577 #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0
2578 $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '
2579 #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (
2580 #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %
2581 *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &
2582 )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '
2583 )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! +
2584 (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! +
2585 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *
2586 %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )
2587 !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (
2588 !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &
2589 !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $
2590 !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $
2591 (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #
2592 '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %
2593 $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! +
2594 ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )
2595 +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (
2596 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )
2597 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /
2598 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2
2599 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %
2600 0?!$! &N! )." .,! %."M! ":!M!.! 0
2601 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0
2602 CATHY
2603 return _pic_decode($x);
2606 sub _pic_decode {
2607 my($compressed) = @_;
2608 $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;
2609 App::Ack::print( $compressed );
2610 exit 0;
2614 sub show_help {
2615 App::Ack::print( <<"END_OF_HELP" );
2616 Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
2618 Search for PATTERN in each source file in the tree from the current
2619 directory on down. If any files or directories are specified, then
2620 only those files and directories are checked. ack may also search
2621 STDIN, but only if no file or directory arguments are specified,
2622 or if one of them is "-".
2624 Default switches may be specified in an .ackrc file. If you want no dependency
2625 on the environment, turn it off with --noenv.
2627 Example: ack -i select
2629 Searching:
2630 -i, --ignore-case Ignore case distinctions in PATTERN
2631 -S, --[no]smart-case Ignore case distinctions in PATTERN,
2632 only if PATTERN contains no upper case.
2633 Ignored if -i or -I are specified.
2634 -I, --no-ignore-case Turns on case-sensitivity in PATTERN.
2635 Negates -i and --smart-case.
2636 -v, --invert-match Invert match: select non-matching lines
2637 -w, --word-regexp Force PATTERN to match only whole words
2638 -Q, --literal Quote all metacharacters; PATTERN is literal
2639 --range-start PATTERN Specify PATTERN as the start of a match range.
2640 --range-end PATTERN Specify PATTERN as the end of a match range.
2641 --match PATTERN Specify PATTERN explicitly. Typically omitted.
2643 Search output:
2644 -l, --files-with-matches Only print filenames containing matches
2645 -L, --files-without-matches Only print filenames with no matches
2646 --output=expr Output the evaluation of expr for each line
2647 (turns off text highlighting)
2648 -o Show only the part of a line matching PATTERN
2649 Same as --output='\$&'
2650 --passthru Print all lines, whether matching or not
2651 -m, --max-count=NUM Stop searching in each file after NUM matches
2652 -1 Stop searching after one match of any kind
2653 -H, --with-filename Print the filename for each match (default:
2654 on unless explicitly searching a single file)
2655 -h, --no-filename Suppress the prefixing filename on output
2656 -c, --count Show number of lines matching per file
2657 --[no]column Show the column number of the first match
2659 -A NUM, --after-context=NUM Print NUM lines of trailing context after
2660 matching lines.
2661 -B NUM, --before-context=NUM Print NUM lines of leading context before
2662 matching lines.
2663 -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.
2665 --print0 Print null byte as separator between filenames,
2666 only works with -f, -g, -l, -L or -c.
2668 -s Suppress error messages about nonexistent or
2669 unreadable files.
2672 File presentation:
2673 --pager=COMMAND Pipes all ack output through COMMAND. For
2674 example, --pager="less -R". Ignored if output
2675 is redirected.
2676 --nopager Do not send output through a pager. Cancels
2677 any setting in ~/.ackrc, ACK_PAGER or
2678 ACK_PAGER_COLOR.
2679 --[no]heading Print a filename heading above each file's
2680 results. (default: on when used interactively)
2681 --[no]break Print a break between results from different
2682 files. (default: on when used interactively)
2683 --group Same as --heading --break
2684 --nogroup Same as --noheading --nobreak
2685 -p, --proximate=LINES Separate match output with blank lines unless
2686 they are within LINES lines from each other.
2687 -P, --proximate=0 Negates --proximate.
2688 --[no]underline Print a line of carets under the matched text.
2689 --[no]color, --[no]colour Highlight the matching text (default: on unless
2690 output is redirected, or on Windows)
2691 --color-filename=COLOR
2692 --color-match=COLOR
2693 --color-colno=COLOR
2694 --color-lineno=COLOR Set the color for filenames, matches, line and
2695 column numbers.
2696 --help-colors Show a list of possible color combinations.
2697 --help-rgb-colors Show a list of advanced RGB colors.
2698 --flush Flush output immediately, even when ack is used
2699 non-interactively (when output goes to a pipe or
2700 file).
2703 File finding:
2704 -f Only print the files selected, without
2705 searching. The PATTERN must not be specified.
2706 -g Same as -f, but only select files matching
2707 PATTERN.
2708 --sort-files Sort the found files lexically.
2709 --show-types Show which types each file has.
2710 --files-from=FILE Read the list of files to search from FILE.
2711 -x Read the list of files to search from STDIN.
2713 File inclusion/exclusion:
2714 --[no]ignore-dir=name Add/remove directory from list of ignored dirs
2715 --[no]ignore-directory=name Synonym for ignore-dir
2716 --ignore-file=FILTER:ARGS Add filter for ignoring files.
2717 -r, -R, --recurse Recurse into subdirectories (default: on)
2718 -n, --no-recurse No descending into subdirectories
2719 --[no]follow Follow symlinks. Default is off.
2721 File type inclusion/exclusion:
2722 -t X, --type=X Include only X files, where X is a filetype,
2723 e.g. python, html, markdown, etc
2724 -T X, --type=noX Exclude X files, where X is a filetype.
2725 -k, --known-types Include only files of types that ack recognizes.
2726 --help-types Display all known types, and how they're defined.
2728 File type specification:
2729 --type-set=TYPE:FILTER:ARGS Files with the given ARGS applied to the given
2730 FILTER are recognized as being of type TYPE.
2731 This replaces an existing definition for TYPE.
2732 --type-add=TYPE:FILTER:ARGS Files with the given ARGS applied to the given
2733 FILTER are recognized as being type TYPE.
2734 --type-del=TYPE Removes all filters associated with TYPE.
2736 Miscellaneous:
2737 --version Display version & copyright
2738 --[no]env Ignore environment variables and global ackrc
2739 files. --env is legal but redundant.
2740 --ackrc=filename Specify an ackrc file to use
2741 --ignore-ack-defaults Ignore default definitions included with ack.
2742 --create-ackrc Outputs a default ackrc for your customization
2743 to standard output.
2744 --dump Dump information on which options are loaded
2745 and where they're defined.
2746 --[no]filter Force ack to treat standard input as a pipe
2747 (--filter) or tty (--nofilter)
2748 --help This help
2749 --man Print the manual.
2750 --help-types Display all known types, and how they're defined.
2751 --help-colors Show a list of possible color combinations.
2752 --help-rgb-colors Show a list of advanced RGB colors.
2753 --thpppt Bill the Cat
2754 --bar The warning admiral
2755 --cathy Chocolate! Chocolate! Chocolate!
2757 Filter specifications:
2758 If FILTER is "ext", ARGS is a list of extensions checked against the
2759 file's extension.
2760 If FILTER is "is", ARGS must match the file's name exactly.
2761 If FILTER is "match", ARGS is matched as a case-insensitive regex
2762 against the filename.
2763 If FILTER is "firstlinematch", ARGS is matched as a regex the first
2764 line of the file's contents.
2766 Exit status is 0 if match, 1 if no match.
2768 ack's home page is at https://beyondgrep.com/
2770 The full ack manual is available by running "ack --man".
2772 This is version $App::Ack::VERSION of ack. Run "ack --version" for full version info.
2773 END_OF_HELP
2775 return;
2780 sub show_help_types {
2781 App::Ack::print( <<'END_OF_HELP' );
2782 Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
2784 The following is the list of filetypes supported by ack. You can specify a
2785 filetype to include with -t TYPE or --type=TYPE. You can exclude a
2786 filetype with -T TYPE or --type=noTYPE.
2788 Note that some files may appear in multiple types. For example, a file
2789 called Rakefile is both Ruby (--type=ruby) and Rakefile (--type=rakefile).
2791 END_OF_HELP
2793 my @types = keys %App::Ack::mappings;
2794 my $maxlen = 0;
2795 for ( @types ) {
2796 $maxlen = length if $maxlen < length;
2798 for my $type ( sort @types ) {
2799 next if $type =~ /^-/; # Stuff to not show
2800 my $ext_list = $mappings{$type};
2802 if ( ref $ext_list ) {
2803 $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );
2805 App::Ack::print( sprintf( " %-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
2808 return;
2813 sub show_help_colors {
2814 App::Ack::print( <<'END_OF_HELP' );
2815 ack allows customization of the colors it uses when presenting matches
2816 onscreen. See the "ACK COLORS" section of the ack manual (ack --man).
2818 Here is a chart of how various color combinations appear: Each of the eight
2819 foreground colors, on each of the eight background colors or no background
2820 color, with and without the bold modifier.
2822 Run ack --help-rgb-colors for a chart of the RGB colors.
2824 END_OF_HELP
2826 _show_color_grid();
2828 return;
2833 sub show_help_rgb {
2834 App::Ack::print( <<'END_OF_HELP' );
2835 ack allows customization of the colors it uses when presenting matches
2836 onscreen. See the "ACK COLORS" section of the ack manual (ack --man).
2838 Colors may be specified as "rggNNN" where "NNN" is a triplet of digits
2839 from 0 to 5 specifying the intensity of red, green and blue, respectively.
2841 Here is a grid of the 216 possible values for NNN.
2843 END_OF_HELP
2845 _show_rgb_grid();
2847 App::Ack::say( 'Here are the 216 possible colors with the "reverse" modifier applied.', "\n" );
2849 _show_rgb_grid( 'reverse' );
2851 return;
2855 sub _show_color_grid {
2856 my $cell_width = 7;
2858 my @fg_colors = qw( black red green yellow blue magenta cyan white );
2859 my @bg_colors = map { "on_$_" } @fg_colors;
2861 App::Ack::say(
2862 _color_cell( '' ),
2863 map { _color_cell( $_ ) } @fg_colors
2866 App::Ack::say(
2867 _color_cell( '' ),
2868 map { _color_cell( '-' x $cell_width ) } @fg_colors
2871 for my $bg ( '', @bg_colors ) {
2872 App::Ack::say(
2873 _color_cell( '' ),
2874 ( map { _color_cell( $_, "$_ $bg" ) } @fg_colors ),
2878 App::Ack::say(
2879 _color_cell( 'bold' ),
2880 ( map { _color_cell( $_, "bold $_ $bg" ) } @fg_colors ),
2883 App::Ack::say();
2886 return;
2890 sub _color_cell {
2891 my $text = shift;
2892 my $color = shift;
2894 my $cell_width = 7;
2895 $text = sprintf( '%-*s', $cell_width, $text );
2897 return ($color ? Term::ANSIColor::colored( $text, $color ) : $text) . ' ';
2901 sub _show_rgb_grid {
2902 my $modifier = shift // '';
2904 my $grid = <<'HERE';
2905 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
2906 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
2907 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
2908 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
2909 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
2910 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
2911 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
2912 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
2914 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
2916 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
2918 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
2920 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
2922 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
2923 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
2925 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
2927 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
2928 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
2929 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
2930 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
2931 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
2932 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
2933 HERE
2935 $grid =~ s/(\d\d\d)/Term::ANSIColor::colored( "$1", "$modifier rgb$1" )/eg;
2937 App::Ack::say( $grid );
2939 return;
2943 sub show_man {
2944 require Pod::Usage;
2945 Pod::Usage::pod2usage({
2946 -input => $App::Ack::ORIGINAL_PROGRAM_NAME,
2947 -verbose => 2,
2948 -exitval => 0,
2951 return;
2956 sub get_version_statement {
2957 require Config;
2959 my $copyright = $App::Ack::COPYRIGHT;
2960 my $this_perl = $Config::Config{perlpath};
2961 if ($^O ne 'VMS') {
2962 my $ext = $Config::Config{_exe};
2963 $this_perl .= $ext unless $this_perl =~ m/$ext$/i;
2965 my $perl_ver = sprintf( 'v%vd', $^V );
2967 my $build_type = $App::Ack::STANDALONE ? 'standalone version' : 'standard build';
2969 return <<"END_OF_VERSION";
2970 ack $App::Ack::VERSION ($build_type)
2971 Running under Perl $perl_ver at $this_perl
2973 $copyright
2975 This program is free software. You may modify or distribute it
2976 under the terms of the Artistic License v2.0.
2977 END_OF_VERSION
2981 sub print { print {$fh} @_; return; }
2982 sub say { print {$fh} @_, $ors; return; }
2983 sub print_blank_line { print {$fh} "\n"; return; }
2985 sub set_up_pager {
2986 my $command = shift;
2988 return if App::Ack::output_to_pipe();
2990 my $pager;
2991 if ( not open( $pager, '|-', $command ) ) {
2992 App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
2994 $fh = $pager;
2996 return;
3000 sub output_to_pipe {
3001 return $output_to_pipe;
3005 sub exit_from_ack {
3006 my $nmatches = shift;
3008 my $rc = $nmatches ? 0 : 1;
3009 exit $rc;
3013 sub show_types {
3014 my $file = shift;
3016 my @types = filetypes( $file );
3017 my $arrow = @types ? ' => ' : ' =>';
3018 App::Ack::say( $file->name, $arrow, join( ',', @types ) );
3020 return;
3024 sub filetypes {
3025 my ( $file ) = @_;
3027 my @matches;
3029 foreach my $k (keys %App::Ack::mappings) {
3030 my $filters = $App::Ack::mappings{$k};
3032 foreach my $filter (@{$filters}) {
3033 # Clone the file.
3034 my $clone = $file->clone;
3035 if ( $filter->filter($clone) ) {
3036 push @matches, $k;
3037 last;
3042 # https://metacpan.org/pod/distribution/Perl-Critic/lib/Perl/Critic/Policy/Subroutines/ProhibitReturnSort.pm
3043 @matches = sort @matches;
3044 return @matches;
3048 sub is_lowercase {
3049 my $pat = shift;
3051 # The simplest case.
3052 return 1 if lc($pat) eq $pat;
3054 # If we have capitals, then go clean up any metacharacters that might have capitals.
3056 # Get rid of any literal backslashes first to avoid confusion.
3057 $pat =~ s/\\\\//g;
3059 my $metacharacter = qr/
3060 |\\A # Beginning of string
3061 |\\B # Not word boundary
3062 |\\c[a-zA-Z] # Control characters
3063 |\\D # Non-digit character
3064 |\\G # End-of-match position of prior match
3065 |\\H # Not horizontal whitespace
3066 |\\K # Keep to the left
3067 |\\N(\{.+?\})? # Anything but \n, OR Unicode sequence
3068 |\\[pP]\{.+?\} # Named property and negation
3069 |\\[pP][A-Z] # Named property and negation, single-character shorthand
3070 |\\R # Linebreak
3071 |\\S # Non-space character
3072 |\\V # Not vertical whitespace
3073 |\\W # Non-word character
3074 |\\X # ???
3075 |\\x[0-9A-Fa-f]{2} # Hex sequence
3076 |\\Z # End of string
3078 $pat =~ s/$metacharacter//g;
3080 my $name = qr/[_A-Za-z][_A-Za-z0-9]*?/;
3081 # Eliminate named captures.
3082 $pat =~ s/\(\?'$name'//g;
3083 $pat =~ s/\(\?<$name>//g;
3085 # Eliminate named backreferences.
3086 $pat =~ s/\\k'$name'//g;
3087 $pat =~ s/\\k<$name>//g;
3088 $pat =~ s/\\k\{$name\}//g;
3090 # Now with those metacharacters and named things removed, now see if it's lowercase.
3091 return 1 if lc($pat) eq $pat;
3093 return 0;
3097 1; # End of App::Ack
3098 package App::Ack::ConfigDefault;
3100 use warnings;
3101 use strict;
3106 sub options {
3107 return split( /\n/, _options_block() );
3111 sub options_clean {
3112 return grep { /./ && !/^#/ } options();
3116 sub _options_block {
3117 my $lines = <<'HERE';
3118 # This is the default ackrc for ack version ==VERSION==.
3120 # There are four different ways to match
3122 # is: Match the filename exactly
3124 # ext: Match the extension of the filename exactly
3126 # match: Match the filename against a Perl regular expression
3128 # firstlinematch: Match the first 250 characters of the first line
3129 # of text against a Perl regular expression. This is only for
3130 # the --type-add option.
3133 ### Directories to ignore
3135 # Bazaar
3136 # https://bazaar.canonical.com/
3137 --ignore-directory=is:.bzr
3139 # Codeville
3140 # http://freshmeat.sourceforge.net/projects/codeville
3141 --ignore-directory=is:.cdv
3143 # Interface Builder (Xcode)
3144 # https://en.wikipedia.org/wiki/Interface_Builder
3145 --ignore-directory=is:~.dep
3146 --ignore-directory=is:~.dot
3147 --ignore-directory=is:~.nib
3148 --ignore-directory=is:~.plst
3150 # Git
3151 # https://git-scm.com/
3152 --ignore-directory=is:.git
3153 # When submodules are used, .git is a file.
3154 --ignore-file=is:.git
3156 # Mercurial
3157 # https://www.mercurial-scm.org/
3158 --ignore-directory=is:.hg
3160 # Quilt
3161 # https://directory.fsf.org/wiki/Quilt
3162 --ignore-directory=is:.pc
3164 # Subversion
3165 # https://subversion.apache.org/
3166 --ignore-directory=is:.svn
3168 # Monotone
3169 # https://www.monotone.ca/
3170 --ignore-directory=is:_MTN
3172 # CVS
3173 # https://savannah.nongnu.org/projects/cvs
3174 --ignore-directory=is:CVS
3176 # RCS
3177 # https://www.gnu.org/software/rcs/
3178 --ignore-directory=is:RCS
3180 # SCCS
3181 # https://en.wikipedia.org/wiki/Source_Code_Control_System
3182 --ignore-directory=is:SCCS
3184 # darcs
3185 # http://darcs.net/
3186 --ignore-directory=is:_darcs
3188 # Vault/Fortress
3189 --ignore-directory=is:_sgbak
3191 # autoconf
3192 # https://www.gnu.org/software/autoconf/
3193 --ignore-directory=is:autom4te.cache
3195 # Perl module building
3196 --ignore-directory=is:blib
3197 --ignore-directory=is:_build
3199 # Perl Devel::Cover module's output directory
3200 # https://metacpan.org/release/Devel-Cover
3201 --ignore-directory=is:cover_db
3203 # Node modules created by npm
3204 --ignore-directory=is:node_modules
3206 # CMake cache
3207 # https://www.cmake.org/
3208 --ignore-directory=is:CMakeFiles
3210 # Eclipse workspace folder
3211 # https://eclipse.org/
3212 --ignore-directory=is:.metadata
3214 # Cabal (Haskell) sandboxes
3215 # https://www.haskell.org/cabal/users-guide/installing-packages.html
3216 --ignore-directory=is:.cabal-sandbox
3218 ### Files to ignore
3220 # Backup files
3221 --ignore-file=ext:bak
3222 --ignore-file=match:/~$/
3224 # Emacs swap files
3225 --ignore-file=match:/^#.+#$/
3227 # vi/vim swap files http://vim.org/
3228 --ignore-file=match:/[._].*\.swp$/
3230 # core dumps
3231 --ignore-file=match:/core\.\d+$/
3233 # minified Javascript
3234 --ignore-file=match:/[.-]min[.]js$/
3235 --ignore-file=match:/[.]js[.]min$/
3237 # minified CSS
3238 --ignore-file=match:/[.]min[.]css$/
3239 --ignore-file=match:/[.]css[.]min$/
3241 # JS and CSS source maps
3242 --ignore-file=match:/[.]js[.]map$/
3243 --ignore-file=match:/[.]css[.]map$/
3245 # PDFs, because they pass Perl's -T detection
3246 --ignore-file=ext:pdf
3248 # Common graphics, just as an optimization
3249 --ignore-file=ext:gif,jpg,jpeg,png
3251 # Common archives, as an optimization
3252 --ignore-file=ext:gz,tar,tgz,zip
3255 ### Filetypes defined
3257 # Makefiles
3258 # https://www.gnu.org/s/make/
3259 --type-add=make:ext:mk
3260 --type-add=make:ext:mak
3261 --type-add=make:is:makefile
3262 --type-add=make:is:Makefile
3263 --type-add=make:is:Makefile.Debug
3264 --type-add=make:is:Makefile.Release
3265 --type-add=make:is:GNUmakefile
3267 # Rakefiles
3268 # https://rake.rubyforge.org/
3269 --type-add=rake:is:Rakefile
3271 # CMake
3272 # https://cmake.org/
3273 --type-add=cmake:is:CMakeLists.txt
3274 --type-add=cmake:ext:cmake
3276 # Actionscript
3277 --type-add=actionscript:ext:as,mxml
3279 # Ada
3280 # https://www.adaic.org/
3281 --type-add=ada:ext:ada,adb,ads
3283 # ASP
3284 # https://docs.microsoft.com/en-us/previous-versions/office/developer/server-technologies/aa286483(v=msdn.10)
3285 --type-add=asp:ext:asp
3287 # ASP.Net
3288 # https://dotnet.microsoft.com/apps/aspnet
3289 --type-add=aspx:ext:master,ascx,asmx,aspx,svc
3291 # Assembly
3292 --type-add=asm:ext:asm,s
3294 # DOS/Windows batch
3295 --type-add=batch:ext:bat,cmd
3297 # ColdFusion
3298 # https://en.wikipedia.org/wiki/ColdFusion
3299 --type-add=cfmx:ext:cfc,cfm,cfml
3301 # Clojure
3302 # https://clojure.org/
3303 --type-add=clojure:ext:clj,cljs,edn,cljc
3306 # .xs are Perl C files
3307 --type-add=cc:ext:c,h,xs
3309 # C header files
3310 --type-add=hh:ext:h
3312 # CoffeeScript
3313 # https://coffeescript.org/
3314 --type-add=coffeescript:ext:coffee
3316 # C++
3317 --type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx
3319 # C++ header files
3320 --type-add=hpp:ext:hpp,hh,h,hxx
3322 # C#
3323 --type-add=csharp:ext:cs
3325 # CSS
3326 # https://www.w3.org/Style/CSS/
3327 --type-add=css:ext:css
3329 # Dart
3330 # https://dart.dev/
3331 --type-add=dart:ext:dart
3333 # Delphi
3334 # https://en.wikipedia.org/wiki/Embarcadero_Delphi
3335 --type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj
3337 # Elixir
3338 # https://elixir-lang.org/
3339 --type-add=elixir:ext:ex,exs
3341 # Emacs Lisp
3342 # https://www.gnu.org/software/emacs
3343 --type-add=elisp:ext:el
3345 # Erlang
3346 # https://www.erlang.org/
3347 --type-add=erlang:ext:erl,hrl
3349 # Fortran
3350 # https://en.wikipedia.org/wiki/Fortran
3351 --type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp
3353 # Go
3354 # https://golang.org/
3355 --type-add=go:ext:go
3357 # Groovy
3358 # https://www.groovy-lang.org/
3359 --type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
3361 # GSP
3362 # https://gsp.grails.org/
3363 --type-add=gsp:ext:gsp
3365 # Haskell
3366 # http://www.haskell.org/
3367 --type-add=haskell:ext:hs,lhs
3369 # HTML
3370 --type-add=html:ext:htm,html,xhtml
3372 # Jade
3373 # http://jade-lang.com/
3374 --type-add=jade:ext:jade
3376 # Java
3377 # https://www.oracle.com/technetwork/java/index.html
3378 --type-add=java:ext:java,properties
3380 # JavaScript
3381 --type-add=js:ext:js
3383 # JSP
3384 # https://www.oracle.com/technetwork/java/javaee/jsp/index.html
3385 --type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml
3387 # JSON
3388 # https://json.org/
3389 --type-add=json:ext:json
3391 # Kotlin
3392 # https://kotlinlang.org/
3393 --type-add=kotlin:ext:kt,kts
3395 # Less
3396 # http://www.lesscss.org/
3397 --type-add=less:ext:less
3399 # Common Lisp
3400 # https://common-lisp.net/
3401 --type-add=lisp:ext:lisp,lsp
3403 # Lua
3404 # https://www.lua.org/
3405 --type-add=lua:ext:lua
3406 --type-add=lua:firstlinematch:/^#!.*\blua(jit)?/
3408 # Markdown
3409 # https://en.wikipedia.org/wiki/Markdown
3410 --type-add=markdown:ext:md,markdown
3411 # We understand that there are many ad hoc extensions for markdown
3412 # that people use. .md and .markdown are the two that ack recognizes.
3413 # You are free to add your own in your ackrc file.
3415 # Matlab
3416 # https://en.wikipedia.org/wiki/MATLAB
3417 --type-add=matlab:ext:m
3419 # Objective-C
3420 --type-add=objc:ext:m,h
3422 # Objective-C++
3423 --type-add=objcpp:ext:mm,h
3425 # OCaml
3426 # https://ocaml.org/
3427 --type-add=ocaml:ext:ml,mli,mll,mly
3429 # Perl
3430 # http://perl.org/
3431 --type-add=perl:ext:pl,pm,pod,t,psgi
3432 --type-add=perl:firstlinematch:/^#!.*\bperl/
3434 # Perl tests
3435 --type-add=perltest:ext:t
3437 # Perl's Plain Old Documentation format, POD
3438 --type-add=pod:ext:pod
3440 # PHP
3441 # https://www.php.net/
3442 --type-add=php:ext:php,phpt,php3,php4,php5,phtml
3443 --type-add=php:firstlinematch:/^#!.*\bphp/
3445 # Plone
3446 # https://plone.org/
3447 --type-add=plone:ext:pt,cpt,metadata,cpy,py
3449 # Python
3450 # https://www.python.org/
3451 --type-add=python:ext:py
3452 --type-add=python:firstlinematch:/^#!.*\bpython/
3455 # https://www.r-project.org/
3456 --type-add=rr:ext:R
3458 # reStructured Text
3459 # http://docutils.sourceforge.net/rst.html
3460 --type-add=rst:ext:rst
3462 # Ruby
3463 # https://www.ruby-lang.org/
3464 --type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec
3465 --type-add=ruby:is:Rakefile
3466 --type-add=ruby:firstlinematch:/^#!.*\bruby/
3468 # Rust
3469 # https://www.rust-lang.org/
3470 --type-add=rust:ext:rs
3472 # Sass
3473 # https://sass-lang.com
3474 --type-add=sass:ext:sass,scss
3476 # Scala
3477 # https://www.scala-lang.org/
3478 --type-add=scala:ext:scala
3480 # Scheme
3481 # https://groups.csail.mit.edu/mac/projects/scheme/
3482 --type-add=scheme:ext:scm,ss
3484 # Shell
3485 --type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish
3486 --type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/
3488 # Smalltalk
3489 # http://www.smalltalk.org/
3490 --type-add=smalltalk:ext:st
3492 # Smarty
3493 # https://www.smarty.net/
3494 --type-add=smarty:ext:tpl
3496 # SQL
3497 # https://www.iso.org/standard/45498.html
3498 --type-add=sql:ext:sql,ctl
3500 # Stylus
3501 # http://stylus-lang.com/
3502 --type-add=stylus:ext:styl
3504 # SVG
3505 # https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
3506 --type-add=svg:ext:svg
3508 # Swift
3509 # https://developer.apple.com/swift/
3510 --type-add=swift:ext:swift
3511 --type-add=swift:firstlinematch:/^#!.*\bswift/
3513 # Tcl
3514 # https://www.tcl.tk/
3515 --type-add=tcl:ext:tcl,itcl,itk
3517 # TeX & LaTeX
3518 # https://www.latex-project.org/
3519 --type-add=tex:ext:tex,cls,sty
3521 # Template Toolkit (Perl)
3522 # http//template-toolkit.org/
3523 --type-add=ttml:ext:tt,tt2,ttml
3525 # Typescript
3526 # https://www.typescriptlang.org/
3527 --type-add=ts:ext:ts,tsx
3529 # Visual Basic
3530 --type-add=vb:ext:bas,cls,frm,ctl,vb,resx
3532 # Verilog
3533 --type-add=verilog:ext:v,vh,sv
3535 # VHDL
3536 # http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome
3537 --type-add=vhdl:ext:vhd,vhdl
3539 # Vim
3540 # https://www.vim.org/
3541 --type-add=vim:ext:vim
3543 # XML
3544 # https://www.w3.org/TR/REC-xml/
3545 --type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl
3546 --type-add=xml:firstlinematch:/<[?]xml/
3548 # YAML
3549 # https://yaml.org/
3550 --type-add=yaml:ext:yaml,yml
3551 HERE
3552 $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;
3554 return $lines;
3558 package App::Ack::ConfigFinder;
3561 use strict;
3562 use warnings;
3564 use Cwd 3.00 ();
3565 use File::Spec 3.00 ();
3567 use if ($^O eq 'MSWin32'), 'Win32';
3570 sub new {
3571 my ( $class ) = @_;
3573 return bless {}, $class;
3577 sub _remove_redundancies {
3578 my @configs = @_;
3580 my %seen;
3581 my @uniq;
3582 foreach my $config (@configs) {
3583 my $path = $config->{path};
3584 my $key = -e $path ? Cwd::realpath( $path ) : $path;
3585 if ( not $App::Ack::is_windows ) {
3586 # On Unix, uniquify on inode.
3587 my ($dev, $inode) = (stat $key)[0, 1];
3588 $key = "$dev:$inode" if defined $dev;
3590 push( @uniq, $config ) unless $seen{$key}++;
3592 return @uniq;
3596 sub _check_for_ackrc {
3597 return unless defined $_[0];
3599 my @files = grep { -f }
3600 map { File::Spec->catfile(@_, $_) }
3601 qw(.ackrc _ackrc);
3603 App::Ack::die( File::Spec->catdir(@_) . ' contains both .ackrc and _ackrc. Please remove one of those files.' )
3604 if @files > 1;
3606 return wantarray ? @files : $files[0];
3607 } # end _check_for_ackrc
3611 sub find_config_files {
3612 my @config_files;
3614 if ( $App::Ack::is_windows ) {
3615 push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (
3616 Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),
3617 Win32::GetFolderPath(Win32::CSIDL_APPDATA()),
3620 else {
3621 push @config_files, { path => '/etc/ackrc' };
3625 if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {
3626 push @config_files, { path => $ENV{'ACKRC'} };
3628 else {
3629 push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});
3632 my $cwd = Cwd::getcwd();
3633 return () unless defined $cwd;
3635 # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this.
3636 $cwd =~ /(.+)/;
3637 $cwd = $1;
3638 my @dirs = File::Spec->splitdir( $cwd );
3639 while ( @dirs ) {
3640 my $ackrc = _check_for_ackrc(@dirs);
3641 if ( defined $ackrc ) {
3642 push @config_files, { project => 1, path => $ackrc };
3643 last;
3645 pop @dirs;
3648 # We only test for existence here, so if the file is deleted out from under us, this will fail later.
3649 return _remove_redundancies( @config_files );
3653 package App::Ack::ConfigLoader;
3655 use strict;
3656 use warnings;
3657 use 5.010;
3659 use File::Spec 3.00 ();
3660 use Getopt::Long 2.38 ();
3661 use Text::ParseWords 3.1 ();
3663 sub opt_parser {
3664 my @opts = @_;
3666 my @standard = qw(
3667 default
3668 bundling
3669 no_auto_help
3670 no_auto_version
3671 no_ignore_case
3673 return Getopt::Long::Parser->new( config => [ @standard, @opts ] );
3676 sub _generate_ignore_dir {
3677 my ( $option_name, $opt ) = @_;
3679 my $is_inverted = $option_name =~ /^--no/;
3681 return sub {
3682 my ( undef, $dir ) = @_;
3684 $dir = _remove_directory_separator( $dir );
3685 if ( $dir !~ /:/ ) {
3686 $dir = 'is:' . $dir;
3689 my ( $filter_type, $args ) = split /:/, $dir, 2;
3691 if ( $filter_type eq 'firstlinematch' ) {
3692 App::Ack::die( qq{Invalid filter specification "$filter_type" for option '$option_name'} );
3695 my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));
3696 my $collection;
3698 my $previous_inversion_matches = $opt->{idirs} && !($is_inverted xor $opt->{idirs}[-1]->is_inverted());
3700 if ( $previous_inversion_matches ) {
3701 $collection = $opt->{idirs}[-1];
3703 if ( $is_inverted ) {
3704 # This relies on invert of an inverted filter to return the original.
3705 $collection = $collection->invert();
3708 else {
3709 $collection = App::Ack::Filter::Collection->new();
3710 push @{ $opt->{idirs} }, $is_inverted ? $collection->invert() : $collection;
3713 $collection->add($filter);
3715 if ( $filter_type eq 'is' ) {
3716 $collection->add(App::Ack::Filter::IsPath->new($args));
3722 sub _remove_directory_separator {
3723 my $path = shift;
3725 state $dir_sep_chars = $App::Ack::is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
3727 $path =~ s/[$dir_sep_chars]$//;
3729 return $path;
3733 sub _process_filter_spec {
3734 my ( $spec ) = @_;
3736 if ( $spec =~ /^(\w+):(\w+):(.*)/ ) {
3737 my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 );
3739 return ( $type_name,
3740 App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) );
3742 elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification.
3743 my ( $type_name, $extensions ) = ( $1, $2 );
3745 my @extensions = split(/,/, $extensions);
3746 foreach my $extension ( @extensions ) {
3747 $extension =~ s/^[.]//;
3750 return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) );
3752 else {
3753 App::Ack::die( "Invalid filter specification '$spec'" );
3758 sub _uninvert_filter {
3759 my ( $opt, @filters ) = @_;
3761 return unless defined $opt->{filters} && @filters;
3763 # Loop through all the registered filters. If we hit one that
3764 # matches this extension and it's inverted, we need to delete it from
3765 # the options.
3766 for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) {
3767 my $opt_filter = @{ $opt->{filters} }[$i];
3769 # XXX Do a real list comparison? This just checks string equivalence.
3770 if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) {
3771 splice @{ $opt->{filters} }, $i, 1;
3772 $i--;
3776 return;
3780 sub _process_filetypes {
3781 my ( $opt, $arg_sources ) = @_;
3783 my %additional_specs;
3785 my $add_spec = sub {
3786 my ( undef, $spec ) = @_;
3788 my ( $name, $filter ) = _process_filter_spec($spec);
3790 push @{ $App::Ack::mappings{$name} }, $filter;
3792 $additional_specs{$name . '!'} = sub {
3793 my ( undef, $value ) = @_;
3795 my @filters = @{ $App::Ack::mappings{$name} };
3796 if ( not $value ) {
3797 @filters = map { $_->invert() } @filters;
3799 else {
3800 _uninvert_filter( $opt, @filters );
3803 push @{ $opt->{'filters'} }, @filters;
3807 my $set_spec = sub {
3808 my ( undef, $spec ) = @_;
3810 my ( $name, $filter ) = _process_filter_spec($spec);
3812 $App::Ack::mappings{$name} = [ $filter ];
3814 $additional_specs{$name . '!'} = sub {
3815 my ( undef, $value ) = @_;
3817 my @filters = @{ $App::Ack::mappings{$name} };
3818 if ( not $value ) {
3819 @filters = map { $_->invert() } @filters;
3822 push @{ $opt->{'filters'} }, @filters;
3826 my $delete_spec = sub {
3827 my ( undef, $name ) = @_;
3829 delete $App::Ack::mappings{$name};
3830 delete $additional_specs{$name . '!'};
3833 my %type_arg_specs = (
3834 'type-add=s' => $add_spec,
3835 'type-set=s' => $set_spec,
3836 'type-del=s' => $delete_spec,
3839 my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );
3840 foreach my $source (@{$arg_sources}) {
3841 my $args = $source->{contents};
3843 if ( ref($args) ) {
3844 # $args are modified in place, so no need to munge $arg_sources
3845 $p->getoptionsfromarray( $args, %type_arg_specs );
3847 else {
3848 ( undef, $source->{contents} ) =
3849 $p->getoptionsfromstring( $args, %type_arg_specs );
3853 $additional_specs{'k|known-types'} = sub {
3854 my @filters = map { @{$_} } values(%App::Ack::mappings);
3856 push @{ $opt->{'filters'} }, @filters;
3859 return \%additional_specs;
3863 sub get_arg_spec {
3864 my ( $opt, $extra_specs ) = @_;
3867 sub _type_handler {
3868 my ( $getopt, $value ) = @_;
3870 my $cb_value = 1;
3871 if ( $value =~ s/^no// ) {
3872 $cb_value = 0;
3875 my $callback;
3877 no warnings;
3878 $callback = $extra_specs->{ $value . '!' };
3881 if ( $callback ) {
3882 $callback->( $getopt, $cb_value );
3884 else {
3885 App::Ack::die( "Unknown type '$value'" );
3888 return;
3891 return {
3892 1 => sub { $opt->{1} = $opt->{m} = 1 },
3893 'A|after-context:-1' => sub { shift; $opt->{A} = _context_value(shift) },
3894 'B|before-context:-1' => sub { shift; $opt->{B} = _context_value(shift) },
3895 'C|context:-1' => sub { shift; $opt->{B} = $opt->{A} = _context_value(shift) },
3896 'break!' => \$opt->{break},
3897 'c|count' => \$opt->{c},
3898 'color|colour!' => \$opt->{color},
3899 'color-match=s' => \$ENV{ACK_COLOR_MATCH},
3900 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},
3901 'color-colno=s' => \$ENV{ACK_COLOR_COLNO},
3902 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},
3903 'column!' => \$opt->{column},
3904 'create-ackrc' => sub { say for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; },
3905 'debug' => \$opt->{debug},
3906 'env!' => sub {
3907 my ( undef, $value ) = @_;
3909 if ( !$value ) {
3910 $opt->{noenv_seen} = 1;
3913 f => \$opt->{f},
3914 'files-from=s' => \$opt->{files_from},
3915 'filter!' => \$App::Ack::is_filter_mode,
3916 flush => sub { $| = 1 },
3917 'follow!' => \$opt->{follow},
3918 g => \$opt->{g},
3919 'group!' => sub { shift; $opt->{heading} = $opt->{break} = shift },
3920 'heading!' => \$opt->{heading},
3921 'h|no-filename' => \$opt->{h},
3922 'H|with-filename' => \$opt->{H},
3923 'i|ignore-case' => sub { $opt->{i} = 1; $opt->{S} = 0; },
3924 'I|no-ignore-case' => sub { $opt->{i} = 0; $opt->{S} = 0; },
3925 'ignore-directory|ignore-dir=s' => _generate_ignore_dir('--ignore-dir', $opt),
3926 'ignore-file=s' => sub {
3927 my ( undef, $file ) = @_;
3929 my ( $filter_type, $args ) = split /:/, $file, 2;
3931 my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args//''));
3933 if ( !$opt->{ifiles} ) {
3934 $opt->{ifiles} = App::Ack::Filter::Collection->new();
3936 $opt->{ifiles}->add($filter);
3938 'l|files-with-matches'
3939 => \$opt->{l},
3940 'L|files-without-matches'
3941 => \$opt->{L},
3942 'm|max-count=i' => \$opt->{m},
3943 'match=s' => \$opt->{regex},
3944 'n|no-recurse' => \$opt->{n},
3945 o => sub { $opt->{output} = '$&' },
3946 'output=s' => \$opt->{output},
3947 'pager:s' => sub {
3948 my ( undef, $value ) = @_;
3950 $opt->{pager} = $value || $ENV{PAGER};
3952 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt),
3953 'nopager' => sub { $opt->{pager} = undef },
3954 'passthru' => \$opt->{passthru},
3955 'print0' => \$opt->{print0},
3956 'p|proximate:1' => \$opt->{p},
3957 'P' => sub { $opt->{p} = 0 },
3958 'Q|literal' => \$opt->{Q},
3959 'r|R|recurse' => sub { $opt->{n} = 0 },
3960 'range-start=s' => \$opt->{range_start},
3961 'range-end=s' => \$opt->{range_end},
3962 'range-invert!' => \$opt->{range_invert},
3963 's' => \$opt->{s},
3964 'show-types' => \$opt->{show_types},
3965 'S|smart-case!' => sub { my (undef,$value) = @_; $opt->{S} = $value; $opt->{i} = 0 if $value; },
3966 'sort-files' => \$opt->{sort_files},
3967 't|type=s' => \&_type_handler,
3968 'T=s' => sub { my ($getopt,$value) = @_; $value="no$value"; _type_handler($getopt,$value); },
3969 'underline!' => \$opt->{underline},
3970 'v|invert-match' => \$opt->{v},
3971 'w|word-regexp' => \$opt->{w},
3972 'x' => sub { $opt->{files_from} = '-' },
3974 'help' => sub { App::Ack::show_help(); exit; },
3975 'help-types' => sub { App::Ack::show_help_types(); exit; },
3976 'help-colors' => sub { App::Ack::show_help_colors(); exit; },
3977 'help-rgb-colors' => sub { App::Ack::show_help_rgb(); exit; },
3978 $extra_specs ? %{$extra_specs} : (),
3979 }; # arg_specs
3983 sub _context_value {
3984 my $val = shift;
3986 # Contexts default to 2.
3987 return (!defined($val) || ($val < 0)) ? 2 : $val;
3991 sub _process_other {
3992 my ( $opt, $extra_specs, $arg_sources ) = @_;
3994 my $argv_source;
3995 my $is_help_types_active;
3997 foreach my $source (@{$arg_sources}) {
3998 if ( $source->{name} eq 'ARGV' ) {
3999 $argv_source = $source->{contents};
4000 last;
4004 if ( $argv_source ) { # This *should* always be true, but you never know...
4005 my $p = opt_parser( 'pass_through' );
4006 $p->getoptionsfromarray( [ @{$argv_source} ],
4007 'help-types' => \$is_help_types_active,
4011 my $arg_specs = get_arg_spec( $opt, $extra_specs );
4013 my $p = opt_parser();
4014 foreach my $source (@{$arg_sources}) {
4015 my ( $source_name, $args ) = @{$source}{qw/name contents/};
4017 my $args_for_source = { %{$arg_specs} };
4019 if ( $source->{is_ackrc} ) {
4020 my $illegal = sub {
4021 my $name = shift;
4022 App::Ack::die( "Option --$name is forbidden in .ackrc files." );
4025 $args_for_source = {
4026 %{$args_for_source},
4027 'output=s' => $illegal,
4028 'match=s' => $illegal,
4031 if ( $source->{project} ) {
4032 my $illegal = sub {
4033 my $name = shift;
4034 App::Ack::die( "Option --$name is forbidden in project .ackrc files." );
4037 $args_for_source = {
4038 %{$args_for_source},
4039 'pager:s' => $illegal,
4043 my $ret;
4044 if ( ref($args) ) {
4045 $ret = $p->getoptionsfromarray( $args, %{$args_for_source} );
4047 else {
4048 ( $ret, $source->{contents} ) =
4049 $p->getoptionsfromstring( $args, %{$args_for_source} );
4051 if ( !$ret ) {
4052 if ( !$is_help_types_active ) {
4053 my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name";
4054 App::Ack::die( "Invalid option $where" );
4057 if ( $opt->{noenv_seen} ) {
4058 App::Ack::die( "--noenv found in $source_name" );
4062 # XXX We need to check on a -- in the middle of a non-ARGV source
4064 return;
4068 sub _explode_sources {
4069 my ( $sources ) = @_;
4071 my @new_sources;
4073 my %opt;
4074 my $arg_spec = get_arg_spec( \%opt, {} );
4076 my $dummy_sub = sub {};
4077 my $add_type = sub {
4078 my ( undef, $arg ) = @_;
4080 if ( $arg =~ /(\w+)=/) {
4081 $arg_spec->{$1} = $dummy_sub;
4083 else {
4084 ( $arg ) = split /:/, $arg;
4085 $arg_spec->{$arg} = $dummy_sub;
4089 my $del_type = sub {
4090 my ( undef, $arg ) = @_;
4092 delete $arg_spec->{$arg};
4095 my $p = opt_parser( 'pass_through' );
4096 foreach my $source (@{$sources}) {
4097 my ( $name, $options ) = @{$source}{qw/name contents/};
4098 if ( ref($options) ne 'ARRAY' ) {
4099 $source->{contents} = $options =
4100 [ Text::ParseWords::shellwords($options) ];
4103 for my $j ( 0 .. @{$options}-1 ) {
4104 next unless $options->[$j] =~ /^-/;
4105 my @chunk = ( $options->[$j] );
4106 push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/;
4107 $j--;
4109 my @copy = @chunk;
4110 $p->getoptionsfromarray( [@chunk],
4111 'type-add=s' => $add_type,
4112 'type-set=s' => $add_type,
4113 'type-del=s' => $del_type,
4114 %{$arg_spec}
4117 push @new_sources, {
4118 name => $name,
4119 contents => \@copy,
4124 return \@new_sources;
4128 sub _compare_opts {
4129 my ( $a, $b ) = @_;
4131 my $first_a = $a->[0];
4132 my $first_b = $b->[0];
4134 $first_a =~ s/^--?//;
4135 $first_b =~ s/^--?//;
4137 return $first_a cmp $first_b;
4141 sub _dump_options {
4142 my ( $sources ) = @_;
4144 $sources = _explode_sources($sources);
4146 my %opts_by_source;
4147 my @source_names;
4149 foreach my $source (@{$sources}) {
4150 my $name = $source->{name};
4151 if ( not $opts_by_source{$name} ) {
4152 $opts_by_source{$name} = [];
4153 push @source_names, $name;
4155 push @{$opts_by_source{$name}}, $source->{contents};
4158 foreach my $name (@source_names) {
4159 my $contents = $opts_by_source{$name};
4161 say $name;
4162 say '=' x length($name);
4163 say ' ', join(' ', @{$_}) for sort { _compare_opts($a, $b) } @{$contents};
4166 return;
4170 sub _remove_default_options_if_needed {
4171 my ( $sources ) = @_;
4173 my $default_index;
4175 foreach my $index ( 0 .. $#{$sources} ) {
4176 if ( $sources->[$index]{'name'} eq 'Defaults' ) {
4177 $default_index = $index;
4178 last;
4182 return $sources unless defined $default_index;
4184 my $should_remove = 0;
4186 my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );
4188 foreach my $index ( $default_index + 1 .. $#{$sources} ) {
4189 my $args = $sources->[$index]->{contents};
4191 if (ref($args)) {
4192 $p->getoptionsfromarray( $args,
4193 'ignore-ack-defaults' => \$should_remove,
4196 else {
4197 ( undef, $sources->[$index]{contents} ) = $p->getoptionsfromstring( $args,
4198 'ignore-ack-defaults' => \$should_remove,
4203 return $sources unless $should_remove;
4205 my @copy = @{$sources};
4206 splice @copy, $default_index, 1;
4207 return \@copy;
4211 sub process_args {
4212 my $arg_sources = \@_;
4214 my %opt = (
4215 pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
4218 $arg_sources = _remove_default_options_if_needed($arg_sources);
4220 # Check for --dump early.
4221 foreach my $source (@{$arg_sources}) {
4222 if ( $source->{name} eq 'ARGV' ) {
4223 my $dump;
4224 my $p = opt_parser( 'pass_through' );
4225 $p->getoptionsfromarray( $source->{contents},
4226 'dump' => \$dump,
4228 if ( $dump ) {
4229 _dump_options($arg_sources);
4230 exit(0);
4235 my $type_specs = _process_filetypes(\%opt, $arg_sources);
4237 _check_for_mutually_exclusive_options( $type_specs );
4239 _process_other(\%opt, $type_specs, $arg_sources);
4240 while ( @{$arg_sources} ) {
4241 my $source = shift @{$arg_sources};
4242 my $args = $source->{contents};
4244 # All of our sources should be transformed into an array ref
4245 if ( ref($args) ) {
4246 my $source_name = $source->{name};
4247 if ( $source_name eq 'ARGV' ) {
4248 @ARGV = @{$args};
4250 elsif (@{$args}) {
4251 App::Ack::die( "Source '$source_name' has extra arguments!" );
4254 else {
4255 App::Ack::die( 'The impossible has occurred!' );
4258 my $filters = ($opt{filters} ||= []);
4260 # Throw the default filter in if no others are selected.
4261 if ( not grep { !$_->is_inverted() } @{$filters} ) {
4262 push @{$filters}, App::Ack::Filter::Default->new();
4264 return \%opt;
4268 sub retrieve_arg_sources {
4269 my @arg_sources;
4271 my $noenv;
4272 my $ackrc;
4274 my $p = opt_parser( 'no_auto_abbrev', 'pass_through' );
4275 $p->getoptions(
4276 'noenv' => \$noenv,
4277 'ackrc=s' => \$ackrc,
4280 my @files;
4282 if ( !$noenv ) {
4283 my $finder = App::Ack::ConfigFinder->new;
4284 @files = $finder->find_config_files;
4286 if ( $ackrc ) {
4287 # We explicitly use open so we get a nice error message.
4288 # XXX This is a potential race condition!.
4289 if ( open my $fh, '<', $ackrc ) {
4290 close $fh;
4292 else {
4293 App::Ack::die( "Unable to load ackrc '$ackrc': $!" );
4295 push( @files, { path => $ackrc } );
4298 push @arg_sources, {
4299 name => 'Defaults',
4300 contents => [ App::Ack::ConfigDefault::options_clean() ],
4303 foreach my $file ( @files) {
4304 my @lines = read_rcfile($file->{path});
4305 if ( @lines ) {
4306 push @arg_sources, {
4307 name => $file->{path},
4308 contents => \@lines,
4309 project => $file->{project},
4310 is_ackrc => 1,
4315 push @arg_sources, {
4316 name => 'ARGV',
4317 contents => [ @ARGV ],
4320 return @arg_sources;
4324 sub read_rcfile {
4325 my $file = shift;
4327 return unless defined $file && -e $file;
4329 my @lines;
4331 open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" );
4332 while ( defined( my $line = <$fh> ) ) {
4333 chomp $line;
4334 $line =~ s/^\s+//;
4335 $line =~ s/\s+$//;
4337 next if $line eq '';
4338 next if $line =~ /^\s*#/;
4340 push( @lines, $line );
4342 close $fh or App::Ack::die( "Unable to close $file: $!" );
4344 return @lines;
4348 # Verifies no mutually-exclusive options were passed. Dies if they were.
4349 sub _check_for_mutually_exclusive_options {
4350 my $type_specs = shift;
4352 my $mutex = mutex_options();
4354 my ($raw,$used) = _options_used( $type_specs );
4356 my @used = sort { lc $a cmp lc $b } keys %{$used};
4358 for my $i ( @used ) {
4359 for my $j ( @used ) {
4360 next if $i eq $j;
4361 if ( $mutex->{$i}{$j} ) {
4362 my $x = $raw->[ $used->{$i} ];
4363 my $y = $raw->[ $used->{$j} ];
4364 App::Ack::die( "Options '$x' and '$y' are mutually exclusive" );
4369 return;
4373 # Processes the command line option and returns a hash of the options that were
4374 # used on the command line, using their full name. "--prox" shows up in the hash as "--proximate".
4375 sub _options_used {
4376 my $type_specs = shift;
4378 my %dummy_opt;
4379 my $real_spec = get_arg_spec( \%dummy_opt, $type_specs );
4381 # The real argument parsing doesn't check for --type-add, --type-del or --type-set because
4382 # they get removed by the argument processing. We have to account for them here.
4383 my $sub_dummy = sub {};
4384 $real_spec = {
4385 %{$real_spec},
4386 'type-add=s' => $sub_dummy,
4387 'type-del=s' => $sub_dummy,
4388 'type-set=s' => $sub_dummy,
4389 'ignore-ack-defaults' => $sub_dummy,
4392 my %parsed;
4393 my @raw;
4394 my %spec_capture_parsed;
4395 my %spec_capture_raw;
4398 # Capture the %parsed hash.
4399 CAPTURE_PARSED: {
4400 my $parsed_pos = 0;
4401 my $sub_count = sub {
4402 my $arg = shift;
4403 $arg = "$arg";
4404 $parsed{$arg} = $parsed_pos++;
4406 %spec_capture_parsed = (
4407 '<>' => sub { $parsed_pos++ }, # Bump forward one pos for non-options.
4408 map { $_ => $sub_count } keys %{$real_spec}
4412 # Capture the @raw array.
4413 CAPTURE_RAW: {
4414 my $raw_pos = 0;
4415 %spec_capture_raw = (
4416 '<>' => sub { $raw_pos++ }, # Bump forward one pos for non-options.
4419 my $sub_count = sub {
4420 my $arg = shift;
4422 $arg = "$arg";
4423 $raw[$raw_pos] = length($arg) == 1 ? "-$arg" : "--$arg";
4424 $raw_pos++;
4427 for my $opt_spec ( keys %{$real_spec} ) {
4428 my $negatable;
4429 my $type;
4430 my $default;
4432 $negatable = ($opt_spec =~ s/!$//);
4434 if ( $opt_spec =~ s/(=[si])$// ) {
4435 $type = $1;
4437 if ( $opt_spec =~ s/(:.+)$// ) {
4438 $default = $1;
4441 my @aliases = split( /\|/, $opt_spec );
4442 for my $alias ( @aliases ) {
4443 $alias .= $type if defined $type;
4444 $alias .= $default if defined $default;
4445 $alias .= '!' if $negatable;
4447 $spec_capture_raw{$alias} = $sub_count;
4452 # Parse @ARGV twice, once with each capture spec.
4453 my $p = opt_parser( 'pass_through' ); # Ignore invalid options.
4454 $p->getoptionsfromarray( [@ARGV], %spec_capture_raw );
4455 $p->getoptionsfromarray( [@ARGV], %spec_capture_parsed );
4457 return (\@raw,\%parsed);
4461 sub mutex_options {
4462 # This list is machine-generated by crank-mutex. Do not modify it by hand.
4464 return {
4465 1 => {
4466 m => 1,
4467 passthru => 1,
4469 A => {
4470 L => 1,
4471 c => 1,
4472 f => 1,
4473 g => 1,
4474 l => 1,
4475 o => 1,
4476 output => 1,
4477 p => 1,
4478 passthru => 1,
4480 B => {
4481 L => 1,
4482 c => 1,
4483 f => 1,
4484 g => 1,
4485 l => 1,
4486 o => 1,
4487 output => 1,
4488 p => 1,
4489 passthru => 1,
4491 C => {
4492 L => 1,
4493 c => 1,
4494 f => 1,
4495 g => 1,
4496 l => 1,
4497 o => 1,
4498 output => 1,
4499 p => 1,
4500 passthru => 1,
4502 H => {
4503 L => 1,
4504 f => 1,
4505 g => 1,
4506 l => 1,
4508 L => {
4509 A => 1,
4510 B => 1,
4511 C => 1,
4512 H => 1,
4513 L => 1,
4514 break => 1,
4515 c => 1,
4516 column => 1,
4517 f => 1,
4518 g => 1,
4519 group => 1,
4520 h => 1,
4521 heading => 1,
4522 l => 1,
4523 'no-filename' => 1,
4524 o => 1,
4525 output => 1,
4526 p => 1,
4527 passthru => 1,
4528 'show-types' => 1,
4529 v => 1,
4530 'with-filename' => 1,
4532 break => {
4533 L => 1,
4534 c => 1,
4535 f => 1,
4536 g => 1,
4537 l => 1,
4539 c => {
4540 A => 1,
4541 B => 1,
4542 C => 1,
4543 L => 1,
4544 break => 1,
4545 column => 1,
4546 f => 1,
4547 g => 1,
4548 group => 1,
4549 heading => 1,
4550 m => 1,
4551 o => 1,
4552 output => 1,
4553 p => 1,
4554 passthru => 1,
4556 column => {
4557 L => 1,
4558 c => 1,
4559 f => 1,
4560 g => 1,
4561 l => 1,
4562 o => 1,
4563 output => 1,
4564 passthru => 1,
4565 v => 1,
4567 f => {
4568 A => 1,
4569 B => 1,
4570 C => 1,
4571 H => 1,
4572 L => 1,
4573 break => 1,
4574 c => 1,
4575 column => 1,
4576 f => 1,
4577 g => 1,
4578 group => 1,
4579 h => 1,
4580 heading => 1,
4581 l => 1,
4582 m => 1,
4583 match => 1,
4584 o => 1,
4585 output => 1,
4586 p => 1,
4587 passthru => 1,
4588 u => 1,
4589 v => 1,
4590 x => 1,
4592 g => {
4593 A => 1,
4594 B => 1,
4595 C => 1,
4596 H => 1,
4597 L => 1,
4598 break => 1,
4599 c => 1,
4600 column => 1,
4601 f => 1,
4602 g => 1,
4603 group => 1,
4604 h => 1,
4605 heading => 1,
4606 l => 1,
4607 m => 1,
4608 match => 1,
4609 o => 1,
4610 output => 1,
4611 p => 1,
4612 passthru => 1,
4613 u => 1,
4614 x => 1,
4616 group => {
4617 L => 1,
4618 c => 1,
4619 f => 1,
4620 g => 1,
4621 l => 1,
4623 h => {
4624 L => 1,
4625 f => 1,
4626 g => 1,
4627 l => 1,
4629 heading => {
4630 L => 1,
4631 c => 1,
4632 f => 1,
4633 g => 1,
4634 l => 1,
4636 l => {
4637 A => 1,
4638 B => 1,
4639 C => 1,
4640 H => 1,
4641 L => 1,
4642 break => 1,
4643 column => 1,
4644 f => 1,
4645 g => 1,
4646 group => 1,
4647 h => 1,
4648 heading => 1,
4649 l => 1,
4650 'no-filename' => 1,
4651 o => 1,
4652 output => 1,
4653 p => 1,
4654 passthru => 1,
4655 'show-types' => 1,
4656 'with-filename' => 1,
4658 m => {
4659 1 => 1,
4660 c => 1,
4661 f => 1,
4662 g => 1,
4663 passthru => 1,
4665 match => {
4666 f => 1,
4667 g => 1,
4669 'no-filename' => {
4670 L => 1,
4671 l => 1,
4673 o => {
4674 A => 1,
4675 B => 1,
4676 C => 1,
4677 L => 1,
4678 c => 1,
4679 column => 1,
4680 f => 1,
4681 g => 1,
4682 l => 1,
4683 o => 1,
4684 output => 1,
4685 p => 1,
4686 passthru => 1,
4687 'show-types' => 1,
4689 output => {
4690 A => 1,
4691 B => 1,
4692 C => 1,
4693 L => 1,
4694 c => 1,
4695 column => 1,
4696 f => 1,
4697 g => 1,
4698 l => 1,
4699 o => 1,
4700 output => 1,
4701 p => 1,
4702 passthru => 1,
4703 'show-types' => 1,
4704 u => 1,
4706 p => {
4707 A => 1,
4708 B => 1,
4709 C => 1,
4710 L => 1,
4711 c => 1,
4712 f => 1,
4713 g => 1,
4714 l => 1,
4715 o => 1,
4716 output => 1,
4717 p => 1,
4718 passthru => 1,
4720 passthru => {
4721 1 => 1,
4722 A => 1,
4723 B => 1,
4724 C => 1,
4725 L => 1,
4726 c => 1,
4727 column => 1,
4728 f => 1,
4729 g => 1,
4730 l => 1,
4731 m => 1,
4732 o => 1,
4733 output => 1,
4734 p => 1,
4736 'show-types' => {
4737 L => 1,
4738 l => 1,
4739 o => 1,
4740 output => 1,
4742 u => {
4743 f => 1,
4744 g => 1,
4745 output => 1,
4747 v => {
4748 L => 1,
4749 column => 1,
4750 f => 1,
4752 'with-filename' => {
4753 L => 1,
4754 l => 1,
4756 x => {
4757 f => 1,
4758 g => 1,
4761 } # End of mutex_options()
4764 1; # End of App::Ack::ConfigLoader
4765 package App::Ack::File;
4767 use warnings;
4768 use strict;
4770 use File::Spec ();
4773 sub new {
4774 my $class = shift;
4775 my $filename = shift;
4777 my $self = bless {
4778 filename => $filename,
4779 fh => undef,
4780 }, $class;
4782 if ( $self->{filename} eq '-' ) {
4783 $self->{fh} = *STDIN;
4786 return $self;
4791 sub name {
4792 return $_[0]->{filename};
4797 sub basename {
4798 my ( $self ) = @_;
4800 return $self->{basename} //= (File::Spec->splitpath($self->name))[2];
4805 sub open {
4806 my ( $self ) = @_;
4808 if ( !$self->{fh} ) {
4809 if ( open $self->{fh}, '<', $self->{filename} ) {
4810 # Do nothing.
4812 else {
4813 $self->{fh} = undef;
4817 return $self->{fh};
4821 sub may_be_present {
4822 my $self = shift;
4823 my $regex = shift;
4825 # Tells if the file needs a line-by-line scan. This is a big
4826 # optimization because if you can tell from the outset that the pattern
4827 # is not found in the file at all, then there's no need to do the
4828 # line-by-line iteration.
4830 # Slurp up an entire file up to 10M, see if there are any matches
4831 # in it, and if so, let us know so we can iterate over it directly.
4833 # The $regex may be undef if it had a "$" in it, and is therefore unsuitable for this heuristic.
4835 my $may_be_present = 1;
4836 if ( $regex && $self->open() && -f $self->{fh} ) {
4837 my $buffer;
4838 my $size = 10_000_000;
4839 my $rc = sysread( $self->{fh}, $buffer, $size );
4840 if ( !defined($rc) ) {
4841 if ( $App::Ack::report_bad_filenames ) {
4842 App::Ack::warn( $self->name . ": $!" );
4844 $may_be_present = 0;
4846 else {
4847 # If we read all 10M, then we need to scan the rest.
4848 # If there are any carriage returns, our results are flaky, so scan the rest.
4849 if ( ($rc == $size) || (index($buffer,"\r") >= 0) ) {
4850 $may_be_present = 1;
4852 else {
4853 if ( $buffer !~ /$regex/o ) {
4854 $may_be_present = 0;
4860 return $may_be_present;
4865 sub reset {
4866 my $self = shift;
4868 if ( defined($self->{fh}) ) {
4869 return unless -f $self->{fh};
4871 if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
4872 App::Ack::warn( "$self->{filename}: $!" );
4876 return;
4881 sub close {
4882 my $self = shift;
4884 if ( $self->{fh} ) {
4885 if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
4886 App::Ack::warn( $self->name() . ": $!" );
4888 $self->{fh} = undef;
4891 return;
4896 sub clone {
4897 my ( $self ) = @_;
4899 return __PACKAGE__->new($self->name);
4904 sub firstliney {
4905 my ( $self ) = @_;
4907 if ( !exists $self->{firstliney} ) {
4908 my $fh = $self->open();
4909 if ( !$fh ) {
4910 if ( $App::Ack::report_bad_filenames ) {
4911 App::Ack::warn( $self->name . ': ' . $! );
4913 $self->{firstliney} = '';
4915 else {
4916 my $buffer;
4917 my $rc = sysread( $fh, $buffer, 250 );
4918 if ( $rc ) {
4919 $buffer =~ s/[\r\n].*//s;
4921 else {
4922 if ( !defined($rc) ) {
4923 App::Ack::warn( $self->name . ': ' . $! );
4925 $buffer = '';
4927 $self->{firstliney} = $buffer;
4928 $self->reset;
4932 return $self->{firstliney};
4936 package App::Ack::Files;
4940 use warnings;
4941 use strict;
4942 use 5.010;
4945 sub from_argv {
4946 my $class = shift;
4947 my $opt = shift;
4948 my $start = shift;
4950 my $self = bless {}, $class;
4952 my $descend_filter = $opt->{descend_filter};
4954 if ( $opt->{n} ) {
4955 $descend_filter = sub {
4956 return 0;
4960 $self->{iter} =
4961 File::Next::files( {
4962 file_filter => $opt->{file_filter},
4963 descend_filter => $descend_filter,
4964 error_handler => _generate_error_handler(),
4965 warning_handler => sub {},
4966 sort_files => $opt->{sort_files},
4967 follow_symlinks => $opt->{follow},
4968 }, @{$start} );
4970 return $self;
4974 sub from_file {
4975 my $class = shift;
4976 my $opt = shift;
4977 my $file = shift;
4979 my $error_handler = _generate_error_handler();
4980 my $iter =
4981 File::Next::from_file( {
4982 error_handler => $error_handler,
4983 warning_handler => $error_handler,
4984 sort_files => $opt->{sort_files},
4985 }, $file ) or return undef;
4987 return bless {
4988 iter => $iter,
4989 }, $class;
4995 sub from_stdin {
4996 my $class = shift;
4998 my $self = bless {}, $class;
5000 $self->{iter} = sub {
5001 state $has_been_called = 0;
5003 if ( !$has_been_called ) {
5004 $has_been_called = 1;
5005 return '-';
5007 return;
5010 return $self;
5014 sub next {
5015 my $self = shift;
5017 my $file = $self->{iter}->();
5019 return unless defined($file);
5021 return App::Ack::File->new( $file );
5025 sub _generate_error_handler {
5026 if ( $App::Ack::report_bad_filenames ) {
5027 return sub {
5028 my $msg = shift;
5029 App::Ack::warn( $msg );
5032 else {
5033 return sub {};
5038 package App::Ack::Filter;
5040 use strict;
5041 use warnings;
5044 my %filter_types;
5047 sub create_filter {
5048 my ( undef, $type, @args ) = @_;
5050 if ( my $package = $filter_types{$type} ) {
5051 return $package->new(@args);
5053 my $allowed_types = join( ', ', sort keys %filter_types );
5054 App::Ack::die( "Unknown filter type '$type'. Type must be one of: $allowed_types." );
5058 sub register_filter {
5059 my ( undef, $type, $package ) = @_;
5061 $filter_types{$type} = $package;
5063 return;
5067 sub invert {
5068 my ( $self ) = @_;
5070 return App::Ack::Filter::Inverse->new( $self );
5074 sub is_inverted {
5075 return 0;
5079 sub to_string {
5080 return '(unimplemented to_string)';
5084 sub inspect {
5085 my ( $self ) = @_;
5087 return ref($self);
5091 package App::Ack::Filter::Collection;
5094 use strict;
5095 use warnings;
5096 BEGIN {
5097 our @ISA = 'App::Ack::Filter';
5100 sub new {
5101 my ( $class ) = @_;
5103 return bless {
5104 groups => {},
5105 ungrouped => [],
5106 }, $class;
5109 sub filter {
5110 my ( $self, $file ) = @_;
5112 for my $group (values %{$self->{groups}}) {
5113 return 1 if $group->filter($file);
5116 for my $filter (@{$self->{ungrouped}}) {
5117 return 1 if $filter->filter($file);
5120 return 0;
5123 sub add {
5124 my ( $self, $filter ) = @_;
5126 if (exists $filter->{'groupname'}) {
5127 my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group());
5128 $group->add($filter);
5130 else {
5131 push @{$self->{'ungrouped'}}, $filter;
5134 return;
5137 sub inspect {
5138 my ( $self ) = @_;
5140 return ref($self) . " - $self";
5143 sub to_string {
5144 my ( $self ) = @_;
5146 return join(', ', map { "($_)" } @{$self->{ungrouped}});
5150 package App::Ack::Filter::Default;
5153 use strict;
5154 use warnings;
5155 BEGIN {
5156 our @ISA = 'App::Ack::Filter';
5159 sub new {
5160 my ( $class ) = @_;
5162 return bless {}, $class;
5165 sub filter {
5166 my ( undef, $file ) = @_;
5168 return -T $file->name;
5172 package App::Ack::Filter::Extension;
5175 use strict;
5176 use warnings;
5177 BEGIN {
5178 our @ISA = 'App::Ack::Filter';
5182 sub new {
5183 my ( $class, @extensions ) = @_;
5185 my $exts = join('|', map { "\Q$_\E"} @extensions);
5186 my $re = qr/[.](?:$exts)$/i;
5188 return bless {
5189 extensions => \@extensions,
5190 regex => $re,
5191 groupname => 'ExtensionGroup',
5192 }, $class;
5195 sub create_group {
5196 return App::Ack::Filter::ExtensionGroup->new();
5199 sub filter {
5200 my ( $self, $file ) = @_;
5202 return $file->name =~ /$self->{regex}/;
5205 sub inspect {
5206 my ( $self ) = @_;
5208 return ref($self) . ' - ' . $self->{regex};
5211 sub to_string {
5212 my ( $self ) = @_;
5214 return join( ' ', map { ".$_" } @{$self->{extensions}} );
5217 BEGIN {
5218 App::Ack::Filter->register_filter(ext => __PACKAGE__);
5222 package App::Ack::Filter::ExtensionGroup;
5225 use strict;
5226 use warnings;
5227 BEGIN {
5228 our @ISA = 'App::Ack::Filter';
5231 sub new {
5232 my ( $class ) = @_;
5234 return bless {
5235 data => {},
5236 }, $class;
5239 sub add {
5240 my ( $self, $filter ) = @_;
5242 foreach my $ext (@{$filter->{extensions}}) {
5243 $self->{data}->{lc $ext} = 1;
5246 return;
5249 sub filter {
5250 my ( $self, $file ) = @_;
5252 if ($file->name =~ /[.]([^.]*)$/) {
5253 return exists $self->{'data'}->{lc $1};
5256 return 0;
5259 sub inspect {
5260 my ( $self ) = @_;
5262 return ref($self) . " - $self";
5265 sub to_string {
5266 my ( $self ) = @_;
5268 return join(' ', map { ".$_" } sort keys %{$self->{data}});
5272 package App::Ack::Filter::FirstLineMatch;
5276 use strict;
5277 use warnings;
5278 BEGIN {
5279 our @ISA = 'App::Ack::Filter';
5282 sub new {
5283 my ( $class, $re ) = @_;
5285 $re =~ s{^/|/$}{}g; # XXX validate?
5286 $re = qr{$re}i;
5288 return bless {
5289 regex => $re,
5290 }, $class;
5293 # This test reads the first 250 characters of a file, then just uses the
5294 # first line found in that. This prevents reading something like an entire
5295 # .min.js file (which might be only one "line" long) into memory.
5297 sub filter {
5298 my ( $self, $file ) = @_;
5300 return $file->firstliney =~ /$self->{regex}/;
5303 sub inspect {
5304 my ( $self ) = @_;
5307 return ref($self) . ' - ' . $self->{regex};
5310 sub to_string {
5311 my ( $self ) = @_;
5313 (my $re = $self->{regex}) =~ s{\([^:]*:(.*)\)$}{$1};
5315 return "First line matches /$re/";
5318 BEGIN {
5319 App::Ack::Filter->register_filter(firstlinematch => __PACKAGE__);
5323 package App::Ack::Filter::Inverse;
5327 use strict;
5328 use warnings;
5329 BEGIN {
5330 our @ISA = 'App::Ack::Filter';
5333 sub new {
5334 my ( $class, $filter ) = @_;
5336 return bless {
5337 filter => $filter,
5338 }, $class;
5341 sub filter {
5342 my ( $self, $file ) = @_;
5344 return !$self->{filter}->filter( $file );
5347 sub invert {
5348 my $self = shift;
5350 return $self->{'filter'};
5353 sub is_inverted {
5354 return 1;
5357 sub inspect {
5358 my ( $self ) = @_;
5360 my $filter = $self->{'filter'};
5362 return "!$filter";
5366 package App::Ack::Filter::Is;
5369 use strict;
5370 use warnings;
5371 BEGIN {
5372 our @ISA = 'App::Ack::Filter';
5375 use File::Spec 3.00 ();
5377 sub new {
5378 my ( $class, $filename ) = @_;
5380 return bless {
5381 filename => $filename,
5382 groupname => 'IsGroup',
5383 }, $class;
5386 sub create_group {
5387 return App::Ack::Filter::IsGroup->new();
5390 sub filter {
5391 my ( $self, $file ) = @_;
5393 return (File::Spec->splitpath($file->name))[2] eq $self->{filename};
5396 sub inspect {
5397 my ( $self ) = @_;
5399 return ref($self) . ' - ' . $self->{filename};
5402 sub to_string {
5403 my ( $self ) = @_;
5405 return $self->{filename};
5408 BEGIN {
5409 App::Ack::Filter->register_filter(is => __PACKAGE__);
5413 package App::Ack::Filter::IsGroup;
5416 use strict;
5417 use warnings;
5418 BEGIN {
5419 our @ISA = 'App::Ack::Filter';
5422 sub new {
5423 my ( $class ) = @_;
5425 return bless {
5426 data => {},
5427 }, $class;
5430 sub add {
5431 my ( $self, $filter ) = @_;
5433 $self->{data}->{ $filter->{filename} } = 1;
5435 return;
5438 sub filter {
5439 my ( $self, $file ) = @_;
5441 return exists $self->{data}->{ $file->basename };
5444 sub inspect {
5445 my ( $self ) = @_;
5447 return ref($self) . " - $self";
5450 sub to_string {
5451 my ( $self ) = @_;
5453 return join(' ', keys %{$self->{data}});
5457 package App::Ack::Filter::IsPath;
5460 use strict;
5461 use warnings;
5462 BEGIN {
5463 our @ISA = 'App::Ack::Filter';
5467 sub new {
5468 my ( $class, $filename ) = @_;
5470 return bless {
5471 filename => $filename,
5472 groupname => 'IsPathGroup',
5473 }, $class;
5476 sub create_group {
5477 return App::Ack::Filter::IsPathGroup->new();
5480 sub filter {
5481 my ( $self, $file ) = @_;
5483 return $file->name eq $self->{filename};
5486 sub inspect {
5487 my ( $self ) = @_;
5489 return ref($self) . ' - ' . $self->{filename};
5492 sub to_string {
5493 my ( $self ) = @_;
5495 return $self->{filename};
5499 package App::Ack::Filter::IsPathGroup;
5502 use strict;
5503 use warnings;
5504 BEGIN {
5505 our @ISA = 'App::Ack::Filter';
5508 sub new {
5509 my ( $class ) = @_;
5511 return bless {
5512 data => {},
5513 }, $class;
5516 sub add {
5517 my ( $self, $filter ) = @_;
5519 $self->{data}->{ $filter->{filename} } = 1;
5521 return;
5524 sub filter {
5525 my ( $self, $file ) = @_;
5527 return exists $self->{data}->{$file->name};
5530 sub inspect {
5531 my ( $self ) = @_;
5533 return ref($self) . " - $self";
5536 sub to_string {
5537 my ( $self ) = @_;
5539 return join(' ', keys %{$self->{data}});
5543 package App::Ack::Filter::Match;
5545 use strict;
5546 use warnings;
5547 BEGIN {
5548 our @ISA = 'App::Ack::Filter';
5553 sub new {
5554 my ( $class, $re ) = @_;
5556 $re =~ s{^/|/$}{}g; # XXX validate?
5557 $re = qr/$re/i;
5559 return bless {
5560 regex => $re,
5561 groupname => 'MatchGroup',
5562 }, $class;
5565 sub create_group {
5566 return App::Ack::Filter::MatchGroup->new;
5569 sub filter {
5570 my ( $self, $file ) = @_;
5572 return $file->basename =~ /$self->{regex}/;
5575 sub inspect {
5576 my ( $self ) = @_;
5578 return ref($self) . ' - ' . $self->{regex};
5581 sub to_string {
5582 my ( $self ) = @_;
5584 return "Filename matches $self->{regex}";
5587 BEGIN {
5588 App::Ack::Filter->register_filter(match => __PACKAGE__);
5592 package App::Ack::Filter::MatchGroup;
5595 use strict;
5596 use warnings;
5597 BEGIN {
5598 our @ISA = 'App::Ack::Filter';
5601 sub new {
5602 my ( $class ) = @_;
5604 return bless {
5605 matches => [],
5606 big_re => undef,
5607 }, $class;
5610 sub add {
5611 my ( $self, $filter ) = @_;
5613 push @{ $self->{matches} }, $filter->{regex};
5615 my $re = join('|', map { "(?:$_)" } @{ $self->{matches} });
5616 $self->{big_re} = qr/$re/;
5618 return;
5621 sub filter {
5622 my ( $self, $file ) = @_;
5624 return $file->basename =~ /$self->{big_re}/;
5627 # This class has no inspect() or to_string() method.
5628 # It will just use the default one unless someone writes something useful.
5631 package File::Next;
5633 use strict;
5634 use warnings;
5637 our $VERSION = '1.18';
5641 use File::Spec ();
5643 our $name; # name of the current file
5644 our $dir; # dir of the current file
5646 our %files_defaults;
5647 our %skip_dirs;
5649 BEGIN {
5650 %files_defaults = (
5651 file_filter => undef,
5652 descend_filter => undef,
5653 error_handler => sub { CORE::die $_[0] },
5654 warning_handler => sub { CORE::warn @_ },
5655 sort_files => undef,
5656 follow_symlinks => 1,
5657 nul_separated => 0,
5659 %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
5663 sub files {
5664 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
5666 my ($parms,@queue) = _setup( \%files_defaults, @_ );
5668 my $filter = $parms->{file_filter};
5669 return sub {
5670 while ( my $entry = shift @queue ) {
5671 my ( $dirname, $file, $fullpath, $is_dir, $is_file, $is_fifo ) = @{$entry};
5672 if ( $is_file || $is_fifo ) {
5673 if ( $filter ) {
5674 local $_ = $file;
5675 local $File::Next::dir = $dirname;
5676 local $File::Next::name = $fullpath;
5677 next if not $filter->();
5679 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
5681 if ( $is_dir ) {
5682 unshift( @queue, _candidate_files( $parms, $fullpath ) );
5684 } # while
5686 return;
5687 }; # iterator
5696 sub from_file {
5697 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
5699 my ($parms,@queue) = _setup( \%files_defaults, @_ );
5700 my $err = $parms->{error_handler};
5701 my $warn = $parms->{warning_handler};
5703 my $filename = $queue[0]->[1];
5705 if ( !defined($filename) ) {
5706 $err->( 'Must pass a filename to from_file()' );
5707 return undef;
5710 my $fh;
5711 if ( $filename eq '-' ) {
5712 $fh = \*STDIN;
5714 else {
5715 if ( !open( $fh, '<', $filename ) ) {
5716 $err->( "Unable to open $filename: $!", $! + 0 );
5717 return undef;
5721 my $filter = $parms->{file_filter};
5722 return sub {
5723 local $/ = $parms->{nul_separated} ? "\x00" : $/;
5724 while ( my $fullpath = <$fh> ) {
5725 chomp $fullpath;
5726 next unless $fullpath =~ /./;
5727 if ( not ( -f $fullpath || -p _ ) ) {
5728 $warn->( "$fullpath: No such file" );
5729 next;
5732 my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );
5733 if ( $filter ) {
5734 local $_ = $file;
5735 local $File::Next::dir = $dirname;
5736 local $File::Next::name = $fullpath;
5737 next if not $filter->();
5739 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
5740 } # while
5741 close $fh;
5743 return;
5744 }; # iterator
5747 sub _bad_invocation {
5748 my $good = (caller(1))[3];
5749 my $bad = $good;
5750 $bad =~ s/(.+)::/$1->/;
5751 return "$good must not be invoked as $bad";
5754 sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }
5755 sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }
5757 sub reslash {
5758 my $path = shift;
5760 my @parts = split( /\//, $path );
5762 return $path if @parts < 2;
5764 return File::Spec->catfile( @parts );
5769 sub _setup {
5770 my $defaults = shift;
5771 my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
5773 my %passed_parms = %{$passed_parms};
5775 my $parms = {};
5776 for my $key ( keys %{$defaults} ) {
5777 $parms->{$key} =
5778 exists $passed_parms{$key}
5779 ? delete $passed_parms{$key}
5780 : $defaults->{$key};
5783 # Any leftover keys are bogus
5784 for my $badkey ( sort keys %passed_parms ) {
5785 my $sub = (caller(1))[3];
5786 $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );
5789 # If it's not a code ref, assume standard sort
5790 if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {
5791 $parms->{sort_files} = \&sort_standard;
5793 my @queue;
5795 for ( @_ ) {
5796 my $start = reslash( $_ );
5797 my $is_dir = -d $start;
5798 my $is_file = -f _;
5799 my $is_fifo = (-p _) || ($start =~ m{^/dev/fd});
5800 push @queue,
5801 $is_dir
5802 ? [ $start, undef, $start, $is_dir, $is_file, $is_fifo ]
5803 : [ undef, $start, $start, $is_dir, $is_file, $is_fifo ];
5806 return ($parms,@queue);
5810 sub _candidate_files {
5811 my $parms = shift;
5812 my $dirname = shift;
5814 my $dh;
5815 if ( !opendir $dh, $dirname ) {
5816 $parms->{error_handler}->( "$dirname: $!", $! + 0 );
5817 return;
5820 my @newfiles;
5821 my $descend_filter = $parms->{descend_filter};
5822 my $follow_symlinks = $parms->{follow_symlinks};
5824 for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
5825 my $fullpath = File::Spec->catdir( $dirname, $file );
5826 if ( !$follow_symlinks ) {
5827 next if -l $fullpath;
5829 else {
5830 stat($fullpath);
5832 my $is_dir = -d _;
5833 my $is_file = -f _;
5834 my $is_fifo = (-p _) || ($fullpath =~ m{^/dev/fd});
5836 # Only do directory checking if we have a descend_filter
5837 if ( $descend_filter ) {
5838 if ( $is_dir ) {
5839 local $File::Next::dir = $fullpath;
5840 local $_ = $file;
5841 next if not $descend_filter->();
5844 push @newfiles, [ $dirname, $file, $fullpath, $is_dir, $is_file, $is_fifo ];
5846 closedir $dh;
5848 my $sort_sub = $parms->{sort_files};
5849 if ( $sort_sub ) {
5850 @newfiles = sort $sort_sub @newfiles;
5853 return @newfiles;
5858 1; # End of File::Next