]> Tony Duckles's Git Repositories (git.nynim.org) - dotfiles.git/blob - bin/ack
.ackrc: Tweak colors, group by filename
[dotfiles.git] / bin / ack
1 #!/usr/bin/env perl
2 #
3 # This file, ack, is generated code.
4 # Please DO NOT EDIT or send patches for it.
5 #
6 # Please take a look at the source from
7 # https://github.com/beyondgrep/ack2
8 # and submit patches against the individual files
9 # that build ack.
10 #
11
12 package main;
13
14 use strict;
15 use warnings;
16 our $VERSION = '2.22'; # Check https://beyondgrep.com/ for updates
17
18 use 5.008008;
19 use Getopt::Long 2.38 ();
20 use Carp 1.04 ();
21
22 use File::Spec ();
23
24
25 # XXX Don't make this so brute force
26 # See also: https://github.com/beyondgrep/ack2/issues/89
27
28 our $opt_after_context;
29 our $opt_before_context;
30 our $opt_output;
31 our $opt_print0;
32 our $opt_color;
33 our $opt_heading;
34 our $opt_show_filename;
35 our $opt_regex;
36 our $opt_break;
37 our $opt_count;
38 our $opt_v;
39 our $opt_m;
40 our $opt_g;
41 our $opt_f;
42 our $opt_lines;
43 our $opt_L;
44 our $opt_l;
45 our $opt_passthru;
46 our $opt_column;
47
48 # Flag if we need any context tracking.
49 our $is_tracking_context;
50
51 # These are all our globals.
52
53 MAIN: {
54 $App::Ack::orig_program_name = $0;
55 $0 = join(' ', 'ack', $0);
56 if ( $App::Ack::VERSION ne $main::VERSION ) {
57 App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );
58 }
59
60 # Do preliminary arg checking;
61 my $env_is_usable = 1;
62 for my $arg ( @ARGV ) {
63 last if ( $arg eq '--' );
64
65 # Get the --thpppt, --bar, --cathy checking out of the way.
66 $arg =~ /^--th[pt]+t+$/ and App::Ack::thpppt($arg);
67 $arg eq '--bar' and App::Ack::ackbar();
68 $arg eq '--cathy' and App::Ack::cathy();
69
70 # See if we want to ignore the environment. (Don't tell Al Gore.)
71 $arg eq '--env' and $env_is_usable = 1;
72 $arg eq '--noenv' and $env_is_usable = 0;
73 }
74
75 if ( !$env_is_usable ) {
76 my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
77 delete @ENV{@keys};
78 }
79 load_colors();
80
81 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
82 Getopt::Long::Configure('pass_through', 'no_auto_abbrev');
83 Getopt::Long::GetOptions(
84 'help' => sub { App::Ack::show_help(); exit; },
85 'version' => sub { App::Ack::print_version_statement(); exit; },
86 'man' => sub { App::Ack::show_man(); exit; },
87 );
88 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
89
90 if ( !@ARGV ) {
91 App::Ack::show_help();
92 exit 1;
93 }
94
95 main();
96 }
97
98 sub _compile_descend_filter {
99 my ( $opt ) = @_;
100
101 my $idirs = 0;
102 my $dont_ignore_dirs = 0;
103
104 for my $filter (@{$opt->{idirs} || []}) {
105 if ($filter->is_inverted()) {
106 $dont_ignore_dirs++;
107 }
108 else {
109 $idirs++;
110 }
111 }
112
113 # If we have one or more --noignore-dir directives, we can't ignore
114 # entire subdirectory hierarchies, so we return an "accept all"
115 # filter and scrutinize the files more in _compile_file_filter.
116 return if $dont_ignore_dirs;
117 return unless $idirs;
118
119 $idirs = $opt->{idirs};
120
121 return sub {
122 my $resource = App::Ack::Resource->new($File::Next::dir);
123 return !grep { $_->filter($resource) } @{$idirs};
124 };
125 }
126
127 sub _compile_file_filter {
128 my ( $opt, $start ) = @_;
129
130 my $ifiles_filters = $opt->{ifiles};
131
132 my $filters = $opt->{'filters'} || [];
133 my $direct_filters = App::Ack::Filter::Collection->new();
134 my $inverse_filters = App::Ack::Filter::Collection->new();
135
136 foreach my $filter (@{$filters}) {
137 if ($filter->is_inverted()) {
138 # We want to check if files match the uninverted filters
139 $inverse_filters->add($filter->invert());
140 }
141 else {
142 $direct_filters->add($filter);
143 }
144 }
145
146 my %is_member_of_starting_set = map { (get_file_id($_) => 1) } @{$start};
147
148 my @ignore_dir_filter = @{$opt->{idirs} || []};
149 my @is_inverted = map { $_->is_inverted() } @ignore_dir_filter;
150 # This depends on InverseFilter->invert returning the original filter (for optimization).
151 @ignore_dir_filter = map { $_->is_inverted() ? $_->invert() : $_ } @ignore_dir_filter;
152 my $dont_ignore_dir_filter = grep { $_ } @is_inverted;
153 my $previous_dir = '';
154 my $previous_dir_ignore_result;
155
156 return sub {
157 if ( $opt_g ) {
158 if ( $File::Next::name =~ /$opt_regex/ && $opt_v ) {
159 return 0;
160 }
161 if ( $File::Next::name !~ /$opt_regex/ && !$opt_v ) {
162 return 0;
163 }
164 }
165 # ack always selects files that are specified on the command
166 # line, regardless of filetype. If you want to ack a JPEG,
167 # and say "ack foo whatever.jpg" it will do it for you.
168 return 1 if $is_member_of_starting_set{ get_file_id($File::Next::name) };
169
170 if ( $dont_ignore_dir_filter ) {
171 if ( $previous_dir eq $File::Next::dir ) {
172 if ( $previous_dir_ignore_result ) {
173 return 0;
174 }
175 }
176 else {
177 my @dirs = File::Spec->splitdir($File::Next::dir);
178
179 my $is_ignoring = 0;
180
181 for ( my $i = 0; $i < @dirs; $i++) {
182 my $dir_rsrc = App::Ack::Resource->new(File::Spec->catfile(@dirs[0 .. $i]));
183
184 my $j = 0;
185 for my $filter (@ignore_dir_filter) {
186 if ( $filter->filter($dir_rsrc) ) {
187 $is_ignoring = !$is_inverted[$j];
188 }
189 $j++;
190 }
191 }
192
193 $previous_dir = $File::Next::dir;
194 $previous_dir_ignore_result = $is_ignoring;
195
196 if ( $is_ignoring ) {
197 return 0;
198 }
199 }
200 }
201
202 # Ignore named pipes found in directory searching. Named
203 # pipes created by subprocesses get specified on the command
204 # line, so the rule of "always select whatever is on the
205 # command line" wins.
206 return 0 if -p $File::Next::name;
207
208 # We can't handle unreadable filenames; report them.
209 if ( not -r _ ) {
210 use filetest 'access';
211
212 if ( not -R $File::Next::name ) {
213 if ( $App::Ack::report_bad_filenames ) {
214 App::Ack::warn( "${File::Next::name}: cannot open file for reading" );
215 }
216 return 0;
217 }
218 }
219
220 my $resource = App::Ack::Resource->new($File::Next::name);
221
222 if ( $ifiles_filters && $ifiles_filters->filter($resource) ) {
223 return 0;
224 }
225
226 my $match_found = $direct_filters->filter($resource);
227
228 # Don't bother invoking inverse filters unless we consider the current resource a match
229 if ( $match_found && $inverse_filters->filter( $resource ) ) {
230 $match_found = 0;
231 }
232 return $match_found;
233 };
234 }
235
236 sub show_types {
237 my $resource = shift;
238 my $ors = shift;
239
240 my @types = filetypes( $resource );
241 my $types = join( ',', @types );
242 my $arrow = @types ? ' => ' : ' =>';
243 App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );
244
245 return;
246 }
247
248 # Set default colors, load Term::ANSIColor
249 sub load_colors {
250 eval 'use Term::ANSIColor 1.10 ()';
251 eval 'use Win32::Console::ANSI' if $App::Ack::is_windows;
252
253 $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
254 $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
255 $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
256
257 return;
258 }
259
260 sub filetypes {
261 my ( $resource ) = @_;
262
263 my @matches;
264
265 foreach my $k (keys %App::Ack::mappings) {
266 my $filters = $App::Ack::mappings{$k};
267
268 foreach my $filter (@{$filters}) {
269 # Clone the resource.
270 my $clone = $resource->clone;
271 if ( $filter->filter($clone) ) {
272 push @matches, $k;
273 last;
274 }
275 }
276 }
277
278 # http://search.cpan.org/dist/Perl-Critic/lib/Perl/Critic/Policy/Subroutines/ProhibitReturnSort.pm
279 @matches = sort @matches;
280 return @matches;
281 }
282
283 # Returns a (fairly) unique identifier for a file.
284 # Use this function to compare two files to see if they're
285 # equal (ie. the same file, but with a different path/links/etc).
286 sub get_file_id {
287 my ( $filename ) = @_;
288
289 if ( $App::Ack::is_windows ) {
290 return File::Next::reslash( $filename );
291 }
292 else {
293 # XXX Is this the best method? It always hits the FS.
294 if( my ( $dev, $inode ) = (stat($filename))[0, 1] ) {
295 return join(':', $dev, $inode);
296 }
297 else {
298 # XXX This could be better.
299 return $filename;
300 }
301 }
302 }
303
304 # Returns a regex object based on a string and command-line options.
305 # Dies when the regex $str is undefined (i.e. not given on command line).
306
307 sub build_regex {
308 my $str = shift;
309 my $opt = shift;
310
311 defined $str or App::Ack::die( 'No regular expression found.' );
312
313 $str = quotemeta( $str ) if $opt->{Q};
314 if ( $opt->{w} ) {
315 my $pristine_str = $str;
316
317 $str = "(?:$str)";
318 $str = "\\b$str" if $pristine_str =~ /^\w/;
319 $str = "$str\\b" if $pristine_str =~ /\w$/;
320 }
321
322 my $regex_is_lc = $str eq lc $str;
323 if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
324 $str = "(?i)$str";
325 }
326
327 my $re = eval { qr/$str/m };
328 if ( !$re ) {
329 die "Invalid regex '$str':\n $@";
330 }
331
332 return $re;
333
334 }
335
336 my $match_column_number;
337
338 {
339
340 # Number of context lines
341 my $n_before_ctx_lines;
342 my $n_after_ctx_lines;
343
344 # Array to keep track of lines that might be required for a "before" context
345 my @before_context_buf;
346 # Position to insert next line in @before_context_buf
347 my $before_context_pos;
348
349 # Number of "after" context lines still pending
350 my $after_context_pending;
351
352 # Number of latest line that got printed
353 my $printed_line_no;
354
355 my $is_iterating;
356
357 my $is_first_match;
358 my $has_printed_something;
359
360 BEGIN {
361 $has_printed_something = 0;
362 }
363
364 # Set up context tracking variables.
365 sub set_up_line_context {
366 $n_before_ctx_lines = $opt_output ? 0 : ($opt_before_context || 0);
367 $n_after_ctx_lines = $opt_output ? 0 : ($opt_after_context || 0);
368
369 @before_context_buf = (undef) x $n_before_ctx_lines;
370 $before_context_pos = 0;
371
372 $is_tracking_context = $n_before_ctx_lines || $n_after_ctx_lines;
373
374 $is_first_match = 1;
375
376 return;
377 }
378
379 # Adjust context tracking variables when entering a new file.
380 sub set_up_line_context_for_file {
381 $printed_line_no = 0;
382 $after_context_pending = 0;
383 if ( $opt_heading && !$opt_lines ) {
384 $is_first_match = 1;
385 }
386
387 return;
388 }
389
390 =begin Developers
391
392 This subroutine jumps through a number of optimization hoops to
393 try to be fast in the more common use cases of ack. For one thing,
394 in non-context tracking searches (not using -A, -B, or -C),
395 conditions that normally would be checked inside the loop happen
396 outside, resulting in three nearly identical loops for -v, --passthru,
397 and normal searching. Any changes that happen to one should propagate
398 to the others if they make sense. The non-context branches also inline
399 does_match for performance reasons; any relevant changes that happen here
400 must also happen there.
401
402 =end Developers
403
404 =cut
405
406 sub print_matches_in_resource {
407 my ( $resource ) = @_;
408
409 my $max_count = $opt_m || -1;
410 my $nmatches = 0;
411 my $filename = $resource->name;
412 my $ors = $opt_print0 ? "\0" : "\n";
413
414 my $has_printed_for_this_resource = 0;
415
416 $is_iterating = 1;
417
418 my $fh = $resource->open();
419 if ( !$fh ) {
420 if ( $App::Ack::report_bad_filenames ) {
421 App::Ack::warn( "$filename: $!" );
422 }
423 return 0;
424 }
425
426 my $display_filename = $filename;
427 if ( $opt_show_filename && $opt_heading && $opt_color ) {
428 $display_filename = Term::ANSIColor::colored($display_filename, $ENV{ACK_COLOR_FILENAME});
429 }
430
431 # Check for context before the main loop, so we don't pay for it if we don't need it.
432 if ( $is_tracking_context ) {
433 $after_context_pending = 0;
434 while ( <$fh> ) {
435 if ( does_match( $_ ) && $max_count ) {
436 if ( !$has_printed_for_this_resource ) {
437 if ( $opt_break && $has_printed_something ) {
438 App::Ack::print_blank_line();
439 }
440 if ( $opt_show_filename && $opt_heading ) {
441 App::Ack::print_filename( $display_filename, $ors );
442 }
443 }
444 print_line_with_context( $filename, $_, $. );
445 $has_printed_for_this_resource = 1;
446 $nmatches++;
447 $max_count--;
448 }
449 elsif ( $opt_passthru ) {
450 chomp; # XXX Proper newline handling?
451 # XXX Inline this call?
452 if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {
453 App::Ack::print_blank_line();
454 }
455 print_line_with_options( $filename, $_, $., ':' );
456 $has_printed_for_this_resource = 1;
457 }
458 else {
459 chomp; # XXX Proper newline handling?
460 print_line_if_context( $filename, $_, $., '-' );
461 }
462
463 last if ($max_count == 0) && ($after_context_pending == 0);
464 }
465 }
466 else {
467 if ( $opt_passthru ) {
468 local $_;
469
470 while ( <$fh> ) {
471 $match_column_number = undef;
472 if ( $opt_v ? !/$opt_regex/o : /$opt_regex/o ) {
473 if ( !$opt_v ) {
474 $match_column_number = $-[0] + 1;
475 }
476 if ( !$has_printed_for_this_resource ) {
477 if ( $opt_break && $has_printed_something ) {
478 App::Ack::print_blank_line();
479 }
480 if ( $opt_show_filename && $opt_heading ) {
481 App::Ack::print_filename( $display_filename, $ors );
482 }
483 }
484 print_line_with_context( $filename, $_, $. );
485 $has_printed_for_this_resource = 1;
486 $nmatches++;
487 $max_count--;
488 }
489 else {
490 chomp; # XXX proper newline handling?
491 if ( $opt_break && !$has_printed_for_this_resource && $has_printed_something ) {
492 App::Ack::print_blank_line();
493 }
494 print_line_with_options( $filename, $_, $., ':' );
495 $has_printed_for_this_resource = 1;
496 }
497 last unless $max_count != 0;
498 }
499 }
500 elsif ( $opt_v ) {
501 local $_;
502
503 $match_column_number = undef;
504 while ( <$fh> ) {
505 if ( !/$opt_regex/o ) {
506 if ( !$has_printed_for_this_resource ) {
507 if ( $opt_break && $has_printed_something ) {
508 App::Ack::print_blank_line();
509 }
510 if ( $opt_show_filename && $opt_heading ) {
511 App::Ack::print_filename( $display_filename, $ors );
512 }
513 }
514 print_line_with_context( $filename, $_, $. );
515 $has_printed_for_this_resource = 1;
516 $nmatches++;
517 $max_count--;
518 }
519 last unless $max_count != 0;
520 }
521 }
522 else {
523 local $_;
524
525 while ( <$fh> ) {
526 $match_column_number = undef;
527 if ( /$opt_regex/o ) {
528 $match_column_number = $-[0] + 1;
529 if ( !$has_printed_for_this_resource ) {
530 if ( $opt_break && $has_printed_something ) {
531 App::Ack::print_blank_line();
532 }
533 if ( $opt_show_filename && $opt_heading ) {
534 App::Ack::print_filename( $display_filename, $ors );
535 }
536 }
537 s/[\r\n]+$//g;
538 print_line_with_options( $filename, $_, $., ':' );
539 $has_printed_for_this_resource = 1;
540 $nmatches++;
541 $max_count--;
542 }
543 last unless $max_count != 0;
544 }
545 }
546
547 }
548
549 $is_iterating = 0;
550
551 return $nmatches;
552 }
553
554 sub print_line_with_options {
555 my ( $filename, $line, $line_no, $separator ) = @_;
556
557 $has_printed_something = 1;
558 $printed_line_no = $line_no;
559
560 my $ors = $opt_print0 ? "\0" : "\n";
561
562 my @line_parts;
563
564 if( $opt_color ) {
565 $filename = Term::ANSIColor::colored($filename,
566 $ENV{ACK_COLOR_FILENAME});
567 $line_no = Term::ANSIColor::colored($line_no,
568 $ENV{ACK_COLOR_LINENO});
569 }
570
571 if($opt_show_filename) {
572 if( $opt_heading ) {
573 push @line_parts, $line_no;
574 }
575 else {
576 push @line_parts, $filename, $line_no;
577 }
578
579 if( $opt_column ) {
580 push @line_parts, get_match_column();
581 }
582 }
583 if( $opt_output ) {
584 while ( $line =~ /$opt_regex/og ) {
585 # XXX We need to stop using eval() for --output. See https://github.com/beyondgrep/ack2/issues/421
586 my $output = eval $opt_output;
587 App::Ack::print( join( $separator, @line_parts, $output ), $ors );
588 }
589 }
590 else {
591 if ( $opt_color ) {
592 # This match is redundant, but we need to perfom it in order to get if capture groups are set.
593 $line =~ /$opt_regex/o;
594
595 if ( @+ > 1 ) { # If we have captures...
596 while ( $line =~ /$opt_regex/og ) {
597 my $offset = 0; # Additional offset for when we add stuff.
598 my $previous_match_end = 0;
599
600 last if $-[0] == $+[0];
601
602 for ( my $i = 1; $i < @+; $i++ ) {
603 my ( $match_start, $match_end ) = ( $-[$i], $+[$i] );
604
605 next unless defined($match_start);
606 next if $match_start < $previous_match_end;
607
608 my $substring = substr( $line,
609 $offset + $match_start, $match_end - $match_start );
610 my $substitution = Term::ANSIColor::colored( $substring,
611 $ENV{ACK_COLOR_MATCH} );
612
613 substr( $line, $offset + $match_start,
614 $match_end - $match_start, $substitution );
615
616 $previous_match_end = $match_end; # Offsets do not need to be applied.
617 $offset += length( $substitution ) - length( $substring );
618 }
619
620 pos($line) = $+[0] + $offset;
621 }
622 }
623 else {
624 my $matched = 0; # If matched, need to escape afterwards.
625
626 while ( $line =~ /$opt_regex/og ) {
627
628 $matched = 1;
629 my ( $match_start, $match_end ) = ($-[0], $+[0]);
630 next unless defined($match_start);
631 last if $match_start == $match_end;
632
633 my $substring = substr( $line, $match_start,
634 $match_end - $match_start );
635 my $substitution = Term::ANSIColor::colored( $substring,
636 $ENV{ACK_COLOR_MATCH} );
637
638 substr( $line, $match_start, $match_end - $match_start,
639 $substitution );
640
641 pos($line) = $match_end +
642 (length( $substitution ) - length( $substring ));
643 }
644 # XXX Why do we do this?
645 $line .= "\033[0m\033[K" if $matched;
646 }
647 }
648
649 push @line_parts, $line;
650 App::Ack::print( join( $separator, @line_parts ), $ors );
651 }
652
653 return;
654 }
655
656 sub iterate {
657 my ( $resource, $cb ) = @_;
658
659 $is_iterating = 1;
660
661 my $fh = $resource->open();
662 if ( !$fh ) {
663 if ( $App::Ack::report_bad_filenames ) {
664 App::Ack::warn( $resource->name . ': ' . $! );
665 }
666 return;
667 }
668
669 # Check for context before the main loop, so we don't pay for it if we don't need it.
670 if ( $is_tracking_context ) {
671 $after_context_pending = 0;
672
673 while ( <$fh> ) {
674 last unless $cb->();
675 }
676 }
677 else {
678 local $_;
679
680 while ( <$fh> ) {
681 last unless $cb->();
682 }
683 }
684
685 $is_iterating = 0;
686
687 return;
688 }
689
690 sub print_line_with_context {
691 my ( $filename, $matching_line, $line_no ) = @_;
692
693 my $ors = $opt_print0 ? "\0" : "\n";
694 my $is_tracking_context = $opt_after_context || $opt_before_context;
695
696 $matching_line =~ s/[\r\n]+$//g;
697
698 # Check if we need to print context lines first.
699 if ( $is_tracking_context ) {
700 my $before_unprinted = $line_no - $printed_line_no - 1;
701 if ( !$is_first_match && ( !$printed_line_no || $before_unprinted > $n_before_ctx_lines ) ) {
702 App::Ack::print('--', $ors);
703 }
704
705 # We want at most $n_before_ctx_lines of context.
706 if ( $before_unprinted > $n_before_ctx_lines ) {
707 $before_unprinted = $n_before_ctx_lines;
708 }
709
710 while ( $before_unprinted > 0 ) {
711 my $line = $before_context_buf[($before_context_pos - $before_unprinted + $n_before_ctx_lines) % $n_before_ctx_lines];
712
713 chomp $line;
714
715 # Disable $opt->{column} since there are no matches in the context lines.
716 local $opt_column = 0;
717
718 print_line_with_options( $filename, $line, $line_no-$before_unprinted, '-' );
719 $before_unprinted--;
720 }
721 }
722
723 print_line_with_options( $filename, $matching_line, $line_no, ':' );
724
725 # We want to get the next $n_after_ctx_lines printed.
726 $after_context_pending = $n_after_ctx_lines;
727
728 $is_first_match = 0;
729
730 return;
731 }
732
733 # Print the line only if it's part of a context we need to display.
734 sub print_line_if_context {
735 my ( $filename, $line, $line_no, $separator ) = @_;
736
737 if ( $after_context_pending ) {
738 # Disable $opt_column since there are no matches in the context lines.
739 local $opt_column = 0;
740 print_line_with_options( $filename, $line, $line_no, $separator );
741 --$after_context_pending;
742 }
743 elsif ( $n_before_ctx_lines ) {
744 # Save line for "before" context.
745 $before_context_buf[$before_context_pos] = $_;
746 $before_context_pos = ($before_context_pos+1) % $n_before_ctx_lines;
747 }
748
749 return;
750 }
751
752 }
753
754 # does_match() MUST have an $opt_regex set.
755
756 =begin Developers
757
758 This subroutine is inlined a few places in print_matches_in_resource
759 for performance reasons, so any changes here must be copied there as
760 well.
761
762 =end Developers
763
764 =cut
765
766 sub does_match {
767 my ( $line ) = @_;
768
769 $match_column_number = undef;
770
771 if ( $opt_v ) {
772 return ( $line !~ /$opt_regex/o );
773 }
774 else {
775 if ( $line =~ /$opt_regex/o ) {
776 # @- = @LAST_MATCH_START
777 # @+ = @LAST_MATCH_END
778 $match_column_number = $-[0] + 1;
779 return 1;
780 }
781 else {
782 return;
783 }
784 }
785 }
786
787 sub get_match_column {
788 return $match_column_number;
789 }
790
791 sub resource_has_match {
792 my ( $resource ) = @_;
793
794 my $has_match = 0;
795 my $fh = $resource->open();
796 if ( !$fh ) {
797 if ( $App::Ack::report_bad_filenames ) {
798 App::Ack::warn( $resource->name . ': ' . $! );
799 }
800 }
801 else {
802 while ( <$fh> ) {
803 if (/$opt_regex/o xor $opt_v) {
804 $has_match = 1;
805 last;
806 }
807 }
808 close $fh;
809 }
810
811 return $has_match;
812 }
813
814 sub count_matches_in_resource {
815 my ( $resource ) = @_;
816
817 my $nmatches = 0;
818 my $fh = $resource->open();
819 if ( !$fh ) {
820 if ( $App::Ack::report_bad_filenames ) {
821 App::Ack::warn( $resource->name . ': ' . $! );
822 }
823 }
824 else {
825 while ( <$fh> ) {
826 ++$nmatches if (/$opt_regex/o xor $opt_v);
827 }
828 close $fh;
829 }
830
831 return $nmatches;
832 }
833
834 sub main {
835 my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();
836
837 my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );
838
839 $opt_after_context = $opt->{after_context};
840 $opt_before_context = $opt->{before_context};
841 $opt_output = $opt->{output};
842 $opt_print0 = $opt->{print0};
843 $opt_color = $opt->{color};
844 $opt_heading = $opt->{heading};
845 $opt_show_filename = $opt->{show_filename};
846 $opt_regex = $opt->{regex};
847 $opt_break = $opt->{break};
848 $opt_count = $opt->{count};
849 $opt_v = $opt->{v};
850 $opt_m = $opt->{m};
851 $opt_g = $opt->{g};
852 $opt_f = $opt->{f};
853 $opt_lines = $opt->{lines};
854 $opt_L = $opt->{L};
855 $opt_l = $opt->{l};
856 $opt_passthru = $opt->{passthru};
857 $opt_column = $opt->{column};
858
859 $App::Ack::report_bad_filenames = !$opt->{dont_report_bad_filenames};
860
861 if ( $opt->{flush} ) {
862 $| = 1;
863 }
864
865 if ( !defined($opt_color) && !$opt_g ) {
866 my $windows_color = 1;
867 if ( $App::Ack::is_windows ) {
868 $windows_color = eval { require Win32::Console::ANSI; };
869 }
870 $opt_color = !App::Ack::output_to_pipe() && $windows_color;
871 }
872 if ( not defined $opt_heading and not defined $opt_break ) {
873 $opt_heading = $opt_break = $opt->{break} = !App::Ack::output_to_pipe();
874 }
875
876 if ( defined($opt->{H}) || defined($opt->{h}) ) {
877 $opt_show_filename = $opt->{show_filename} = $opt->{H} && !$opt->{h};
878 }
879
880 if ( my $output = $opt_output ) {
881 $output =~ s{\\}{\\\\}g;
882 $output =~ s{"}{\\"}g;
883 $opt_output = qq{"$output"};
884 }
885
886 my $resources;
887 if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x
888 $resources = App::Ack::Resources->from_stdin( $opt );
889 $opt_regex = shift @ARGV if not defined $opt_regex;
890 $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );
891 }
892 else {
893 if ( $opt_f || $opt_lines ) {
894 if ( $opt_regex ) {
895 App::Ack::warn( "regex ($opt_regex) specified with -f or --lines" );
896 App::Ack::exit_from_ack( 0 ); # XXX the 0 is misleading
897 }
898 }
899 else {
900 $opt_regex = shift @ARGV if not defined $opt_regex;
901 $opt_regex = $opt->{regex} = build_regex( $opt_regex, $opt );
902 }
903 if ( $opt_regex && $opt_regex =~ /\n/ ) {
904 App::Ack::exit_from_ack( 0 );
905 }
906 my @start;
907 if ( not defined $opt->{files_from} ) {
908 @start = @ARGV;
909 }
910 if ( !exists($opt->{show_filename}) ) {
911 unless(@start == 1 && !(-d $start[0])) {
912 $opt_show_filename = $opt->{show_filename} = 1;
913 }
914 }
915
916 if ( defined $opt->{files_from} ) {
917 $resources = App::Ack::Resources->from_file( $opt, $opt->{files_from} );
918 exit 1 unless $resources;
919 }
920 else {
921 @start = ('.') unless @start;
922 foreach my $target (@start) {
923 if ( !-e $target && $App::Ack::report_bad_filenames) {
924 App::Ack::warn( "$target: No such file or directory" );
925 }
926 }
927
928 $opt->{file_filter} = _compile_file_filter($opt, \@start);
929 $opt->{descend_filter} = _compile_descend_filter($opt);
930
931 $resources = App::Ack::Resources->from_argv( $opt, \@start );
932 }
933 }
934 App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
935
936 my $ors = $opt_print0 ? "\0" : "\n";
937 my $only_first = $opt->{1};
938
939 my $nmatches = 0;
940 my $total_count = 0;
941
942 set_up_line_context();
943
944 RESOURCES:
945 while ( my $resource = $resources->next ) {
946 if ($is_tracking_context) {
947 set_up_line_context_for_file();
948 }
949
950 if ( $opt_f ) {
951 if ( $opt->{show_types} ) {
952 show_types( $resource, $ors );
953 }
954 else {
955 App::Ack::print( $resource->name, $ors );
956 }
957 ++$nmatches;
958 last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;
959 }
960 elsif ( $opt_g ) {
961 if ( $opt->{show_types} ) {
962 show_types( $resource, $ors );
963 }
964 else {
965 local $opt_show_filename = 0; # XXX Why is this local?
966
967 print_line_with_options( '', $resource->name, 0, $ors );
968 }
969 ++$nmatches;
970 last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;
971 }
972 elsif ( $opt_lines ) {
973 my %line_numbers;
974 foreach my $line ( @{ $opt_lines } ) {
975 my @lines = split /,/, $line;
976 @lines = map {
977 /^(\d+)-(\d+)$/
978 ? ( $1 .. $2 )
979 : $_
980 } @lines;
981 @line_numbers{@lines} = (1) x @lines;
982 }
983
984 my $filename = $resource->name;
985
986 local $opt_color = 0;
987
988 iterate( $resource, sub {
989 chomp;
990
991 if ( $line_numbers{$.} ) {
992 print_line_with_context( $filename, $_, $. );
993 }
994 elsif ( $opt_passthru ) {
995 print_line_with_options( $filename, $_, $., ':' );
996 }
997 elsif ( $is_tracking_context ) {
998 print_line_if_context( $filename, $_, $., '-' );
999 }
1000 return 1;
1001 });
1002 }
1003 elsif ( $opt_count ) {
1004 my $matches_for_this_file = count_matches_in_resource( $resource );
1005
1006 if ( not $opt_show_filename ) {
1007 $total_count += $matches_for_this_file;
1008 next RESOURCES;
1009 }
1010
1011 if ( !$opt_l || $matches_for_this_file > 0) {
1012 if ( $opt_show_filename ) {
1013 App::Ack::print( $resource->name, ':', $matches_for_this_file, $ors );
1014 }
1015 else {
1016 App::Ack::print( $matches_for_this_file, $ors );
1017 }
1018 }
1019 }
1020 elsif ( $opt_l || $opt_L ) {
1021 my $is_match = resource_has_match( $resource );
1022
1023 if ( $opt_L ? !$is_match : $is_match ) {
1024 App::Ack::print( $resource->name, $ors );
1025 ++$nmatches;
1026
1027 last RESOURCES if $only_first;
1028 last RESOURCES if defined($opt_m) && $nmatches >= $opt_m;
1029 }
1030 }
1031 else {
1032 $nmatches += print_matches_in_resource( $resource, $opt );
1033 if ( $nmatches && $only_first ) {
1034 last RESOURCES;
1035 }
1036 }
1037 }
1038
1039 if ( $opt_count && !$opt_show_filename ) {
1040 App::Ack::print( $total_count, "\n" );
1041 }
1042
1043 close $App::Ack::fh;
1044 App::Ack::exit_from_ack( $nmatches );
1045 }
1046
1047 =pod
1048
1049 =encoding UTF-8
1050
1051 =head1 NAME
1052
1053 ack - grep-like text finder
1054
1055 =head1 SYNOPSIS
1056
1057 ack [options] PATTERN [FILE...]
1058 ack -f [options] [DIRECTORY...]
1059
1060 =head1 DESCRIPTION
1061
1062 ack is designed as an alternative to F<grep> for programmers.
1063
1064 ack searches the named input files or directories for lines containing a
1065 match to the given PATTERN. By default, ack prints the matching lines.
1066 If no FILE or DIRECTORY is given, the current directory will be searched.
1067
1068 PATTERN is a Perl regular expression. Perl regular expressions
1069 are commonly found in other programming languages, but for the particulars
1070 of their behavior, please consult
1071 L<http://perldoc.perl.org/perlreref.html|perlreref>. If you don't know
1072 how to use regular expression but are interested in learning, you may
1073 consult L<http://perldoc.perl.org/perlretut.html|perlretut>. If you do not
1074 need or want ack to use regular expressions, please see the
1075 C<-Q>/C<--literal> option.
1076
1077 Ack can also list files that would be searched, without actually
1078 searching them, to let you take advantage of ack's file-type filtering
1079 capabilities.
1080
1081 =head1 FILE SELECTION
1082
1083 If files are not specified for searching, either on the command
1084 line or piped in with the C<-x> option, I<ack> delves into
1085 subdirectories selecting files for searching.
1086
1087 I<ack> is intelligent about the files it searches. It knows about
1088 certain file types, based on both the extension on the file and,
1089 in some cases, the contents of the file. These selections can be
1090 made with the B<--type> option.
1091
1092 With no file selection, I<ack> searches through regular files that
1093 are not explicitly excluded by B<--ignore-dir> and B<--ignore-file>
1094 options, either present in F<ackrc> files or on the command line.
1095
1096 The default options for I<ack> ignore certain files and directories. These
1097 include:
1098
1099 =over 4
1100
1101 =item * Backup files: Files matching F<#*#> or ending with F<~>.
1102
1103 =item * Coredumps: Files matching F<core.\d+>
1104
1105 =item * Version control directories like F<.svn> and F<.git>.
1106
1107 =back
1108
1109 Run I<ack> with the C<--dump> option to see what settings are set.
1110
1111 However, I<ack> always searches the files given on the command line,
1112 no matter what type. If you tell I<ack> to search in a coredump,
1113 it will search in a coredump.
1114
1115 =head1 DIRECTORY SELECTION
1116
1117 I<ack> descends through the directory tree of the starting directories
1118 specified. If no directories are specified, the current working directory is
1119 used. However, it will ignore the shadow directories used by
1120 many version control systems, and the build directories used by the
1121 Perl MakeMaker system. You may add or remove a directory from this
1122 list with the B<--[no]ignore-dir> option. The option may be repeated
1123 to add/remove multiple directories from the ignore list.
1124
1125 For a complete list of directories that do not get searched, run
1126 C<ack --dump>.
1127
1128 =head1 WHEN TO USE GREP
1129
1130 I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't
1131 throw I<grep> away, because there are times you'll still need it.
1132
1133 E.g., searching through huge files looking for regexes that can be
1134 expressed with I<grep> syntax should be quicker with I<grep>.
1135
1136 If your script or parent program uses I<grep> C<--quiet> or C<--silent>
1137 or needs exit 2 on IO error, use I<grep>.
1138
1139 =head1 OPTIONS
1140
1141 =over 4
1142
1143 =item B<--ackrc>
1144
1145 Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.
1146
1147 =item B<-A I<NUM>>, B<--after-context=I<NUM>>
1148
1149 Print I<NUM> lines of trailing context after matching lines.
1150
1151 =item B<-B I<NUM>>, B<--before-context=I<NUM>>
1152
1153 Print I<NUM> lines of leading context before matching lines.
1154
1155 =item B<--[no]break>
1156
1157 Print a break between results from different files. On by default
1158 when used interactively.
1159
1160 =item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
1161
1162 Print I<NUM> lines (default 2) of context around matching lines.
1163 You can specify zero lines of context to override another context
1164 specified in an ackrc.
1165
1166 =item B<-c>, B<--count>
1167
1168 Suppress normal output; instead print a count of matching lines for
1169 each input file. If B<-l> is in effect, it will only show the
1170 number of lines for each file that has lines matching. Without
1171 B<-l>, some line counts may be zeroes.
1172
1173 If combined with B<-h> (B<--no-filename>) ack outputs only one total
1174 count.
1175
1176 =item B<--[no]color>, B<--[no]colour>
1177
1178 B<--color> highlights the matching text. B<--nocolor> suppresses
1179 the color. This is on by default unless the output is redirected.
1180
1181 On Windows, this option is off by default unless the
1182 L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>
1183 environment variable is used.
1184
1185 =item B<--color-filename=I<color>>
1186
1187 Sets the color to be used for filenames.
1188
1189 =item B<--color-match=I<color>>
1190
1191 Sets the color to be used for matches.
1192
1193 =item B<--color-lineno=I<color>>
1194
1195 Sets the color to be used for line numbers.
1196
1197 =item B<--[no]column>
1198
1199 Show the column number of the first match. This is helpful for
1200 editors that can place your cursor at a given position.
1201
1202 =item B<--create-ackrc>
1203
1204 Dumps the default ack options to standard output. This is useful for
1205 when you want to customize the defaults.
1206
1207 =item B<--dump>
1208
1209 Writes the list of options loaded and where they came from to standard
1210 output. Handy for debugging.
1211
1212 =item B<--[no]env>
1213
1214 B<--noenv> disables all environment processing. No F<.ackrc> is
1215 read and all environment variables are ignored. By default, F<ack>
1216 considers F<.ackrc> and settings in the environment.
1217
1218 =item B<--flush>
1219
1220 B<--flush> flushes output immediately. This is off by default
1221 unless ack is running interactively (when output goes to a pipe or
1222 file).
1223
1224 =item B<-f>
1225
1226 Only print the files that would be searched, without actually doing
1227 any searching. PATTERN must not be specified, or it will be taken
1228 as a path to search.
1229
1230 =item B<--files-from=I<FILE>>
1231
1232 The list of files to be searched is specified in I<FILE>. The list of
1233 files are separated by newlines. If I<FILE> is C<->, the list is loaded
1234 from standard input.
1235
1236 =item B<--[no]filter>
1237
1238 Forces ack to act as if it were receiving input via a pipe.
1239
1240 =item B<--[no]follow>
1241
1242 Follow or don't follow symlinks, other than whatever starting files
1243 or directories were specified on the command line.
1244
1245 This is off by default.
1246
1247 =item B<-g I<PATTERN>>
1248
1249 Print searchable files where the relative path + filename matches
1250 I<PATTERN>.
1251
1252 Note that
1253
1254 ack -g foo
1255
1256 is exactly the same as
1257
1258 ack -f | ack foo
1259
1260 This means that just as ack will not search, for example, F<.jpg>
1261 files, C<-g> will not list F<.jpg> files either. ack is not intended
1262 to be a general-purpose file finder.
1263
1264 Note also that if you have C<-i> in your .ackrc that the filenames
1265 to be matched will be case-insensitive as well.
1266
1267 This option can be combined with B<--color> to make it easier to
1268 spot the match.
1269
1270 =item B<--[no]group>
1271
1272 B<--group> groups matches by file name. This is the default
1273 when used interactively.
1274
1275 B<--nogroup> prints one result per line, like grep. This is the
1276 default when output is redirected.
1277
1278 =item B<-H>, B<--with-filename>
1279
1280 Print the filename for each match. This is the default unless searching
1281 a single explicitly specified file.
1282
1283 =item B<-h>, B<--no-filename>
1284
1285 Suppress the prefixing of filenames on output when multiple files are
1286 searched.
1287
1288 =item B<--[no]heading>
1289
1290 Print a filename heading above each file's results. This is the default
1291 when used interactively.
1292
1293 =item B<--help>, B<-?>
1294
1295 Print a short help statement.
1296
1297 =item B<--help-types>, B<--help=types>
1298
1299 Print all known types.
1300
1301 =item B<-i>, B<--ignore-case>
1302
1303 Ignore case distinctions in PATTERN
1304
1305 =item B<--ignore-ack-defaults>
1306
1307 Tells ack to completely ignore the default definitions provided with ack.
1308 This is useful in combination with B<--create-ackrc> if you I<really> want
1309 to customize ack.
1310
1311 =item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=I<DIRNAME>>
1312
1313 Ignore directory (as CVS, .svn, etc are ignored). May be used
1314 multiple times to ignore multiple directories. For example, mason
1315 users may wish to include B<--ignore-dir=data>. The B<--noignore-dir>
1316 option allows users to search directories which would normally be
1317 ignored (perhaps to research the contents of F<.svn/props> directories).
1318
1319 The I<DIRNAME> must always be a simple directory name. Nested
1320 directories like F<foo/bar> are NOT supported. You would need to
1321 specify B<--ignore-dir=foo> and then no files from any foo directory
1322 are taken into account by ack unless given explicitly on the command
1323 line.
1324
1325 =item B<--ignore-file=I<FILTERTYPE:FILTERARGS>>
1326
1327 Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified
1328 identically to file type filters as seen in L</"Defining your own types">.
1329
1330 =item B<-k>, B<--known-types>
1331
1332 Limit selected files to those with types that ack knows about. This is
1333 equivalent to the default behavior found in ack 1.
1334
1335 =item B<--lines=I<NUM>>
1336
1337 Only print line I<NUM> of each file. Multiple lines can be given with multiple
1338 B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=4-7>
1339 also works. The lines are always output in ascending order, no matter the
1340 order given on the command line.
1341
1342 =item B<-l>, B<--files-with-matches>
1343
1344 Only print the filenames of matching files, instead of the matching text.
1345
1346 =item B<-L>, B<--files-without-matches>
1347
1348 Only print the filenames of files that do I<NOT> match.
1349
1350 =item B<--match I<PATTERN>>
1351
1352 Specify the I<PATTERN> explicitly. This is helpful if you don't want to put the
1353 regex as your first argument, e.g. when executing multiple searches over the
1354 same set of files.
1355
1356 # search for foo and bar in given files
1357 ack file1 t/file* --match foo
1358 ack file1 t/file* --match bar
1359
1360 =item B<-m=I<NUM>>, B<--max-count=I<NUM>>
1361
1362 Stop reading a file after I<NUM> matches.
1363
1364 =item B<--man>
1365
1366 Print this manual page.
1367
1368 =item B<-n>, B<--no-recurse>
1369
1370 No descending into subdirectories.
1371
1372 =item B<-o>
1373
1374 Show only the part of each line matching PATTERN (turns off text
1375 highlighting)
1376
1377 =item B<--output=I<expr>>
1378
1379 Output the evaluation of I<expr> for each line (turns off text
1380 highlighting)
1381 If PATTERN matches more than once then a line is output for each non-overlapping match.
1382 For more information please see the section L</"Examples of F<--output>">.
1383
1384 =item B<--pager=I<program>>, B<--nopager>
1385
1386 B<--pager> directs ack's output through I<program>. This can also be specified
1387 via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.
1388
1389 Using --pager does not suppress grouping and coloring like piping
1390 output on the command-line does.
1391
1392 B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.
1393 No output will be sent through a pager.
1394
1395 =item B<--passthru>
1396
1397 Prints all lines, whether or not they match the expression. Highlighting
1398 will still work, though, so it can be used to highlight matches while
1399 still seeing the entire file, as in:
1400
1401 # Watch a log file, and highlight a certain IP address
1402 $ tail -f ~/access.log | ack --passthru 123.45.67.89
1403
1404 =item B<--print0>
1405
1406 Only works in conjunction with -f, -g, -l or -c (filename output). The filenames
1407 are output separated with a null byte instead of the usual newline. This is
1408 helpful when dealing with filenames that contain whitespace, e.g.
1409
1410 # remove all files of type html
1411 ack -f --html --print0 | xargs -0 rm -f
1412
1413 =item B<-Q>, B<--literal>
1414
1415 Quote all metacharacters in PATTERN, it is treated as a literal.
1416
1417 =item B<-r>, B<-R>, B<--recurse>
1418
1419 Recurse into sub-directories. This is the default and just here for
1420 compatibility with grep. You can also use it for turning B<--no-recurse> off.
1421
1422 =item B<-s>
1423
1424 Suppress error messages about nonexistent or unreadable files. This is taken
1425 from fgrep.
1426
1427 =item B<--[no]smart-case>, B<--no-smart-case>
1428
1429 Ignore case in the search strings if PATTERN contains no uppercase
1430 characters. This is similar to C<smartcase> in vim. This option is
1431 off by default, and ignored if C<-i> is specified.
1432
1433 B<-i> always overrides this option.
1434
1435 =item B<--sort-files>
1436
1437 Sorts the found files lexicographically. Use this if you want your file
1438 listings to be deterministic between runs of I<ack>.
1439
1440 =item B<--show-types>
1441
1442 Outputs the filetypes that ack associates with each file.
1443
1444 Works with B<-f> and B<-g> options.
1445
1446 =item B<--type=[no]TYPE>
1447
1448 Specify the types of files to include or exclude from a search.
1449 TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can
1450 also be specified as B<--perl>, and B<--type=noperl> can be done
1451 as B<--noperl>.
1452
1453 If a file is of both type "foo" and "bar", specifying --foo and
1454 --nobar will exclude the file, because an exclusion takes precedence
1455 over an inclusion.
1456
1457 Type specifications can be repeated and are ORed together.
1458
1459 See I<ack --help=types> for a list of valid types.
1460
1461 =item B<--type-add I<TYPE>:I<FILTER>:I<FILTERARGS>>
1462
1463 Files with the given FILTERARGS applied to the given FILTER
1464 are recognized as being of (the existing) type TYPE.
1465 See also L</"Defining your own types">.
1466
1467
1468 =item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>
1469
1470 Files with the given FILTERARGS applied to the given FILTER are recognized as
1471 being of type TYPE. This replaces an existing definition for type TYPE. See
1472 also L</"Defining your own types">.
1473
1474 =item B<--type-del I<TYPE>>
1475
1476 The filters associated with TYPE are removed from Ack, and are no longer considered
1477 for searches.
1478
1479 =item B<-v>, B<--invert-match>
1480
1481 Invert match: select non-matching lines
1482
1483 =item B<--version>
1484
1485 Display version and copyright information.
1486
1487 =item B<-w>, B<--word-regexp>
1488
1489 =item B<-w>, B<--word-regexp>
1490
1491 Turn on "words mode". This sometimes matches a whole word, but the
1492 semantics is quite subtle. If the passed regexp begins with a word
1493 character, then a word boundary is required before the match. If the
1494 passed regexp ends with a word character, or with a word character
1495 followed by newline, then a word boundary is required after the match.
1496
1497 Thus, for example, B<-w> with the regular expression C<ox> will not
1498 match the strings C<box> or C<oxen>. However, if the regular
1499 expression is C<(ox|ass)> then it will match those strings. Because
1500 the regular expression's first character is C<(>, the B<-w> flag has
1501 no effect at the start, and because the last character is C<)>, it has
1502 no effect at the end.
1503
1504 Force PATTERN to match only whole words. The PATTERN is wrapped with
1505 C<\b> metacharacters.
1506
1507 =item B<-x>
1508
1509 An abbreviation for B<--files-from=->; the list of files to search are read
1510 from standard input, with one line per file.
1511
1512 =item B<-1>
1513
1514 Stops after reporting first match of any kind. This is different
1515 from B<--max-count=1> or B<-m1>, where only one match per file is
1516 shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does
1517 not.
1518
1519 =item B<--thpppt>
1520
1521 Display the all-important Bill The Cat logo. Note that the exact
1522 spelling of B<--thpppppt> is not important. It's checked against
1523 a regular expression.
1524
1525 =item B<--bar>
1526
1527 Check with the admiral for traps.
1528
1529 =item B<--cathy>
1530
1531 Chocolate, Chocolate, Chocolate!
1532
1533 =back
1534
1535 =head1 THE .ackrc FILE
1536
1537 The F<.ackrc> file contains command-line options that are prepended
1538 to the command line before processing. Multiple options may live
1539 on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>
1540 might look like this:
1541
1542 # Always sort the files
1543 --sort-files
1544
1545 # Always color, even if piping to another program
1546 --color
1547
1548 # Use "less -r" as my pager
1549 --pager=less -r
1550
1551 Note that arguments with spaces in them do not need to be quoted,
1552 as they are not interpreted by the shell. Basically, each I<line>
1553 in the F<.ackrc> file is interpreted as one element of C<@ARGV>.
1554
1555 F<ack> looks in several locations for F<.ackrc> files; the searching
1556 process is detailed in L</"ACKRC LOCATION SEMANTICS">. These
1557 files are not considered if B<--noenv> is specified on the command line.
1558
1559 =head1 Defining your own types
1560
1561 ack allows you to define your own types in addition to the predefined
1562 types. This is done with command line options that are best put into
1563 an F<.ackrc> file - then you do not have to define your types over and
1564 over again. In the following examples the options will always be shown
1565 on one command line so that they can be easily copy & pasted.
1566
1567 File types can be specified both with the the I<--type=xxx> option,
1568 or the file type as an option itself. For example, if you create
1569 a filetype of "cobol", you can specify I<--type=cobol> or simply
1570 I<--cobol>. File types must be at least two characters long. This
1571 is why the C language is I<--cc> and the R language is I<--rr>.
1572
1573 I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>
1574 tells you, that perl files are files ending
1575 in .pl, .pm, .pod or .t. So what if you would like to include .xs
1576 files as well when searching for --perl files? I<ack --type-add perl:ext:xs --perl foo>
1577 does this for you. B<--type-add> appends
1578 additional extensions to an existing type.
1579
1580 If you want to define a new type, or completely redefine an existing
1581 type, then use B<--type-set>. I<ack --type-set eiffel:ext:e,eiffel> defines
1582 the type I<eiffel> to include files with
1583 the extensions .e or .eiffel. So to search for all eiffel files
1584 containing the word Bertrand use I<ack --type-set eiffel:ext:e,eiffel --eiffel Bertrand>.
1585 As usual, you can also write B<--type=eiffel>
1586 instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes
1587 all eiffel files from a search. Redefining also works: I<ack --type-set cc:ext:c,h>
1588 and I<.xs> files no longer belong to the type I<cc>.
1589
1590 When defining your own types in the F<.ackrc> file you have to use
1591 the following:
1592
1593 --type-set=eiffel:ext:e,eiffel
1594
1595 or writing on separate lines
1596
1597 --type-set
1598 eiffel:ext:e,eiffel
1599
1600 The following does B<NOT> work in the F<.ackrc> file:
1601
1602 --type-set eiffel:ext:e,eiffel
1603
1604 In order to see all currently defined types, use I<--help-types>, e.g.
1605 I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>
1606
1607 In addition to filtering based on extension (like ack 1.x allowed), ack 2
1608 offers additional filter types. The generic syntax is
1609 I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value
1610 of I<FILTER>.
1611
1612 =over 4
1613
1614 =item is:I<FILENAME>
1615
1616 I<is> filters match the target filename exactly. It takes exactly one
1617 argument, which is the name of the file to match.
1618
1619 Example:
1620
1621 --type-set make:is:Makefile
1622
1623 =item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]
1624
1625 I<ext> filters match the extension of the target file against a list
1626 of extensions. No leading dot is needed for the extensions.
1627
1628 Example:
1629
1630 --type-set perl:ext:pl,pm,t
1631
1632 =item match:I<PATTERN>
1633
1634 I<match> filters match the target filename against a regular expression.
1635 The regular expression is made case insensitive for the search.
1636
1637 Example:
1638
1639 --type-set make:match:/(gnu)?makefile/
1640
1641 =item firstlinematch:I<PATTERN>
1642
1643 I<firstlinematch> matches the first line of the target file against a
1644 regular expression. Like I<match>, the regular expression is made
1645 case insensitive.
1646
1647 Example:
1648
1649 --type-add perl:firstlinematch:/perl/
1650
1651 =back
1652
1653 More filter types may be made available in the future.
1654
1655 =head1 ENVIRONMENT VARIABLES
1656
1657 For commonly-used ack options, environment variables can make life
1658 much easier. These variables are ignored if B<--noenv> is specified
1659 on the command line.
1660
1661 =over 4
1662
1663 =item ACKRC
1664
1665 Specifies the location of the user's F<.ackrc> file. If this file doesn't
1666 exist, F<ack> looks in the default location.
1667
1668 =item ACK_OPTIONS
1669
1670 This variable specifies default options to be placed in front of
1671 any explicit options on the command line.
1672
1673 =item ACK_COLOR_FILENAME
1674
1675 Specifies the color of the filename when it's printed in B<--group>
1676 mode. By default, it's "bold green".
1677
1678 The recognized attributes are clear, reset, dark, bold, underline,
1679 underscore, blink, reverse, concealed black, red, green, yellow,
1680 blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,
1681 on_magenta, on_cyan, and on_white. Case is not significant.
1682 Underline and underscore are equivalent, as are clear and reset.
1683 The color alone sets the foreground color, and on_color sets the
1684 background color.
1685
1686 This option can also be set with B<--color-filename>.
1687
1688 =item ACK_COLOR_MATCH
1689
1690 Specifies the color of the matching text when printed in B<--color>
1691 mode. By default, it's "black on_yellow".
1692
1693 This option can also be set with B<--color-match>.
1694
1695 See B<ACK_COLOR_FILENAME> for the color specifications.
1696
1697 =item ACK_COLOR_LINENO
1698
1699 Specifies the color of the line number when printed in B<--color>
1700 mode. By default, it's "bold yellow".
1701
1702 This option can also be set with B<--color-lineno>.
1703
1704 See B<ACK_COLOR_FILENAME> for the color specifications.
1705
1706 =item ACK_PAGER
1707
1708 Specifies a pager program, such as C<more>, C<less> or C<most>, to which
1709 ack will send its output.
1710
1711 Using C<ACK_PAGER> does not suppress grouping and coloring like
1712 piping output on the command-line does, except that on Windows
1713 ack will assume that C<ACK_PAGER> does not support color.
1714
1715 C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
1716
1717 =item ACK_PAGER_COLOR
1718
1719 Specifies a pager program that understands ANSI color sequences.
1720 Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring
1721 like piping output on the command-line does.
1722
1723 If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
1724
1725 =back
1726
1727 =head1 AVAILABLE COLORS
1728
1729 F<ack> uses the colors available in Perl's L<Term::ANSIColor> module, which
1730 provides the following listed values. Note that case does not matter when using
1731 these values.
1732
1733 =head2 Foreground colors
1734
1735 black red green yellow blue magenta cyan white
1736
1737 bright_black bright_red bright_green bright_yellow
1738 bright_blue bright_magenta bright_cyan bright_white
1739
1740 =head2 Background colors
1741
1742 on_black on_red on_green on_yellow
1743 on_blue on_magenta on_cyan on_white
1744
1745 on_bright_black on_bright_red on_bright_green on_bright_yellow
1746 on_bright_blue on_bright_magenta on_bright_cyan on_bright_white
1747
1748 =head1 ACK & OTHER TOOLS
1749
1750 =head2 Simple vim integration
1751
1752 F<ack> integrates easily with the Vim text editor. Set this in your
1753 F<.vimrc> to use F<ack> instead of F<grep>:
1754
1755 set grepprg=ack\ -k
1756
1757 That example uses C<-k> to search through only files of the types ack
1758 knows about, but you may use other default flags. Now you can search
1759 with F<ack> and easily step through the results in Vim:
1760
1761 :grep Dumper perllib
1762
1763 =head2 Editor integration
1764
1765 Many users have integrated ack into their preferred text editors.
1766 For details and links, see L<https://beyondgrep.com/more-tools/>.
1767
1768 =head2 Shell and Return Code
1769
1770 For greater compatibility with I<grep>, I<ack> in normal use returns
1771 shell return or exit code of 0 only if something is found and 1 if
1772 no match is found.
1773
1774 (Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
1775
1776 The I<grep> code 2 for errors is not used.
1777
1778 If C<-f> or C<-g> are specified, then 0 is returned if at least one
1779 file is found. If no files are found, then 1 is returned.
1780
1781 =cut
1782
1783 =head1 DEBUGGING ACK PROBLEMS
1784
1785 If ack gives you output you're not expecting, start with a few simple steps.
1786
1787 =head2 Use B<--noenv>
1788
1789 Your environment variables and F<.ackrc> may be doing things you're
1790 not expecting, or forgotten you specified. Use B<--noenv> to ignore
1791 your environment and F<.ackrc>.
1792
1793 =head2 Use B<-f> to see what files have been selected
1794
1795 Ack's B<-f> was originally added as a debugging tool. If ack is
1796 not finding matches you think it should find, run F<ack -f> to see
1797 what files have been selected. You can also add the C<--show-types>
1798 options to show the type of each file selected.
1799
1800 =head2 Use B<--dump>
1801
1802 This lists the ackrc files that are loaded and the options loaded
1803 from them.
1804 So for example you can find a list of directories that do not get searched or where filetypes are defined.
1805
1806 =head1 TIPS
1807
1808 =head2 Use the F<.ackrc> file.
1809
1810 The F<.ackrc> is the place to put all your options you use most of
1811 the time but don't want to remember. Put all your --type-add and
1812 --type-set definitions in it. If you like --smart-case, set it
1813 there, too. I also set --sort-files there.
1814
1815 =head2 Use F<-f> for working with big codesets
1816
1817 Ack does more than search files. C<ack -f --perl> will create a
1818 list of all the Perl files in a tree, ideal for sending into F<xargs>.
1819 For example:
1820
1821 # Change all "this" to "that" in all Perl files in a tree.
1822 ack -f --perl | xargs perl -p -i -e's/this/that/g'
1823
1824 or if you prefer:
1825
1826 perl -p -i -e's/this/that/g' $(ack -f --perl)
1827
1828 =head2 Use F<-Q> when in doubt about metacharacters
1829
1830 If you're searching for something with a regular expression
1831 metacharacter, most often a period in a filename or IP address, add
1832 the -Q to avoid false positives without all the backslashing. See
1833 the following example for more...
1834
1835 =head2 Use ack to watch log files
1836
1837 Here's one I used the other day to find trouble spots for a website
1838 visitor. The user had a problem loading F<troublesome.gif>, so I
1839 took the access log and scanned it with ack twice.
1840
1841 ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif
1842
1843 The first ack finds only the lines in the Apache log for the given
1844 IP. The second finds the match on my troublesome GIF, and shows
1845 the previous five lines from the log in each case.
1846
1847 =head2 Examples of F<--output>
1848
1849 Following variables are useful in the expansion string:
1850
1851 =over 4
1852
1853 =item C<$&>
1854
1855 The whole string matched by PATTERN.
1856
1857 =item C<$1>, C<$2>, ...
1858
1859 The contents of the 1st, 2nd ... bracketed group in PATTERN.
1860
1861 =item C<$`>
1862
1863 The string before the match.
1864
1865 =item C<$'>
1866
1867 The string after the match.
1868
1869 =back
1870
1871 For more details and other variables see
1872 L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.
1873
1874 This example shows how to add text around a particular pattern
1875 (in this case adding _ around word with "e")
1876
1877 ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"
1878 _The_ quick brown fox jumps over the lazy dog
1879 The quick brown fox jumps _over_ the lazy dog
1880 The quick brown fox jumps over _the_ lazy dog
1881
1882 This shows how to pick out particular parts of a match using ( ) within regular expression.
1883
1884 ack '=head(\d+)\s+(.*)' --output=' $1 : $2'
1885 input file contains "=head1 NAME"
1886 output "1 : NAME"
1887
1888 =head1 COMMUNITY
1889
1890 There are ack mailing lists and a Slack channel for ack. See
1891 L<https://beyondgrep.com/community/> for details.
1892
1893 =head1 FAQ
1894
1895 =head2 Why isn't ack finding a match in (some file)?
1896
1897 First, take a look and see if ack is even looking at the file. ack is
1898 intelligent in what files it will search and which ones it won't, but
1899 sometimes that can be surprising.
1900
1901 Use the C<-f> switch, with no regex, to see a list of files that ack
1902 will search for you. If your file doesn't show up in the list of files
1903 that C<ack -f> shows, then ack never looks in it.
1904
1905 NOTE: If you're using an old ack before 2.0, it's probably because it's of
1906 a type that ack doesn't recognize. In ack 1.x, the searching behavior is
1907 driven by filetype. B<If ack 1.x doesn't know what kind of file it is,
1908 ack ignores the file.> You can use the C<--show-types> switch to show
1909 which type ack thinks each file is.
1910
1911 =head2 Wouldn't it be great if F<ack> did search & replace?
1912
1913 No, ack will always be read-only. Perl has a perfectly good way
1914 to do search & replace in files, using the C<-i>, C<-p> and C<-n>
1915 switches.
1916
1917 You can certainly use ack to select your files to update. For
1918 example, to change all "foo" to "bar" in all PHP files, you can do
1919 this from the Unix shell:
1920
1921 $ perl -i -p -e's/foo/bar/g' $(ack -f --php)
1922
1923 =head2 Can I make ack recognize F<.xyz> files?
1924
1925 Yes! Please see L</"Defining your own types">. If you think
1926 that F<ack> should recognize a type by default, please see
1927 L</"ENHANCEMENTS">.
1928
1929 =head2 There's already a program/package called ack.
1930
1931 Yes, I know.
1932
1933 =head2 Why is it called ack if it's called ack-grep?
1934
1935 The name of the program is "ack". Some packagers have called it
1936 "ack-grep" when creating packages because there's already a package
1937 out there called "ack" that has nothing to do with this ack.
1938
1939 I suggest you make a symlink named F<ack> that points to F<ack-grep>
1940 because one of the crucial benefits of ack is having a name that's
1941 so short and simple to type.
1942
1943 To do that, run this with F<sudo> or as root:
1944
1945 ln -s /usr/bin/ack-grep /usr/bin/ack
1946
1947 Alternatively, you could use a shell alias:
1948
1949 # bash/zsh
1950 alias ack=ack-grep
1951
1952 # csh
1953 alias ack ack-grep
1954
1955 =head2 What does F<ack> mean?
1956
1957 Nothing. I wanted a name that was easy to type and that you could
1958 pronounce as a single syllable.
1959
1960 =head2 Can I do multi-line regexes?
1961
1962 No, ack does not support regexes that match multiple lines. Doing
1963 so would require reading in the entire file at a time.
1964
1965 If you want to see lines near your match, use the C<--A>, C<--B>
1966 and C<--C> switches for displaying context.
1967
1968 =head2 Why is ack telling me I have an invalid option when searching for C<+foo>?
1969
1970 ack treats command line options beginning with C<+> or C<-> as options; if you
1971 would like to search for these, you may prefix your search term with C<--> or
1972 use the C<--match> option. (However, don't forget that C<+> is a regular
1973 expression metacharacter!)
1974
1975 =head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?
1976
1977 The Perl language limits the repetition quantifier to 32K. You
1978 can search for C<.{32767}> but not C<.{32768}>.
1979
1980 =head2 Ack does "X" and shouldn't, should it?
1981
1982 We try to remain as close to grep's behavior as possible, so when in doubt,
1983 see what grep does! If there's a mismatch in functionality there, please
1984 bring it up on the ack-users mailing list.
1985
1986 =head1 ACKRC LOCATION SEMANTICS
1987
1988 Ack can load its configuration from many sources. The following list
1989 specifies the sources Ack looks for configuration files; each one
1990 that is found is loaded in the order specified here, and
1991 each one overrides options set in any of the sources preceding
1992 it. (For example, if I set --sort-files in my user ackrc, and
1993 --nosort-files on the command line, the command line takes
1994 precedence)
1995
1996 =over 4
1997
1998 =item *
1999
2000 Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted
2001 using C<--ignore-ack-defaults>.
2002
2003 =item * Global ackrc
2004
2005 Options are then loaded from the global ackrc. This is located at
2006 C</etc/ackrc> on Unix-like systems.
2007
2008 Under Windows XP and earlier, the global ackrc is at
2009 C<C:\Documents and Settings\All Users\Application Data\ackrc>
2010
2011 Under Windows Vista/7, the global ackrc is at
2012 C<C:\ProgramData\ackrc>
2013
2014 The C<--noenv> option prevents all ackrc files from being loaded.
2015
2016 =item * User ackrc
2017
2018 Options are then loaded from the user's ackrc. This is located at
2019 C<$HOME/.ackrc> on Unix-like systems.
2020
2021 Under Windows XP and earlier, the user's ackrc is at
2022 C<C:\Documents and Settings\$USER\Application Data\ackrc>.
2023
2024 Under Windows Vista/7, the user's ackrc is at
2025 C<C:\Users\$USER\AppData\Roaming\ackrc>.
2026
2027 If you want to load a different user-level ackrc, it may be specified
2028 with the C<$ACKRC> environment variable.
2029
2030 The C<--noenv> option prevents all ackrc files from being loaded.
2031
2032 =item * Project ackrc
2033
2034 Options are then loaded from the project ackrc. The project ackrc is
2035 the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching
2036 in the current directory, then the parent directory, then the grandparent
2037 directory, etc. This can be omitted using C<--noenv>.
2038
2039 =item * --ackrc
2040
2041 The C<--ackrc> option may be included on the command line to specify an
2042 ackrc file that can override all others. It is consulted even if C<--noenv>
2043 is present.
2044
2045 =item * ACK_OPTIONS
2046
2047 Options are then loaded from the environment variable C<ACK_OPTIONS>. This can
2048 be omitted using C<--noenv>.
2049
2050 =item * Command line
2051
2052 Options are then loaded from the command line.
2053
2054 =back
2055
2056 =head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X
2057
2058 A lot of changes were made for ack 2; here is a list of them.
2059
2060 =head2 GENERAL CHANGES
2061
2062 =over 4
2063
2064 =item *
2065
2066 When no selectors are specified, ack 1.x only searches through files that
2067 it can map to a file type. ack 2.x, by contrast, will search through
2068 every regular, non-binary file that is not explicitly ignored via
2069 B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the
2070 B<-a/--all> option in ack 1.x.
2071
2072 =item *
2073
2074 A more flexible filter system has been added, so that more powerful file types
2075 may be created by the user. For details, please consult
2076 L</"Defining your own types">.
2077
2078 =item *
2079
2080 ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for
2081 details.
2082
2083 =item *
2084
2085 ack's default filter definitions aren't special; you may tell ack to
2086 completely disregard them if you don't like them.
2087
2088 =back
2089
2090 =head2 REMOVED OPTIONS
2091
2092 =over 4
2093
2094 =item *
2095
2096 Because of the change in default search behavior, the B<-a/--all> and
2097 B<-u/--unrestricted> options have been removed. In addition, the
2098 B<-k/--known-types> option was added to cause ack to behave with
2099 the default search behavior of ack 1.x.
2100
2101 =item *
2102
2103 The B<-G> option has been removed. Two regular expressions on the
2104 command line was considered too confusing; to simulate B<-G>'s functionality,
2105 you may use the new B<-x> option to pipe filenames from one invocation of
2106 ack into another.
2107
2108 =item *
2109
2110 The B<--binary> option has been removed.
2111
2112 =item *
2113
2114 The B<--skipped> option has been removed.
2115
2116 =item *
2117
2118 The B<--text> option has been removed.
2119
2120 =item *
2121
2122 The B<--invert-file-match> option has been removed. Instead, you may
2123 use B<-v> with B<-g>.
2124
2125 =back
2126
2127 =head2 CHANGED OPTIONS
2128
2129 =over 4
2130
2131 =item *
2132
2133 The options that modify the regular expression's behavior (B<-i>, B<-w>,
2134 B<-Q>, and B<-v>) may now be used with B<-g>.
2135
2136 =back
2137
2138 =head2 ADDED OPTIONS
2139
2140 =over 4
2141
2142 =item *
2143
2144 B<--files-from> was added so that a user may submit a list of filenames as
2145 a list of files to search.
2146
2147 =item *
2148
2149 B<-x> was added to tell ack to accept a list of filenames via standard input;
2150 this list is the list of filenames that will be used for the search.
2151
2152 =item *
2153
2154 B<-s> was added to tell ack to suppress error messages about non-existent or
2155 unreadable files.
2156
2157 =item *
2158
2159 B<--ignore-directory> and B<--noignore-directory> were added as aliases for
2160 B<--ignore-dir> and B<--noignore-dir> respectively.
2161
2162 =item *
2163
2164 B<--ignore-file> was added so that users may specify patterns of files to
2165 ignore (ex. /.*~$/).
2166
2167 =item *
2168
2169 B<--dump> was added to allow users to easily find out which options are
2170 set where.
2171
2172 =item *
2173
2174 B<--create-ackrc> was added so that users may create custom ackrc files based
2175 on the default settings loaded by ack, and so that users may easily view those
2176 defaults.
2177
2178 =item *
2179
2180 B<--type-del> was added to selectively remove file type definitions.
2181
2182 =item *
2183
2184 B<--ignore-ack-defaults> was added so that users may ignore ack's default
2185 options in favor of their own.
2186
2187 =item *
2188
2189 B<--bar> was added so ack users may consult Admiral Ackbar.
2190
2191 =back
2192
2193 =head1 AUTHOR
2194
2195 Andy Lester, C<< <andy at petdance.com> >>
2196
2197 =head1 BUGS
2198
2199 Please report any bugs or feature requests to the issues list at
2200 Github: L<https://github.com/beyondgrep/ack2/issues>
2201
2202 =head1 ENHANCEMENTS
2203
2204 All enhancement requests MUST first be posted to the ack-users
2205 mailing list at L<http://groups.google.com/group/ack-users>. I
2206 will not consider a request without it first getting seen by other
2207 ack users. This includes requests for new filetypes.
2208
2209 There is a list of enhancements I want to make to F<ack> in the ack
2210 issues list at Github: L<https://github.com/beyondgrep/ack2/issues>
2211
2212 Patches are always welcome, but patches with tests get the most
2213 attention.
2214
2215 =head1 SUPPORT
2216
2217 Support for and information about F<ack> can be found at:
2218
2219 =over 4
2220
2221 =item * The ack homepage
2222
2223 L<https://beyondgrep.com/>
2224
2225 =item * The ack-users mailing list
2226
2227 L<http://groups.google.com/group/ack-users>
2228
2229 =item * The ack issues list at Github
2230
2231 L<https://github.com/beyondgrep/ack2/issues>
2232
2233 =item * AnnoCPAN: Annotated CPAN documentation
2234
2235 L<http://annocpan.org/dist/ack>
2236
2237 =item * CPAN Ratings
2238
2239 L<http://cpanratings.perl.org/d/ack>
2240
2241 =item * Search CPAN
2242
2243 L<http://search.cpan.org/dist/ack>
2244
2245 =item * MetaCPAN
2246
2247 L<http://metacpan.org/release/ack>
2248
2249 =item * Git source repository
2250
2251 L<https://github.com/beyondgrep/ack2>
2252
2253 =back
2254
2255 =head1 ACKNOWLEDGEMENTS
2256
2257 How appropriate to have I<ack>nowledgements!
2258
2259 Thanks to everyone who has contributed to ack in any way, including
2260 Michele Campeotto,
2261 H.Merijn Brand,
2262 Duke Leto,
2263 Gerhard Poul,
2264 Ethan Mallove,
2265 Marek Kubica,
2266 Ray Donnelly,
2267 Nikolaj Schumacher,
2268 Ed Avis,
2269 Nick Morrott,
2270 Austin Chamberlin,
2271 Varadinsky,
2272 SE<eacute>bastien FeugE<egrave>re,
2273 Jakub Wilk,
2274 Pete Houston,
2275 Stephen Thirlwall,
2276 Jonah Bishop,
2277 Chris Rebert,
2278 Denis Howe,
2279 RaE<uacute>l GundE<iacute>n,
2280 James McCoy,
2281 Daniel Perrett,
2282 Steven Lee,
2283 Jonathan Perret,
2284 Fraser Tweedale,
2285 RaE<aacute>l GundE<aacute>n,
2286 Steffen Jaeckel,
2287 Stephan Hohe,
2288 Michael Beijen,
2289 Alexandr Ciornii,
2290 Christian Walde,
2291 Charles Lee,
2292 Joe McMahon,
2293 John Warwick,
2294 David Steinbrunner,
2295 Kara Martens,
2296 Volodymyr Medvid,
2297 Ron Savage,
2298 Konrad Borowski,
2299 Dale Sedivic,
2300 Michael McClimon,
2301 Andrew Black,
2302 Ralph Bodenner,
2303 Shaun Patterson,
2304 Ryan Olson,
2305 Shlomi Fish,
2306 Karen Etheridge,
2307 Olivier Mengue,
2308 Matthew Wild,
2309 Scott Kyle,
2310 Nick Hooey,
2311 Bo Borgerson,
2312 Mark Szymanski,
2313 Marq Schneider,
2314 Packy Anderson,
2315 JR Boyens,
2316 Dan Sully,
2317 Ryan Niebur,
2318 Kent Fredric,
2319 Mike Morearty,
2320 Ingmar Vanhassel,
2321 Eric Van Dewoestine,
2322 Sitaram Chamarty,
2323 Adam James,
2324 Richard Carlsson,
2325 Pedro Melo,
2326 AJ Schuster,
2327 Phil Jackson,
2328 Michael Schwern,
2329 Jan Dubois,
2330 Christopher J. Madsen,
2331 Matthew Wickline,
2332 David Dyck,
2333 Jason Porritt,
2334 Jjgod Jiang,
2335 Thomas Klausner,
2336 Uri Guttman,
2337 Peter Lewis,
2338 Kevin Riggle,
2339 Ori Avtalion,
2340 Torsten Blix,
2341 Nigel Metheringham,
2342 GE<aacute>bor SzabE<oacute>,
2343 Tod Hagan,
2344 Michael Hendricks,
2345 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,
2346 Piers Cawley,
2347 Stephen Steneker,
2348 Elias Lutfallah,
2349 Mark Leighton Fisher,
2350 Matt Diephouse,
2351 Christian Jaeger,
2352 Bill Sully,
2353 Bill Ricker,
2354 David Golden,
2355 Nilson Santos F. Jr,
2356 Elliot Shank,
2357 Merijn Broeren,
2358 Uwe Voelker,
2359 Rick Scott,
2360 Ask BjE<oslash>rn Hansen,
2361 Jerry Gay,
2362 Will Coleda,
2363 Mike O'Regan,
2364 Slaven ReziE<0x107>,
2365 Mark Stosberg,
2366 David Alan Pisoni,
2367 Adriano Ferreira,
2368 James Keenan,
2369 Leland Johnson,
2370 Ricardo Signes,
2371 Pete Krawczyk and
2372 Rob Hoelz.
2373
2374 =head1 COPYRIGHT & LICENSE
2375
2376 Copyright 2005-2017 Andy Lester.
2377
2378 This program is free software; you can redistribute it and/or modify
2379 it under the terms of the Artistic License v2.0.
2380
2381 See http://www.perlfoundation.org/artistic_license_2_0 or the LICENSE.md
2382 file that comes with the ack distribution.
2383
2384 =cut
2385 package App::Ack;
2386
2387 use warnings;
2388 use strict;
2389
2390
2391 our $VERSION;
2392 our $COPYRIGHT;
2393 BEGIN {
2394 $VERSION = '2.22';
2395 $COPYRIGHT = 'Copyright 2005-2017 Andy Lester.';
2396 }
2397
2398 our $fh;
2399
2400 BEGIN {
2401 $fh = *STDOUT;
2402 }
2403
2404
2405 our %types;
2406 our %type_wanted;
2407 our %mappings;
2408 our %ignore_dirs;
2409
2410 our $is_filter_mode;
2411 our $output_to_pipe;
2412
2413 our $dir_sep_chars;
2414 our $is_cygwin;
2415 our $is_windows;
2416
2417 use File::Spec 1.00015 ();
2418
2419 BEGIN {
2420 # These have to be checked before any filehandle diddling.
2421 $output_to_pipe = not -t *STDOUT;
2422 $is_filter_mode = -p STDIN;
2423
2424 $is_cygwin = ($^O eq 'cygwin' || $^O eq 'msys');
2425 $is_windows = ($^O eq 'MSWin32');
2426 $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
2427 }
2428
2429
2430
2431 sub remove_dir_sep {
2432 my $path = shift;
2433 $path =~ s/[$dir_sep_chars]$//;
2434
2435 return $path;
2436 }
2437
2438
2439
2440 sub warn {
2441 return CORE::warn( _my_program(), ': ', @_, "\n" );
2442 }
2443
2444
2445 sub die {
2446 return CORE::die( _my_program(), ': ', @_, "\n" );
2447 }
2448
2449 sub _my_program {
2450 require File::Basename;
2451 return File::Basename::basename( $0 );
2452 }
2453
2454
2455
2456 sub filetypes_supported {
2457 return keys %mappings;
2458 }
2459
2460 sub thpppt {
2461 my $y = q{_ /|,\\'!.x',=(www)=, U };
2462 $y =~ tr/,x!w/\nOo_/;
2463
2464 App::Ack::print( "$y ack $_[0]!\n" );
2465 exit 0;
2466 }
2467
2468 sub ackbar {
2469 my $x;
2470 $x = <<'_BAR';
2471 6?!I'7!I"?%+!
2472 3~!I#7#I"7#I!?!+!="+"="+!:!
2473 2?#I!7!I!?#I!7!I"+"=%+"=#
2474 1?"+!?*+!=#~"=!+#?"="+!
2475 0?"+!?"I"?&+!="~!=!~"=!+%="+"
2476 /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!
2477 .?%I"?%+%='?!=#~$="
2478 ,,!?%I"?(+$=$~!=#:"~$:!~!
2479 ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!
2480 +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"
2481 *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!
2482 *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"
2483 )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!
2484 (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!
2485 (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!
2486 %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"
2487 $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/
2488 $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!
2489 $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/
2490 %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!
2491 %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!
2492 %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!
2493 %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!
2494 &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!
2495 &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!
2496 &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!
2497 &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!
2498 &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"
2499 &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!
2500 %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&
2501 %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)
2502 $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!
2503 $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"
2504 "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!
2505 !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!
2506 !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*
2507 7+:!,!~#,"=!7'I!?#I"7/+!7+
2508 77I!+!7!?!7!I"71+!7,
2509 _BAR
2510
2511 return _pic_decode($x);
2512 }
2513
2514 sub cathy {
2515 my $x = <<'CATHY';
2516 0+!--+!
2517 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2518 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2519 0|! "C!H!O!C!O!L!A!T!E!!! !|!
2520 0|! $A"C!K!!! $|!
2521 0+!--+!
2522 6\! 1:!,!.! !
2523 7\! /.!M!~!Z!M!~!
2524 8\! /~!D! "M! !
2525 4.! $\! /M!~!.!8! +.!M# 4
2526 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3
2527 /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4
2528 ..! !D!Z!.!Z!.! '\! 9=!M".! 6
2529 /.! !.!~!M".! '\! 8~! 9
2530 4M!.! /.!7!N!M!.! F
2531 4.! &:!M! !N"M# !M"N!M! #D!M&=! =
2532 :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;
2533 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9
2534 (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0
2535 &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! +
2536 &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *
2537 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! +
2538 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4
2539 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3
2540 /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1
2541 #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0
2542 $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '
2543 #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (
2544 #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %
2545 *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &
2546 )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '
2547 )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! +
2548 (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! +
2549 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *
2550 %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )
2551 !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (
2552 !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &
2553 !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $
2554 !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $
2555 (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #
2556 '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %
2557 $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! +
2558 ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )
2559 +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (
2560 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )
2561 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /
2562 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2
2563 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %
2564 0?!$! &N! )." .,! %."M! ":!M!.! 0
2565 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0
2566 CATHY
2567 return _pic_decode($x);
2568 }
2569
2570 sub _pic_decode {
2571 my($compressed) = @_;
2572 $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;
2573 App::Ack::print( $compressed );
2574 exit 0;
2575 }
2576
2577
2578 sub show_help {
2579 my $help_arg = shift || 0;
2580
2581 return show_help_types() if $help_arg =~ /^types?/;
2582
2583 App::Ack::print( <<"END_OF_HELP" );
2584 Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
2585
2586 Search for PATTERN in each source file in the tree from the current
2587 directory on down. If any files or directories are specified, then
2588 only those files and directories are checked. ack may also search
2589 STDIN, but only if no file or directory arguments are specified,
2590 or if one of them is "-".
2591
2592 Default switches may be specified in ACK_OPTIONS environment variable or
2593 an .ackrc file. If you want no dependency on the environment, turn it
2594 off with --noenv.
2595
2596 Example: ack -i select
2597
2598 Searching:
2599 -i, --ignore-case Ignore case distinctions in PATTERN
2600 --[no]smart-case Ignore case distinctions in PATTERN,
2601 only if PATTERN contains no upper case.
2602 Ignored if -i is specified
2603 -v, --invert-match Invert match: select non-matching lines
2604 -w, --word-regexp Force PATTERN to match only whole words
2605 -Q, --literal Quote all metacharacters; PATTERN is literal
2606
2607 Search output:
2608 --lines=NUM Only print line(s) NUM of each file
2609 -l, --files-with-matches Only print filenames containing matches
2610 -L, --files-without-matches Only print filenames with no matches
2611 --output=expr Output the evaluation of expr for each line
2612 (turns off text highlighting)
2613 -o Show only the part of a line matching PATTERN
2614 Same as --output='\$&'
2615 --passthru Print all lines, whether matching or not
2616 --match PATTERN Specify PATTERN explicitly.
2617 -m, --max-count=NUM Stop searching in each file after NUM matches
2618 -1 Stop searching after one match of any kind
2619 -H, --with-filename Print the filename for each match (default:
2620 on unless explicitly searching a single file)
2621 -h, --no-filename Suppress the prefixing filename on output
2622 -c, --count Show number of lines matching per file
2623 --[no]column Show the column number of the first match
2624
2625 -A NUM, --after-context=NUM Print NUM lines of trailing context after
2626 matching lines.
2627 -B NUM, --before-context=NUM Print NUM lines of leading context before
2628 matching lines.
2629 -C [NUM], --context[=NUM] Print NUM lines (default 2) of output context.
2630
2631 --print0 Print null byte as separator between filenames,
2632 only works with -f, -g, -l, -L or -c.
2633
2634 -s Suppress error messages about nonexistent or
2635 unreadable files.
2636
2637
2638 File presentation:
2639 --pager=COMMAND Pipes all ack output through COMMAND. For
2640 example, --pager="less -R". Ignored if output
2641 is redirected.
2642 --nopager Do not send output through a pager. Cancels
2643 any setting in ~/.ackrc, ACK_PAGER or
2644 ACK_PAGER_COLOR.
2645 --[no]heading Print a filename heading above each file's
2646 results. (default: on when used interactively)
2647 --[no]break Print a break between results from different
2648 files. (default: on when used interactively)
2649 --group Same as --heading --break
2650 --nogroup Same as --noheading --nobreak
2651 --[no]color Highlight the matching text (default: on unless
2652 output is redirected, or on Windows)
2653 --[no]colour Same as --[no]color
2654 --color-filename=COLOR
2655 --color-match=COLOR
2656 --color-lineno=COLOR Set the color for filenames, matches, and line
2657 numbers.
2658 --flush Flush output immediately, even when ack is used
2659 non-interactively (when output goes to a pipe or
2660 file).
2661
2662
2663 File finding:
2664 -f Only print the files selected, without
2665 searching. The PATTERN must not be specified.
2666 -g Same as -f, but only select files matching
2667 PATTERN.
2668 --sort-files Sort the found files lexically.
2669 --show-types Show which types each file has.
2670 --files-from=FILE Read the list of files to search from FILE.
2671 -x Read the list of files to search from STDIN.
2672
2673 File inclusion/exclusion:
2674 --[no]ignore-dir=name Add/remove directory from list of ignored dirs
2675 --[no]ignore-directory=name Synonym for ignore-dir
2676 --ignore-file=filter Add filter for ignoring files
2677 -r, -R, --recurse Recurse into subdirectories (default: on)
2678 -n, --no-recurse No descending into subdirectories
2679 --[no]follow Follow symlinks. Default is off.
2680 -k, --known-types Include only files of types that ack recognizes.
2681
2682 --type=X Include only X files, where X is a recognized
2683 filetype.
2684 --type=noX Exclude X files.
2685 See "ack --help-types" for supported filetypes.
2686
2687 File type specification:
2688 --type-set TYPE:FILTER:FILTERARGS
2689 Files with the given FILTERARGS applied to the
2690 given FILTER are recognized as being of type
2691 TYPE. This replaces an existing definition for
2692 type TYPE.
2693 --type-add TYPE:FILTER:FILTERARGS
2694 Files with the given FILTERARGS applied to the
2695 given FILTER are recognized as being type TYPE.
2696 --type-del TYPE Removes all filters associated with TYPE.
2697
2698
2699 Miscellaneous:
2700 --[no]env Ignore environment variables and global ackrc
2701 files. --env is legal but redundant.
2702 --ackrc=filename Specify an ackrc file to use
2703 --ignore-ack-defaults Ignore default definitions included with ack.
2704 --create-ackrc Outputs a default ackrc for your customization
2705 to standard output.
2706 --help, -? This help
2707 --help-types Display all known types
2708 --dump Dump information on which options are loaded
2709 from which RC files
2710 --[no]filter Force ack to treat standard input as a pipe
2711 (--filter) or tty (--nofilter)
2712 --man Man page
2713 --version Display version & copyright
2714 --thpppt Bill the Cat
2715 --bar The warning admiral
2716 --cathy Chocolate! Chocolate! Chocolate!
2717
2718 Exit status is 0 if match, 1 if no match.
2719
2720 ack's home page is at https://beyondgrep.com/
2721
2722 The full ack manual is available by running "ack --man".
2723
2724 This is version $VERSION of ack. Run "ack --version" for full version info.
2725 END_OF_HELP
2726
2727 return;
2728 }
2729
2730
2731
2732 sub show_help_types {
2733 App::Ack::print( <<'END_OF_HELP' );
2734 Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
2735
2736 The following is the list of filetypes supported by ack. You can
2737 specify a file type with the --type=TYPE format, or the --TYPE
2738 format. For example, both --type=perl and --perl work.
2739
2740 Note that some extensions may appear in multiple types. For example,
2741 .pod files are both Perl and Parrot.
2742
2743 END_OF_HELP
2744
2745 my @types = filetypes_supported();
2746 my $maxlen = 0;
2747 for ( @types ) {
2748 $maxlen = length if $maxlen < length;
2749 }
2750 for my $type ( sort @types ) {
2751 next if $type =~ /^-/; # Stuff to not show
2752 my $ext_list = $mappings{$type};
2753
2754 if ( ref $ext_list ) {
2755 $ext_list = join( '; ', map { $_->to_string } @{$ext_list} );
2756 }
2757 App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
2758 }
2759
2760 return;
2761 }
2762
2763 sub show_man {
2764 require Pod::Usage;
2765
2766 Pod::Usage::pod2usage({
2767 -input => $App::Ack::orig_program_name,
2768 -verbose => 2,
2769 -exitval => 0,
2770 });
2771
2772 return;
2773 }
2774
2775
2776 sub get_version_statement {
2777 require Config;
2778
2779 my $copyright = get_copyright();
2780 my $this_perl = $Config::Config{perlpath};
2781 if ($^O ne 'VMS') {
2782 my $ext = $Config::Config{_exe};
2783 $this_perl .= $ext unless $this_perl =~ m/$ext$/i;
2784 }
2785 my $ver = sprintf( '%vd', $^V );
2786
2787 return <<"END_OF_VERSION";
2788 ack ${VERSION}
2789 Running under Perl $ver at $this_perl
2790
2791 $copyright
2792
2793 This program is free software. You may modify or distribute it
2794 under the terms of the Artistic License v2.0.
2795 END_OF_VERSION
2796 }
2797
2798
2799 sub print_version_statement {
2800 App::Ack::print( get_version_statement() );
2801
2802 return;
2803 }
2804
2805
2806 sub get_copyright {
2807 return $COPYRIGHT;
2808 }
2809
2810
2811 sub print { print {$fh} @_; return; }
2812 sub print_blank_line { App::Ack::print( "\n" ); return; }
2813 sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }
2814
2815 sub set_up_pager {
2816 my $command = shift;
2817
2818 return if App::Ack::output_to_pipe();
2819
2820 my $pager;
2821 if ( not open( $pager, '|-', $command ) ) {
2822 App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
2823 }
2824 $fh = $pager;
2825
2826 return;
2827 }
2828
2829
2830 sub output_to_pipe {
2831 return $output_to_pipe;
2832 }
2833
2834
2835 sub exit_from_ack {
2836 my $nmatches = shift;
2837
2838 my $rc = $nmatches ? 0 : 1;
2839 exit $rc;
2840 }
2841
2842
2843
2844 1; # End of App::Ack
2845 package App::Ack::Resource;
2846
2847
2848 use warnings;
2849 use strict;
2850 use overload
2851 '""' => 'name';
2852
2853
2854 sub new {
2855 my $class = shift;
2856 my $filename = shift;
2857
2858 my $self = bless {
2859 filename => $filename,
2860 fh => undef,
2861 opened => 0,
2862 }, $class;
2863
2864 if ( $self->{filename} eq '-' ) {
2865 $self->{fh} = *STDIN;
2866 $self->{opened} = 1;
2867 }
2868
2869 return $self;
2870 }
2871
2872
2873
2874 sub name {
2875 return $_[0]->{filename};
2876 }
2877
2878
2879
2880 sub basename {
2881 my ( $self ) = @_;
2882
2883 # XXX Definedness? Pre-populate the slot with an undef?
2884 unless ( exists $self->{basename} ) {
2885 $self->{basename} = (File::Spec->splitpath($self->name))[2];
2886 }
2887
2888 return $self->{basename};
2889 }
2890
2891
2892
2893 sub open {
2894 my ( $self ) = @_;
2895
2896 if ( !$self->{opened} ) {
2897 if ( open $self->{fh}, '<', $self->{filename} ) {
2898 $self->{opened} = 1;
2899 }
2900 else {
2901 $self->{fh} = undef;
2902 }
2903 }
2904
2905 return $self->{fh};
2906 }
2907
2908
2909
2910 sub needs_line_scan {
2911 my $self = shift;
2912 my $opt = shift;
2913
2914 return 1 if $opt->{v};
2915
2916 my $size = -s $self->{fh};
2917 if ( $size == 0 ) {
2918 return 0;
2919 }
2920 elsif ( $size > 100_000 ) {
2921 return 1;
2922 }
2923
2924 my $buffer;
2925 my $rc = sysread( $self->{fh}, $buffer, $size );
2926 if ( !defined($rc) && $App::Ack::report_bad_filenames ) {
2927 App::Ack::warn( "$self->{filename}: $!" );
2928 return 1;
2929 }
2930 return 0 unless $rc && ( $rc == $size );
2931
2932 return $buffer =~ /$opt->{regex}/m;
2933 }
2934
2935
2936
2937 sub reset {
2938 my $self = shift;
2939
2940 # Return if we haven't opened the file yet.
2941 if ( !defined($self->{fh}) ) {
2942 return;
2943 }
2944
2945 if ( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
2946 App::Ack::warn( "$self->{filename}: $!" );
2947 }
2948
2949 return;
2950 }
2951
2952
2953
2954 sub close {
2955 my $self = shift;
2956
2957 # Return if we haven't opened the file yet.
2958 if ( !defined($self->{fh}) ) {
2959 return;
2960 }
2961
2962 if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
2963 App::Ack::warn( $self->name() . ": $!" );
2964 }
2965
2966 $self->{opened} = 0;
2967
2968 return;
2969 }
2970
2971
2972
2973 sub clone {
2974 my ( $self ) = @_;
2975
2976 return __PACKAGE__->new($self->name);
2977 }
2978
2979
2980
2981 sub firstliney {
2982 my ( $self ) = @_;
2983
2984 if ( !exists $self->{firstliney} ) {
2985 my $fh = $self->open();
2986 if ( !$fh ) {
2987 if ( $App::Ack::report_bad_filenames ) {
2988 App::Ack::warn( $self->name . ': ' . $! );
2989 }
2990 return '';
2991 }
2992
2993 my $buffer = '';
2994 my $rc = sysread( $fh, $buffer, 250 );
2995 unless($rc) { # XXX handle this better?
2996 $buffer = '';
2997 }
2998 $buffer =~ s/[\r\n].*//s;
2999 $self->{firstliney} = $buffer;
3000 $self->reset;
3001
3002 $self->close;
3003 }
3004
3005 return $self->{firstliney};
3006 }
3007
3008 1;
3009 package App::Ack::Resources;
3010
3011
3012
3013 use Errno qw(EACCES);
3014
3015 use warnings;
3016 use strict;
3017
3018 sub _generate_error_handler {
3019 my $opt = shift;
3020
3021 if ( $opt->{dont_report_bad_filenames} ) {
3022 return sub {
3023 my $msg = shift;
3024 if ( $! == EACCES ) {
3025 return;
3026 }
3027 App::Ack::warn( $msg );
3028 };
3029 }
3030 else {
3031 return sub {
3032 my $msg = shift;
3033 App::Ack::warn( $msg );
3034 };
3035 }
3036 }
3037
3038
3039 sub from_argv {
3040 my $class = shift;
3041 my $opt = shift;
3042 my $start = shift;
3043
3044 my $self = bless {}, $class;
3045
3046 my $file_filter = undef;
3047 my $descend_filter = $opt->{descend_filter};
3048
3049 if( $opt->{n} ) {
3050 $descend_filter = sub {
3051 return 0;
3052 };
3053 }
3054
3055 $self->{iter} =
3056 File::Next::files( {
3057 file_filter => $opt->{file_filter},
3058 descend_filter => $descend_filter,
3059 error_handler => _generate_error_handler($opt),
3060 warning_handler => sub {},
3061 sort_files => $opt->{sort_files},
3062 follow_symlinks => $opt->{follow},
3063 }, @{$start} );
3064
3065 return $self;
3066 }
3067
3068
3069 sub from_file {
3070 my $class = shift;
3071 my $opt = shift;
3072 my $file = shift;
3073
3074 my $iter =
3075 File::Next::from_file( {
3076 error_handler => _generate_error_handler($opt),
3077 warning_handler => _generate_error_handler($opt),
3078 sort_files => $opt->{sort_files},
3079 }, $file ) or return undef;
3080
3081 return bless {
3082 iter => $iter,
3083 }, $class;
3084 }
3085
3086 # This is for reading input lines from STDIN, not the list of files from STDIN
3087 sub from_stdin {
3088 my $class = shift;
3089 my $opt = shift;
3090
3091 my $self = bless {}, $class;
3092
3093 my $has_been_called = 0;
3094
3095 $self->{iter} = sub {
3096 if ( !$has_been_called ) {
3097 $has_been_called = 1;
3098 return '-';
3099 }
3100 return;
3101 };
3102
3103 return $self;
3104 }
3105
3106 sub next {
3107 my $self = shift;
3108
3109 my $file = $self->{iter}->() or return;
3110
3111 return App::Ack::Resource->new( $file );
3112 }
3113
3114 1;
3115 package App::Ack::ConfigDefault;
3116
3117 use warnings;
3118 use strict;
3119
3120
3121
3122
3123 sub options {
3124 return split( /\n/, _options_block() );
3125 }
3126
3127
3128 sub options_clean {
3129 return grep { /./ && !/^#/ } options();
3130 }
3131
3132
3133 sub _options_block {
3134 my $lines = <<'HERE';
3135 # This is the default ackrc for ack version ==VERSION==.
3136
3137 # There are four different ways to match
3138 #
3139 # is: Match the filename exactly
3140 #
3141 # ext: Match the extension of the filename exactly
3142 #
3143 # match: Match the filename against a Perl regular expression
3144 #
3145 # firstlinematch: Match the first 250 characters of the first line
3146 # of text against a Perl regular expression. This is only for
3147 # the --type-add option.
3148
3149
3150 ### Directories to ignore
3151
3152 # Bazaar
3153 # http://bazaar.canonical.com/
3154 --ignore-directory=is:.bzr
3155
3156 # Codeville
3157 # http://freecode.com/projects/codeville
3158 --ignore-directory=is:.cdv
3159
3160 # Interface Builder (Xcode)
3161 # http://en.wikipedia.org/wiki/Interface_Builder
3162 --ignore-directory=is:~.dep
3163 --ignore-directory=is:~.dot
3164 --ignore-directory=is:~.nib
3165 --ignore-directory=is:~.plst
3166
3167 # Git
3168 # http://git-scm.com/
3169 --ignore-directory=is:.git
3170 # When using submodules, .git is a file.
3171 --ignore-file=is:.git
3172
3173 # Mercurial
3174 # http://mercurial.selenic.com/
3175 --ignore-directory=is:.hg
3176
3177 # quilt
3178 # http://directory.fsf.org/wiki/Quilt
3179 --ignore-directory=is:.pc
3180
3181 # Subversion
3182 # http://subversion.tigris.org/
3183 --ignore-directory=is:.svn
3184
3185 # Monotone
3186 # http://www.monotone.ca/
3187 --ignore-directory=is:_MTN
3188
3189 # CVS
3190 # http://savannah.nongnu.org/projects/cvs
3191 --ignore-directory=is:CVS
3192
3193 # RCS
3194 # http://www.gnu.org/software/rcs/
3195 --ignore-directory=is:RCS
3196
3197 # SCCS
3198 # http://en.wikipedia.org/wiki/Source_Code_Control_System
3199 --ignore-directory=is:SCCS
3200
3201 # darcs
3202 # http://darcs.net/
3203 --ignore-directory=is:_darcs
3204
3205 # Vault/Fortress
3206 --ignore-directory=is:_sgbak
3207
3208 # autoconf
3209 # http://www.gnu.org/software/autoconf/
3210 --ignore-directory=is:autom4te.cache
3211
3212 # Perl module building
3213 --ignore-directory=is:blib
3214 --ignore-directory=is:_build
3215
3216 # Perl Devel::Cover module's output directory
3217 # https://metacpan.org/release/Devel-Cover
3218 --ignore-directory=is:cover_db
3219
3220 # Node modules created by npm
3221 --ignore-directory=is:node_modules
3222
3223 # CMake cache
3224 # http://www.cmake.org/
3225 --ignore-directory=is:CMakeFiles
3226
3227 # Eclipse workspace folder
3228 # http://eclipse.org/
3229 --ignore-directory=is:.metadata
3230
3231 # Cabal (Haskell) sandboxes
3232 # http://www.haskell.org/cabal/users-guide/installing-packages.html
3233 --ignore-directory=is:.cabal-sandbox
3234
3235 ### Files to ignore
3236
3237 # Backup files
3238 --ignore-file=ext:bak
3239 --ignore-file=match:/~$/
3240
3241 # Emacs swap files
3242 --ignore-file=match:/^#.+#$/
3243
3244 # vi/vim swap files http://vim.org/
3245 --ignore-file=match:/[._].*\.swp$/
3246
3247 # core dumps
3248 --ignore-file=match:/core\.\d+$/
3249
3250 # minified Javascript
3251 --ignore-file=match:/[.-]min[.]js$/
3252 --ignore-file=match:/[.]js[.]min$/
3253
3254 # minified CSS
3255 --ignore-file=match:/[.]min[.]css$/
3256 --ignore-file=match:/[.]css[.]min$/
3257
3258 # JS and CSS source maps
3259 --ignore-file=match:/[.]js[.]map$/
3260 --ignore-file=match:/[.]css[.]map$/
3261
3262 # PDFs, because they pass Perl's -T detection
3263 --ignore-file=ext:pdf
3264
3265 # Common graphics, just as an optimization
3266 --ignore-file=ext:gif,jpg,jpeg,png
3267
3268
3269 ### Filetypes defined
3270
3271 # Perl
3272 # http://perl.org/
3273 --type-add=perl:ext:pl,pm,pod,t,psgi
3274 --type-add=perl:firstlinematch:/^#!.*\bperl/
3275
3276 # Perl tests
3277 --type-add=perltest:ext:t
3278
3279 # Makefiles
3280 # http://www.gnu.org/s/make/
3281 --type-add=make:ext:mk
3282 --type-add=make:ext:mak
3283 --type-add=make:is:makefile
3284 --type-add=make:is:Makefile
3285 --type-add=make:is:Makefile.Debug
3286 --type-add=make:is:Makefile.Release
3287
3288 # Rakefiles
3289 # http://rake.rubyforge.org/
3290 --type-add=rake:is:Rakefile
3291
3292 # CMake
3293 # http://www.cmake.org/
3294 --type-add=cmake:is:CMakeLists.txt
3295 --type-add=cmake:ext:cmake
3296
3297 # Actionscript
3298 --type-add=actionscript:ext:as,mxml
3299
3300 # Ada
3301 # http://www.adaic.org/
3302 --type-add=ada:ext:ada,adb,ads
3303
3304 # ASP
3305 # http://msdn.microsoft.com/en-us/library/aa286483.aspx
3306 --type-add=asp:ext:asp
3307
3308 # ASP.Net
3309 # http://www.asp.net/
3310 --type-add=aspx:ext:master,ascx,asmx,aspx,svc
3311
3312 # Assembly
3313 --type-add=asm:ext:asm,s
3314
3315 # Batch
3316 --type-add=batch:ext:bat,cmd
3317
3318 # ColdFusion
3319 # http://en.wikipedia.org/wiki/ColdFusion
3320 --type-add=cfmx:ext:cfc,cfm,cfml
3321
3322 # Clojure
3323 # http://clojure.org/
3324 --type-add=clojure:ext:clj,cljs,edn,cljc
3325
3326 # C
3327 # .xs are Perl C files
3328 --type-add=cc:ext:c,h,xs
3329
3330 # C header files
3331 --type-add=hh:ext:h
3332
3333 # CoffeeScript
3334 # http://coffeescript.org/
3335 --type-add=coffeescript:ext:coffee
3336
3337 # C++
3338 --type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx
3339
3340 # C++ header files
3341 --type-add=hpp:ext:hpp,hh,h,hxx
3342
3343 # C#
3344 --type-add=csharp:ext:cs
3345
3346 # CSS
3347 # http://www.w3.org/Style/CSS/
3348 --type-add=css:ext:css
3349
3350 # Dart
3351 # http://www.dartlang.org/
3352 --type-add=dart:ext:dart
3353
3354 # Delphi
3355 # http://en.wikipedia.org/wiki/Embarcadero_Delphi
3356 --type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj
3357
3358 # Elixir
3359 # http://elixir-lang.org/
3360 --type-add=elixir:ext:ex,exs
3361
3362 # Emacs Lisp
3363 # http://www.gnu.org/software/emacs
3364 --type-add=elisp:ext:el
3365
3366 # Erlang
3367 # http://www.erlang.org/
3368 --type-add=erlang:ext:erl,hrl
3369
3370 # Fortran
3371 # http://en.wikipedia.org/wiki/Fortran
3372 --type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp
3373
3374 # Go
3375 # http://golang.org/
3376 --type-add=go:ext:go
3377
3378 # Groovy
3379 # http://groovy.codehaus.org/
3380 --type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
3381
3382 # GSP
3383 # http://groovy.codehaus.org/GSP
3384 --type-add=gsp:ext:gsp
3385
3386 # Haskell
3387 # http://www.haskell.org/
3388 --type-add=haskell:ext:hs,lhs
3389
3390 # HTML
3391 --type-add=html:ext:htm,html,xhtml
3392
3393 # Jade
3394 # http://jade-lang.com/
3395 --type-add=jade:ext:jade
3396
3397 # Java
3398 # http://www.oracle.com/technetwork/java/index.html
3399 --type-add=java:ext:java,properties
3400
3401 # JavaScript
3402 --type-add=js:ext:js
3403
3404 # JSP
3405 # http://www.oracle.com/technetwork/java/javaee/jsp/index.html
3406 --type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml
3407
3408 # JSON
3409 # http://www.json.org/
3410 --type-add=json:ext:json
3411
3412 # Kotlin
3413 # https://kotlinlang.org/
3414 --type-add=kotlin:ext:kt,kts
3415
3416 # Less
3417 # http://www.lesscss.org/
3418 --type-add=less:ext:less
3419
3420 # Common Lisp
3421 # http://common-lisp.net/
3422 --type-add=lisp:ext:lisp,lsp
3423
3424 # Lua
3425 # http://www.lua.org/
3426 --type-add=lua:ext:lua
3427 --type-add=lua:firstlinematch:/^#!.*\blua(jit)?/
3428
3429 # Objective-C
3430 --type-add=objc:ext:m,h
3431
3432 # Objective-C++
3433 --type-add=objcpp:ext:mm,h
3434
3435 # OCaml
3436 # http://caml.inria.fr/
3437 --type-add=ocaml:ext:ml,mli,mll,mly
3438
3439 # Matlab
3440 # http://en.wikipedia.org/wiki/MATLAB
3441 --type-add=matlab:ext:m
3442
3443 # Parrot
3444 # http://www.parrot.org/
3445 --type-add=parrot:ext:pir,pasm,pmc,ops,pod,pg,tg
3446
3447 # PHP
3448 # http://www.php.net/
3449 --type-add=php:ext:php,phpt,php3,php4,php5,phtml
3450 --type-add=php:firstlinematch:/^#!.*\bphp/
3451
3452 # Plone
3453 # http://plone.org/
3454 --type-add=plone:ext:pt,cpt,metadata,cpy,py
3455
3456 # Python
3457 # http://www.python.org/
3458 --type-add=python:ext:py
3459 --type-add=python:firstlinematch:/^#!.*\bpython/
3460
3461 # R
3462 # http://www.r-project.org/
3463 --type-add=rr:ext:R
3464
3465 # reStructured Text
3466 # http://docutils.sourceforge.net/rst.html
3467 --type-add=rst:ext:rst
3468
3469 # Ruby
3470 # http://www.ruby-lang.org/
3471 --type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec
3472 --type-add=ruby:is:Rakefile
3473 --type-add=ruby:firstlinematch:/^#!.*\bruby/
3474
3475 # Rust
3476 # http://www.rust-lang.org/
3477 --type-add=rust:ext:rs
3478
3479 # Sass
3480 # http://sass-lang.com
3481 --type-add=sass:ext:sass,scss
3482
3483 # Scala
3484 # http://www.scala-lang.org/
3485 --type-add=scala:ext:scala
3486
3487 # Scheme
3488 # http://groups.csail.mit.edu/mac/projects/scheme/
3489 --type-add=scheme:ext:scm,ss
3490
3491 # Shell
3492 --type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish
3493 --type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/
3494
3495 # Smalltalk
3496 # http://www.smalltalk.org/
3497 --type-add=smalltalk:ext:st
3498
3499 # Smarty
3500 # http://www.smarty.net/
3501 --type-add=smarty:ext:tpl
3502
3503 # SQL
3504 # http://www.iso.org/iso/catalogue_detail.htm?csnumber=45498
3505 --type-add=sql:ext:sql,ctl
3506
3507 # Stylus
3508 # http://learnboost.github.io/stylus/
3509 --type-add=stylus:ext:styl
3510
3511 # Swift
3512 # https://developer.apple.com/swift/
3513 --type-add=swift:ext:swift
3514 --type-add=swift:firstlinematch:/^#!.*\bswift/
3515
3516 # Tcl
3517 # http://www.tcl.tk/
3518 --type-add=tcl:ext:tcl,itcl,itk
3519
3520 # LaTeX
3521 # http://www.latex-project.org/
3522 --type-add=tex:ext:tex,cls,sty
3523
3524 # Template Toolkit (Perl)
3525 # http://template-toolkit.org/
3526 --type-add=tt:ext:tt,tt2,ttml
3527
3528 # Visual Basic
3529 --type-add=vb:ext:bas,cls,frm,ctl,vb,resx
3530
3531 # Verilog
3532 --type-add=verilog:ext:v,vh,sv
3533
3534 # VHDL
3535 # http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome
3536 --type-add=vhdl:ext:vhd,vhdl
3537
3538 # Vim
3539 # http://www.vim.org/
3540 --type-add=vim:ext:vim
3541
3542 # XML
3543 # http://www.w3.org/TR/REC-xml/
3544 --type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl
3545 --type-add=xml:firstlinematch:/<[?]xml/
3546
3547 # YAML
3548 # http://yaml.org/
3549 --type-add=yaml:ext:yaml,yml
3550 HERE
3551 $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;
3552
3553 return $lines;
3554 }
3555
3556 1;
3557 package App::Ack::ConfigFinder;
3558
3559
3560 use strict;
3561 use warnings;
3562
3563 use Cwd 3.00 ();
3564 use File::Spec 3.00;
3565
3566 use if ($^O eq 'MSWin32'), 'Win32';
3567
3568
3569 sub new {
3570 my ( $class ) = @_;
3571
3572 return bless {}, $class;
3573 }
3574
3575
3576 sub _remove_redundancies {
3577 my @configs = @_;
3578
3579 my %seen;
3580 foreach my $config (@configs) {
3581 my $key = $config->{path};
3582 if ( not $App::Ack::is_windows ) {
3583 # On Unix, uniquify on inode.
3584 my ($dev, $inode) = (stat $key)[0, 1];
3585 $key = "$dev:$inode" if defined $dev;
3586 }
3587 undef $config if $seen{$key}++;
3588 }
3589 return grep { defined } @configs;
3590 }
3591
3592
3593 sub _check_for_ackrc {
3594 return unless defined $_[0];
3595
3596 my @files = grep { -f }
3597 map { File::Spec->catfile(@_, $_) }
3598 qw(.ackrc _ackrc);
3599
3600 die File::Spec->catdir(@_) . " contains both .ackrc and _ackrc.\n" .
3601 "Please remove one of those files.\n"
3602 if @files > 1;
3603
3604 return wantarray ? @files : $files[0];
3605 } # end _check_for_ackrc
3606
3607
3608
3609 sub find_config_files {
3610 my @config_files;
3611
3612 if ( $App::Ack::is_windows ) {
3613 push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (
3614 Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),
3615 Win32::GetFolderPath(Win32::CSIDL_APPDATA()),
3616 );
3617 }
3618 else {
3619 push @config_files, { path => '/etc/ackrc' };
3620 }
3621
3622
3623 if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {
3624 push @config_files, { path => $ENV{'ACKRC'} };
3625 }
3626 else {
3627 push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});
3628 }
3629
3630 my $cwd = Cwd::getcwd();
3631 return () unless defined $cwd;
3632
3633 # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this.
3634 $cwd =~ /(.+)/;
3635 $cwd = $1;
3636 my @dirs = File::Spec->splitdir( $cwd );
3637 while(@dirs) {
3638 my $ackrc = _check_for_ackrc(@dirs);
3639 if(defined $ackrc) {
3640 push @config_files, { project => 1, path => $ackrc };
3641 last;
3642 }
3643 pop @dirs;
3644 }
3645
3646 # We only test for existence here, so if the file is deleted out from under us, this will fail later.
3647 return _remove_redundancies( @config_files );
3648 }
3649
3650
3651
3652 sub read_rcfile {
3653 my $file = shift;
3654
3655 return unless defined $file && -e $file;
3656
3657 my @lines;
3658
3659 open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" );
3660 while ( my $line = <$fh> ) {
3661 chomp $line;
3662 $line =~ s/^\s+//;
3663 $line =~ s/\s+$//;
3664
3665 next if $line eq '';
3666 next if $line =~ /^\s*#/;
3667
3668 push( @lines, $line );
3669 }
3670 close $fh or App::Ack::die( "Unable to close $file: $!" );
3671
3672 return @lines;
3673 }
3674
3675 1;
3676 package App::Ack::ConfigLoader;
3677
3678 use strict;
3679 use warnings;
3680
3681 use Carp 1.04 ();
3682 use Getopt::Long 2.38 ();
3683 use Text::ParseWords 3.1 ();
3684
3685
3686 my @INVALID_COMBINATIONS;
3687
3688 BEGIN {
3689 my @context = qw( -A -B -C --after-context --before-context --context );
3690 my @pretty = qw( --heading --group --break );
3691 my @filename = qw( -h -H --with-filename --no-filename );
3692
3693 @INVALID_COMBINATIONS = (
3694 # XXX normalize
3695 [qw(-l)] => [@context, @pretty, @filename, qw(-L -o --passthru --output --max-count --column -f -g --show-types)],
3696 [qw(-L)] => [@context, @pretty, @filename, qw(-l -o --passthru --output --max-count --column -f -g --show-types -c --count)],
3697 [qw(--line)] => [@context, @pretty, @filename, qw(-l --files-with-matches --files-without-matches -L -o --passthru --match -m --max-count -1 -c --count --column --print0 -f -g --show-types)],
3698 [qw(-o)] => [@context, qw(--output -c --count --column --column -f --show-types)],
3699 [qw(--passthru)] => [@context, qw(--output --column -m --max-count -1 -c --count -f -g)],
3700 [qw(--output)] => [@context, qw(-c --count -f -g)],
3701 [qw(--match)] => [qw(-f -g)],
3702 [qw(-m --max-count)] => [qw(-1 -f -g -c --count)],
3703 [qw(-h --no-filename)] => [qw(-H --with-filename -f -g --group --heading)],
3704 [qw(-H --with-filename)] => [qw(-h --no-filename -f -g)],
3705 [qw(-c --count)] => [@context, @pretty, qw(--column -f -g)],
3706 [qw(--column)] => [qw(-f -g)],
3707 [@context] => [qw(-f -g)],
3708 [qw(-f)] => [qw(-g), @pretty],
3709 [qw(-g)] => [qw(-f), @pretty],
3710 );
3711 }
3712
3713 sub _generate_ignore_dir {
3714 my ( $option_name, $opt ) = @_;
3715
3716 my $is_inverted = $option_name =~ /^--no/;
3717
3718 return sub {
3719 my ( undef, $dir ) = @_;
3720
3721 $dir = App::Ack::remove_dir_sep( $dir );
3722 if ( $dir !~ /:/ ) {
3723 $dir = 'is:' . $dir;
3724 }
3725
3726 my ( $filter_type, $args ) = split /:/, $dir, 2;
3727
3728 if ( $filter_type eq 'firstlinematch' ) {
3729 Carp::croak( qq{Invalid filter specification "$filter_type" for option '$option_name'} );
3730 }
3731
3732 my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));
3733 my $collection;
3734
3735 my $previous_inversion_matches = $opt->{idirs} && !($is_inverted xor $opt->{idirs}[-1]->is_inverted());
3736
3737 if ( $previous_inversion_matches ) {
3738 $collection = $opt->{idirs}[-1];
3739
3740 if ( $is_inverted ) {
3741 # XXX this relies on invert of an inverted filter
3742 # to return the original
3743 $collection = $collection->invert()
3744 }
3745 }
3746 else {
3747 $collection = App::Ack::Filter::Collection->new();
3748
3749 if ( $is_inverted ) {
3750 push @{ $opt->{idirs} }, $collection->invert();
3751 }
3752 else {
3753 push @{ $opt->{idirs} }, $collection;
3754 }
3755 }
3756
3757 $collection->add($filter);
3758
3759 if ( $filter_type eq 'is' ) {
3760 $collection->add(App::Ack::Filter::IsPath->new($args));
3761 }
3762 };
3763 }
3764
3765 sub process_filter_spec {
3766 my ( $spec ) = @_;
3767
3768 if ( $spec =~ /^(\w+):(\w+):(.*)/ ) {
3769 my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 );
3770
3771 return ( $type_name,
3772 App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) );
3773 }
3774 elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification.
3775 my ( $type_name, $extensions ) = ( $1, $2 );
3776
3777 my @extensions = split(/,/, $extensions);
3778 foreach my $extension ( @extensions ) {
3779 $extension =~ s/^[.]//;
3780 }
3781
3782 return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) );
3783 }
3784 else {
3785 Carp::croak "invalid filter specification '$spec'";
3786 }
3787 }
3788
3789
3790 sub uninvert_filter {
3791 my ( $opt, @filters ) = @_;
3792
3793 return unless defined $opt->{filters} && @filters;
3794
3795 # Loop through all the registered filters. If we hit one that
3796 # matches this extension and it's inverted, we need to delete it from
3797 # the options.
3798 for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) {
3799 my $opt_filter = @{ $opt->{filters} }[$i];
3800
3801 # XXX Do a real list comparison? This just checks string equivalence.
3802 if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) {
3803 splice @{ $opt->{filters} }, $i, 1;
3804 $i--;
3805 }
3806 }
3807
3808 return;
3809 }
3810
3811
3812 sub process_filetypes {
3813 my ( $opt, $arg_sources ) = @_;
3814
3815 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version'); # start with default options, minus some annoying ones
3816 Getopt::Long::Configure(
3817 'no_ignore_case',
3818 'no_auto_abbrev',
3819 'pass_through',
3820 );
3821 my %additional_specs;
3822
3823 my $add_spec = sub {
3824 my ( undef, $spec ) = @_;
3825
3826 my ( $name, $filter ) = process_filter_spec($spec);
3827
3828 push @{ $App::Ack::mappings{$name} }, $filter;
3829
3830 $additional_specs{$name . '!'} = sub {
3831 my ( undef, $value ) = @_;
3832
3833 my @filters = @{ $App::Ack::mappings{$name} };
3834 if ( not $value ) {
3835 @filters = map { $_->invert() } @filters;
3836 }
3837 else {
3838 uninvert_filter( $opt, @filters );
3839 }
3840
3841 push @{ $opt->{'filters'} }, @filters;
3842 };
3843 };
3844
3845 my $set_spec = sub {
3846 my ( undef, $spec ) = @_;
3847
3848 my ( $name, $filter ) = process_filter_spec($spec);
3849
3850 $App::Ack::mappings{$name} = [ $filter ];
3851
3852 $additional_specs{$name . '!'} = sub {
3853 my ( undef, $value ) = @_;
3854
3855 my @filters = @{ $App::Ack::mappings{$name} };
3856 if ( not $value ) {
3857 @filters = map { $_->invert() } @filters;
3858 }
3859
3860 push @{ $opt->{'filters'} }, @filters;
3861 };
3862 };
3863
3864 my $delete_spec = sub {
3865 my ( undef, $name ) = @_;
3866
3867 delete $App::Ack::mappings{$name};
3868 delete $additional_specs{$name . '!'};
3869 };
3870
3871 my %type_arg_specs = (
3872 'type-add=s' => $add_spec,
3873 'type-set=s' => $set_spec,
3874 'type-del=s' => $delete_spec,
3875 );
3876
3877 foreach my $source (@{$arg_sources}) {
3878 my ( $source_name, $args ) = @{$source}{qw/name contents/};
3879
3880 if ( ref($args) ) {
3881 # $args are modified in place, so no need to munge $arg_sources
3882 local @ARGV = @{$args};
3883 Getopt::Long::GetOptions(%type_arg_specs);
3884 @{$args} = @ARGV;
3885 }
3886 else {
3887 ( undef, $source->{contents} ) =
3888 Getopt::Long::GetOptionsFromString($args, %type_arg_specs);
3889 }
3890 }
3891
3892 $additional_specs{'k|known-types'} = sub {
3893 my ( undef, $value ) = @_;
3894
3895 my @filters = map { @{$_} } values(%App::Ack::mappings);
3896
3897 push @{ $opt->{'filters'} }, @filters;
3898 };
3899
3900 return \%additional_specs;
3901 }
3902
3903
3904 sub removed_option {
3905 my ( $option, $explanation ) = @_;
3906
3907 $explanation ||= '';
3908 return sub {
3909 warn "Option '$option' is not valid in ack 2.\n$explanation";
3910 exit 1;
3911 };
3912 }
3913
3914
3915 sub get_arg_spec {
3916 my ( $opt, $extra_specs ) = @_;
3917
3918 my $dash_a_explanation = <<'EOT';
3919 You don't need -a, ack 1.x users. This is because ack 2.x has
3920 -k/--known-types which makes it only select files of known types, rather
3921 than any text file (which is the behavior of ack 1.x).
3922
3923 If you're surprised to see this message because you didn't put -a on the
3924 command line, you may have options in an .ackrc, or in the ACKRC_OPTIONS
3925 environment variable. Try using the --dump flag to help find it.
3926 EOT
3927
3928
3929 sub _context_value {
3930 my $val = shift;
3931
3932 # Contexts default to 2.
3933 return (!defined($val) || ($val < 0)) ? 2 : $val;
3934 }
3935
3936 return {
3937 1 => sub { $opt->{1} = $opt->{m} = 1 },
3938 'A|after-context:-1' => sub { shift; $opt->{after_context} = _context_value(shift) },
3939 'B|before-context:-1' => sub { shift; $opt->{before_context} = _context_value(shift) },
3940 'C|context:-1' => sub { shift; $opt->{before_context} = $opt->{after_context} = _context_value(shift) },
3941 'a' => removed_option('-a', $dash_a_explanation),
3942 'all' => removed_option('--all', $dash_a_explanation),
3943 'break!' => \$opt->{break},
3944 c => \$opt->{count},
3945 'color|colour!' => \$opt->{color},
3946 'color-match=s' => \$ENV{ACK_COLOR_MATCH},
3947 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},
3948 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},
3949 'column!' => \$opt->{column},
3950 count => \$opt->{count},
3951 'create-ackrc' => sub { print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; },
3952 'env!' => sub {
3953 my ( undef, $value ) = @_;
3954
3955 if ( !$value ) {
3956 $opt->{noenv_seen} = 1;
3957 }
3958 },
3959 f => \$opt->{f},
3960 'files-from=s' => \$opt->{files_from},
3961 'filter!' => \$App::Ack::is_filter_mode,
3962 flush => \$opt->{flush},
3963 'follow!' => \$opt->{follow},
3964 g => \$opt->{g},
3965 G => removed_option('-G'),
3966 'group!' => sub { shift; $opt->{heading} = $opt->{break} = shift },
3967 'heading!' => \$opt->{heading},
3968 'h|no-filename' => \$opt->{h},
3969 'H|with-filename' => \$opt->{H},
3970 'i|ignore-case' => \$opt->{i},
3971 'ignore-directory|ignore-dir=s' => _generate_ignore_dir('--ignore-dir', $opt),
3972 'ignore-file=s' => sub {
3973 my ( undef, $file ) = @_;
3974
3975 my ( $filter_type, $args ) = split /:/, $file, 2;
3976
3977 my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args));
3978
3979 if ( !$opt->{ifiles} ) {
3980 $opt->{ifiles} = App::Ack::Filter::Collection->new();
3981 }
3982 $opt->{ifiles}->add($filter);
3983 },
3984 'lines=s' => sub { shift; my $val = shift; push @{$opt->{lines}}, $val },
3985 'l|files-with-matches'
3986 => \$opt->{l},
3987 'L|files-without-matches'
3988 => \$opt->{L},
3989 'm|max-count=i' => \$opt->{m},
3990 'match=s' => \$opt->{regex},
3991 'n|no-recurse' => \$opt->{n},
3992 o => sub { $opt->{output} = '$&' },
3993 'output=s' => \$opt->{output},
3994 'pager:s' => sub {
3995 my ( undef, $value ) = @_;
3996
3997 $opt->{pager} = $value || $ENV{PAGER};
3998 },
3999 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt),
4000 'nopager' => sub { $opt->{pager} = undef },
4001 'passthru' => \$opt->{passthru},
4002 'print0' => \$opt->{print0},
4003 'Q|literal' => \$opt->{Q},
4004 'r|R|recurse' => sub { $opt->{n} = 0 },
4005 's' => \$opt->{dont_report_bad_filenames},
4006 'show-types' => \$opt->{show_types},
4007 'smart-case!' => \$opt->{smart_case},
4008 'sort-files' => \$opt->{sort_files},
4009 'type=s' => sub {
4010 my ( $getopt, $value ) = @_;
4011
4012 my $cb_value = 1;
4013 if ( $value =~ s/^no// ) {
4014 $cb_value = 0;
4015 }
4016
4017 my $callback = $extra_specs->{ $value . '!' };
4018
4019 if ( $callback ) {
4020 $callback->( $getopt, $cb_value );
4021 }
4022 else {
4023 Carp::croak( "Unknown type '$value'" );
4024 }
4025 },
4026 'u' => removed_option('-u'),
4027 'unrestricted' => removed_option('--unrestricted'),
4028 'v|invert-match' => \$opt->{v},
4029 'w|word-regexp' => \$opt->{w},
4030 'x' => sub { $opt->{files_from} = '-' },
4031
4032 'version' => sub { App::Ack::print_version_statement(); exit; },
4033 'help|?:s' => sub { shift; App::Ack::show_help(@_); exit; },
4034 'help-types' => sub { App::Ack::show_help_types(); exit; },
4035 'man' => sub { App::Ack::show_man(); exit; },
4036 $extra_specs ? %{$extra_specs} : (),
4037 }; # arg_specs
4038 }
4039
4040
4041 sub process_other {
4042 my ( $opt, $extra_specs, $arg_sources ) = @_;
4043
4044 # Start with default options, minus some annoying ones.
4045 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
4046 Getopt::Long::Configure(
4047 'bundling',
4048 'no_ignore_case',
4049 );
4050
4051 my $argv_source;
4052 my $is_help_types_active;
4053
4054 foreach my $source (@{$arg_sources}) {
4055 my ( $source_name, $args ) = @{$source}{qw/name contents/};
4056
4057 if ( $source_name eq 'ARGV' ) {
4058 $argv_source = $args;
4059 last;
4060 }
4061 }
4062
4063 if ( $argv_source ) { # This *should* always be true, but you never know...
4064 my @copy = @{$argv_source};
4065 local @ARGV = @copy;
4066
4067 Getopt::Long::Configure('pass_through');
4068
4069 Getopt::Long::GetOptions(
4070 'help-types' => \$is_help_types_active,
4071 );
4072
4073 Getopt::Long::Configure('no_pass_through');
4074 }
4075
4076 my $arg_specs = get_arg_spec($opt, $extra_specs);
4077
4078 foreach my $source (@{$arg_sources}) {
4079 my ( $source_name, $args ) = @{$source}{qw/name contents/};
4080
4081 my $args_for_source = $arg_specs;
4082
4083 if ( $source->{project} ) {
4084 my $illegal = sub {
4085 die "Options --output, --pager and --match are forbidden in project .ackrc files.\n";
4086 };
4087
4088 $args_for_source = {
4089 %{$args_for_source},
4090 'output=s' => $illegal,
4091 'pager:s' => $illegal,
4092 'match=s' => $illegal,
4093 };
4094 }
4095
4096 my $ret;
4097 if ( ref($args) ) {
4098 local @ARGV = @{$args};
4099 $ret = Getopt::Long::GetOptions( %{$args_for_source} );
4100 @{$args} = @ARGV;
4101 }
4102 else {
4103 ( $ret, $source->{contents} ) =
4104 Getopt::Long::GetOptionsFromString( $args, %{$args_for_source} );
4105 }
4106 if ( !$ret ) {
4107 if ( !$is_help_types_active ) {
4108 my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name";
4109 App::Ack::die( "Invalid option $where" );
4110 }
4111 }
4112 if ( $opt->{noenv_seen} ) {
4113 App::Ack::die( "--noenv found in $source_name" );
4114 }
4115 }
4116
4117 # XXX We need to check on a -- in the middle of a non-ARGV source
4118
4119 return;
4120 }
4121
4122
4123 sub should_dump_options {
4124 my ( $sources ) = @_;
4125
4126 foreach my $source (@{$sources}) {
4127 my ( $name, $options ) = @{$source}{qw/name contents/};
4128
4129 if($name eq 'ARGV') {
4130 my $dump;
4131 local @ARGV = @{$options};
4132 Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');
4133 Getopt::Long::GetOptions(
4134 'dump' => \$dump,
4135 );
4136 @{$options} = @ARGV;
4137 return $dump;
4138 }
4139 }
4140 return;
4141 }
4142
4143
4144 sub explode_sources {
4145 my ( $sources ) = @_;
4146
4147 my @new_sources;
4148
4149 Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');
4150
4151 my %opt;
4152 my $arg_spec = get_arg_spec(\%opt);
4153
4154 my $add_type = sub {
4155 my ( undef, $arg ) = @_;
4156
4157 # XXX refactor?
4158 if ( $arg =~ /(\w+)=/) {
4159 $arg_spec->{$1} = sub {};
4160 }
4161 else {
4162 ( $arg ) = split /:/, $arg;
4163 $arg_spec->{$arg} = sub {};
4164 }
4165 };
4166
4167 my $del_type = sub {
4168 my ( undef, $arg ) = @_;
4169
4170 delete $arg_spec->{$arg};
4171 };
4172
4173 foreach my $source (@{$sources}) {
4174 my ( $name, $options ) = @{$source}{qw/name contents/};
4175 if ( ref($options) ne 'ARRAY' ) {
4176 $source->{contents} = $options =
4177 [ Text::ParseWords::shellwords($options) ];
4178 }
4179
4180 for my $j ( 0 .. @{$options}-1 ) {
4181 next unless $options->[$j] =~ /^-/;
4182 my @chunk = ( $options->[$j] );
4183 push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/;
4184 $j--;
4185
4186 my @copy = @chunk;
4187 local @ARGV = @chunk;
4188 Getopt::Long::GetOptions(
4189 'type-add=s' => $add_type,
4190 'type-set=s' => $add_type,
4191 'type-del=s' => $del_type,
4192 );
4193 Getopt::Long::GetOptions( %{$arg_spec} );
4194
4195 push @new_sources, {
4196 name => $name,
4197 contents => \@copy,
4198 };
4199 }
4200 }
4201
4202 return \@new_sources;
4203 }
4204
4205
4206 sub compare_opts {
4207 my ( $a, $b ) = @_;
4208
4209 my $first_a = $a->[0];
4210 my $first_b = $b->[0];
4211
4212 $first_a =~ s/^--?//;
4213 $first_b =~ s/^--?//;
4214
4215 return $first_a cmp $first_b;
4216 }
4217
4218
4219 sub dump_options {
4220 my ( $sources ) = @_;
4221
4222 $sources = explode_sources($sources);
4223
4224 my %opts_by_source;
4225 my @source_names;
4226
4227 foreach my $source (@{$sources}) {
4228 my ( $name, $contents ) = @{$source}{qw/name contents/};
4229 if ( not $opts_by_source{$name} ) {
4230 $opts_by_source{$name} = [];
4231 push @source_names, $name;
4232 }
4233 push @{$opts_by_source{$name}}, $contents;
4234 }
4235
4236 foreach my $name (@source_names) {
4237 my $contents = $opts_by_source{$name};
4238
4239 print $name, "\n";
4240 print '=' x length($name), "\n";
4241 print ' ', join(' ', @{$_}), "\n" foreach sort { compare_opts($a, $b) } @{$contents};
4242 }
4243
4244 return;
4245 }
4246
4247
4248 sub remove_default_options_if_needed {
4249 my ( $sources ) = @_;
4250
4251 my $default_index;
4252
4253 foreach my $index ( 0 .. $#{$sources} ) {
4254 if ( $sources->[$index]{'name'} eq 'Defaults' ) {
4255 $default_index = $index;
4256 last;
4257 }
4258 }
4259
4260 return $sources unless defined $default_index;
4261
4262 my $should_remove = 0;
4263
4264 # Start with default options, minus some annoying ones.
4265 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
4266 Getopt::Long::Configure(
4267 'no_ignore_case',
4268 'no_auto_abbrev',
4269 'pass_through',
4270 );
4271
4272 foreach my $index ( $default_index + 1 .. $#{$sources} ) {
4273 my ( $name, $args ) = @{$sources->[$index]}{qw/name contents/};
4274
4275 if (ref($args)) {
4276 local @ARGV = @{$args};
4277 Getopt::Long::GetOptions(
4278 'ignore-ack-defaults' => \$should_remove,
4279 );
4280 @{$args} = @ARGV;
4281 }
4282 else {
4283 ( undef, $sources->[$index]{contents} ) = Getopt::Long::GetOptionsFromString($args,
4284 'ignore-ack-defaults' => \$should_remove,
4285 );
4286 }
4287 }
4288
4289 Getopt::Long::Configure('default');
4290 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
4291
4292 return $sources unless $should_remove;
4293
4294 my @copy = @{$sources};
4295 splice @copy, $default_index, 1;
4296 return \@copy;
4297 }
4298
4299
4300 sub check_for_mutually_exclusive_options {
4301 my ( $arg_sources ) = @_;
4302
4303 my %mutually_exclusive_with;
4304 my @copy = @{$arg_sources};
4305
4306 for(my $i = 0; $i < @INVALID_COMBINATIONS; $i += 2) {
4307 my ( $lhs, $rhs ) = @INVALID_COMBINATIONS[ $i, $i + 1 ];
4308
4309 foreach my $l_opt ( @{$lhs} ) {
4310 foreach my $r_opt ( @{$rhs} ) {
4311 push @{ $mutually_exclusive_with{ $l_opt } }, $r_opt;
4312 push @{ $mutually_exclusive_with{ $r_opt } }, $l_opt;
4313 }
4314 }
4315 }
4316
4317 while( @copy ) {
4318 my %set_opts;
4319
4320 my $source = shift @copy;
4321 my ( $source_name, $args ) = @{$source}{qw/name contents/};
4322 $args = ref($args) ? [ @{$args} ] : [ Text::ParseWords::shellwords($args) ];
4323
4324 foreach my $opt ( @{$args} ) {
4325 next unless $opt =~ /^[-+]/;
4326 last if $opt eq '--';
4327
4328 if( $opt =~ /^(.*)=/ ) {
4329 $opt = $1;
4330 }
4331 elsif ( $opt =~ /^(-[^-]).+/ ) {
4332 $opt = $1;
4333 }
4334
4335 $set_opts{ $opt } = 1;
4336
4337 my $mutex_opts = $mutually_exclusive_with{ $opt };
4338
4339 next unless $mutex_opts;
4340
4341 foreach my $mutex_opt ( @{$mutex_opts} ) {
4342 if($set_opts{ $mutex_opt }) {
4343 die "Options '$mutex_opt' and '$opt' are mutually exclusive\n";
4344 }
4345 }
4346 }
4347 }
4348
4349 return;
4350 }
4351
4352
4353 sub process_args {
4354 my $arg_sources = \@_;
4355
4356 my %opt = (
4357 pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
4358 );
4359
4360 check_for_mutually_exclusive_options($arg_sources);
4361
4362 $arg_sources = remove_default_options_if_needed($arg_sources);
4363
4364 if ( should_dump_options($arg_sources) ) {
4365 dump_options($arg_sources);
4366 exit(0);
4367 }
4368
4369 my $type_specs = process_filetypes(\%opt, $arg_sources);
4370 process_other(\%opt, $type_specs, $arg_sources);
4371 while ( @{$arg_sources} ) {
4372 my $source = shift @{$arg_sources};
4373 my ( $source_name, $args ) = @{$source}{qw/name contents/};
4374
4375 # All of our sources should be transformed into an array ref
4376 if ( ref($args) ) {
4377 if ( $source_name eq 'ARGV' ) {
4378 @ARGV = @{$args};
4379 }
4380 elsif (@{$args}) {
4381 Carp::croak "source '$source_name' has extra arguments!";
4382 }
4383 }
4384 else {
4385 Carp::croak 'The impossible has occurred!';
4386 }
4387 }
4388 my $filters = ($opt{filters} ||= []);
4389
4390 # Throw the default filter in if no others are selected.
4391 if ( not grep { !$_->is_inverted() } @{$filters} ) {
4392 push @{$filters}, App::Ack::Filter::Default->new();
4393 }
4394 return \%opt;
4395 }
4396
4397
4398 sub retrieve_arg_sources {
4399 my @arg_sources;
4400
4401 my $noenv;
4402 my $ackrc;
4403
4404 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
4405 Getopt::Long::Configure('pass_through');
4406 Getopt::Long::Configure('no_auto_abbrev');
4407
4408 Getopt::Long::GetOptions(
4409 'noenv' => \$noenv,
4410 'ackrc=s' => \$ackrc,
4411 );
4412
4413 Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
4414
4415 my @files;
4416
4417 if ( !$noenv ) {
4418 my $finder = App::Ack::ConfigFinder->new;
4419 @files = $finder->find_config_files;
4420 }
4421 if ( $ackrc ) {
4422 # We explicitly use open so we get a nice error message.
4423 # XXX This is a potential race condition!.
4424 if(open my $fh, '<', $ackrc) {
4425 close $fh;
4426 }
4427 else {
4428 die "Unable to load ackrc '$ackrc': $!"
4429 }
4430 push( @files, { path => $ackrc } );
4431 }
4432
4433 push @arg_sources, {
4434 name => 'Defaults',
4435 contents => [ App::Ack::ConfigDefault::options_clean() ],
4436 };
4437
4438 foreach my $file ( @files) {
4439 my @lines = App::Ack::ConfigFinder::read_rcfile($file->{path});
4440
4441 if(@lines) {
4442 push @arg_sources, {
4443 name => $file->{path},
4444 contents => \@lines,
4445 project => $file->{project},
4446 };
4447 }
4448 }
4449
4450 if ( $ENV{ACK_OPTIONS} && !$noenv ) {
4451 push @arg_sources, {
4452 name => 'ACK_OPTIONS',
4453 contents => $ENV{ACK_OPTIONS},
4454 };
4455 }
4456
4457 push @arg_sources, {
4458 name => 'ARGV',
4459 contents => [ @ARGV ],
4460 };
4461
4462 return @arg_sources;
4463 }
4464
4465 1; # End of App::Ack::ConfigLoader
4466 package App::Ack::Filter;
4467
4468
4469 use strict;
4470 use warnings;
4471
4472 use Carp 1.04 ();
4473
4474 my %filter_types;
4475
4476
4477 sub create_filter {
4478 my ( undef, $type, @args ) = @_;
4479
4480 if ( my $package = $filter_types{$type} ) {
4481 return $package->new(@args);
4482 }
4483 Carp::croak "Unknown filter type '$type'";
4484 }
4485
4486
4487 sub register_filter {
4488 my ( undef, $type, $package ) = @_;
4489
4490 $filter_types{$type} = $package;
4491
4492 return;
4493 }
4494
4495
4496 sub invert {
4497 my ( $self ) = @_;
4498
4499 return App::Ack::Filter::Inverse->new( $self );
4500 }
4501
4502
4503 sub is_inverted {
4504 return 0;
4505 }
4506
4507
4508 sub to_string {
4509 my ( $self ) = @_;
4510
4511 return '(unimplemented to_string)';
4512 }
4513
4514
4515 sub inspect {
4516 my ( $self ) = @_;
4517
4518 return ref($self);
4519 }
4520
4521 1;
4522 package App::Ack::Filter::Extension;
4523
4524
4525 use strict;
4526 use warnings;
4527 BEGIN {
4528 our @ISA = 'App::Ack::Filter';
4529 }
4530
4531
4532 sub new {
4533 my ( $class, @extensions ) = @_;
4534
4535 my $exts = join('|', map { "\Q$_\E"} @extensions);
4536 my $re = qr/[.](?:$exts)$/i;
4537
4538 return bless {
4539 extensions => \@extensions,
4540 regex => $re,
4541 groupname => 'ExtensionGroup',
4542 }, $class;
4543 }
4544
4545 sub create_group {
4546 return App::Ack::Filter::ExtensionGroup->new();
4547 }
4548
4549 sub filter {
4550 my ( $self, $resource ) = @_;
4551
4552 my $re = $self->{'regex'};
4553
4554 return $resource->name =~ /$re/;
4555 }
4556
4557 sub inspect {
4558 my ( $self ) = @_;
4559
4560 my $re = $self->{'regex'};
4561
4562 return ref($self) . " - $re";
4563 }
4564
4565 sub to_string {
4566 my ( $self ) = @_;
4567
4568 my $exts = $self->{'extensions'};
4569
4570 return join(' ', map { ".$_" } @{$exts});
4571 }
4572
4573 BEGIN {
4574 App::Ack::Filter->register_filter(ext => __PACKAGE__);
4575 }
4576
4577 1;
4578 package App::Ack::Filter::FirstLineMatch;
4579
4580
4581
4582 use strict;
4583 use warnings;
4584 BEGIN {
4585 our @ISA = 'App::Ack::Filter';
4586 }
4587
4588 sub new {
4589 my ( $class, $re ) = @_;
4590
4591 $re =~ s{^/|/$}{}g; # XXX validate?
4592 $re = qr{$re}i;
4593
4594 return bless {
4595 regex => $re,
4596 }, $class;
4597 }
4598
4599 # This test reads the first 250 characters of a file, then just uses the
4600 # first line found in that. This prevents reading something like an entire
4601 # .min.js file (which might be only one "line" long) into memory.
4602
4603 sub filter {
4604 my ( $self, $resource ) = @_;
4605
4606 my $re = $self->{'regex'};
4607
4608 my $line = $resource->firstliney;
4609
4610 return $line =~ /$re/;
4611 }
4612
4613 sub inspect {
4614 my ( $self ) = @_;
4615
4616 my $re = $self->{'regex'};
4617
4618 return ref($self) . " - $re";
4619 }
4620
4621 sub to_string {
4622 my ( $self ) = @_;
4623
4624 (my $re = $self->{regex}) =~ s{\([^:]*:(.*)\)$}{$1};
4625
4626 return "first line matches /$re/";
4627 }
4628
4629 BEGIN {
4630 App::Ack::Filter->register_filter(firstlinematch => __PACKAGE__);
4631 }
4632
4633 1;
4634 package App::Ack::Filter::Is;
4635
4636
4637 use strict;
4638 use warnings;
4639 BEGIN {
4640 our @ISA = 'App::Ack::Filter';
4641 }
4642
4643 use File::Spec 3.00 ();
4644
4645 sub new {
4646 my ( $class, $filename ) = @_;
4647
4648 return bless {
4649 filename => $filename,
4650 groupname => 'IsGroup',
4651 }, $class;
4652 }
4653
4654 sub create_group {
4655 return App::Ack::Filter::IsGroup->new();
4656 }
4657
4658 sub filter {
4659 my ( $self, $resource ) = @_;
4660
4661 my $filename = $self->{'filename'};
4662 my $base = (File::Spec->splitpath($resource->name))[2];
4663
4664 return $base eq $filename;
4665 }
4666
4667 sub inspect {
4668 my ( $self ) = @_;
4669
4670 my $filename = $self->{'filename'};
4671
4672 return ref($self) . " - $filename";
4673 }
4674
4675 sub to_string {
4676 my ( $self ) = @_;
4677
4678 my $filename = $self->{'filename'};
4679
4680 return $filename;
4681 }
4682
4683 BEGIN {
4684 App::Ack::Filter->register_filter(is => __PACKAGE__);
4685 }
4686
4687 1;
4688 package App::Ack::Filter::Match;
4689
4690 use strict;
4691 use warnings;
4692 BEGIN {
4693 our @ISA = 'App::Ack::Filter';
4694 }
4695
4696 use File::Spec 3.00;
4697
4698
4699 sub new {
4700 my ( $class, $re ) = @_;
4701
4702 $re =~ s{^/|/$}{}g; # XXX validate?
4703 $re = qr/$re/i;
4704
4705 return bless {
4706 regex => $re,
4707 groupname => 'MatchGroup',
4708 }, $class;
4709 }
4710
4711 sub create_group {
4712 return App::Ack::Filter::MatchGroup->new;
4713 }
4714
4715 sub filter {
4716 my ( $self, $resource ) = @_;
4717
4718 my $re = $self->{'regex'};
4719
4720 return $resource->basename =~ /$re/;
4721 }
4722
4723 sub inspect {
4724 my ( $self ) = @_;
4725
4726 my $re = $self->{'regex'};
4727
4728 print ref($self) . " - $re";
4729
4730 return;
4731 }
4732
4733 sub to_string {
4734 my ( $self ) = @_;
4735
4736 my $re = $self->{'regex'};
4737
4738 return "filename matches $re";
4739 }
4740
4741 BEGIN {
4742 App::Ack::Filter->register_filter(match => __PACKAGE__);
4743 }
4744
4745 1;
4746 package App::Ack::Filter::Default;
4747
4748
4749 use strict;
4750 use warnings;
4751 BEGIN {
4752 our @ISA = 'App::Ack::Filter';
4753 }
4754
4755 sub new {
4756 my ( $class ) = @_;
4757
4758 return bless {}, $class;
4759 }
4760
4761 sub filter {
4762 my ( $self, $resource ) = @_;
4763
4764 return -T $resource->name;
4765 }
4766
4767 1;
4768 package App::Ack::Filter::Inverse;
4769
4770
4771
4772 use strict;
4773 use warnings;
4774 BEGIN {
4775 our @ISA = 'App::Ack::Filter';
4776 }
4777
4778 sub new {
4779 my ( $class, $filter ) = @_;
4780
4781 return bless {
4782 filter => $filter,
4783 }, $class;
4784 }
4785
4786 sub filter {
4787 my ( $self, $resource ) = @_;
4788
4789 my $filter = $self->{'filter'};
4790 return !$filter->filter( $resource );
4791 }
4792
4793 sub invert {
4794 my $self = shift;
4795
4796 return $self->{'filter'};
4797 }
4798
4799 sub is_inverted {
4800 return 1;
4801 }
4802
4803 sub inspect {
4804 my ( $self ) = @_;
4805
4806 my $filter = $self->{'filter'};
4807
4808 return "!$filter";
4809 }
4810
4811 1;
4812 package App::Ack::Filter::Collection;
4813
4814
4815 use strict;
4816 use warnings;
4817 BEGIN {
4818 our @ISA = 'App::Ack::Filter';
4819 }
4820
4821 sub new {
4822 my ( $class ) = @_;
4823
4824 return bless {
4825 groups => {},
4826 ungrouped => [],
4827 }, $class;
4828 }
4829
4830 sub filter {
4831 my ( $self, $resource ) = @_;
4832
4833 for my $group (values %{$self->{'groups'}}) {
4834 if ($group->filter($resource)) {
4835 return 1;
4836 }
4837 }
4838
4839 for my $filter (@{$self->{'ungrouped'}}) {
4840 if ($filter->filter($resource)) {
4841 return 1;
4842 }
4843 }
4844
4845 return 0;
4846 }
4847
4848 sub add {
4849 my ( $self, $filter ) = @_;
4850
4851 if (exists $filter->{'groupname'}) {
4852 my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group());
4853 $group->add($filter);
4854 }
4855 else {
4856 push @{$self->{'ungrouped'}}, $filter;
4857 }
4858
4859 return;
4860 }
4861
4862 sub inspect {
4863 my ( $self ) = @_;
4864
4865 return ref($self) . " - $self";
4866 }
4867
4868 sub to_string {
4869 my ( $self ) = @_;
4870
4871 my $ungrouped = $self->{'ungrouped'};
4872
4873 return join(', ', map { "($_)" } @{$ungrouped});
4874 }
4875
4876 1;
4877 package App::Ack::Filter::IsGroup;
4878
4879
4880 use strict;
4881 use warnings;
4882 BEGIN {
4883 our @ISA = 'App::Ack::Filter';
4884 }
4885
4886 use File::Spec 3.00 ();
4887
4888 sub new {
4889 my ( $class ) = @_;
4890
4891 return bless {
4892 data => {},
4893 }, $class;
4894 }
4895
4896 sub add {
4897 my ( $self, $filter ) = @_;
4898
4899 $self->{data}->{ $filter->{filename} } = 1;
4900
4901 return;
4902 }
4903
4904 sub filter {
4905 my ( $self, $resource ) = @_;
4906
4907 my $data = $self->{'data'};
4908 my $base = $resource->basename;
4909
4910 return exists $data->{$base};
4911 }
4912
4913 sub inspect {
4914 my ( $self ) = @_;
4915
4916 return ref($self) . " - $self";
4917 }
4918
4919 sub to_string {
4920 my ( $self ) = @_;
4921
4922 return join(' ', keys %{$self->{data}});
4923 }
4924
4925 1;
4926 package App::Ack::Filter::ExtensionGroup;
4927
4928
4929 use strict;
4930 use warnings;
4931 BEGIN {
4932 our @ISA = 'App::Ack::Filter';
4933 }
4934
4935 sub new {
4936 my ( $class ) = @_;
4937
4938 return bless {
4939 data => {},
4940 }, $class;
4941 }
4942
4943 sub add {
4944 my ( $self, $filter ) = @_;
4945
4946 foreach my $ext (@{$filter->{extensions}}) {
4947 $self->{data}->{lc $ext} = 1;
4948 }
4949
4950 return;
4951 }
4952
4953 sub filter {
4954 my ( $self, $resource ) = @_;
4955
4956 if ($resource->name =~ /[.]([^.]*)$/) {
4957 return exists $self->{'data'}->{lc $1};
4958 }
4959
4960 return 0;
4961 }
4962
4963 sub inspect {
4964 my ( $self ) = @_;
4965
4966 return ref($self) . " - $self";
4967 }
4968
4969 sub to_string {
4970 my ( $self ) = @_;
4971
4972 return join(' ', map { ".$_" } sort keys %{$self->{data}});
4973 }
4974
4975 1;
4976 package App::Ack::Filter::MatchGroup;
4977
4978
4979 use strict;
4980 use warnings;
4981 BEGIN {
4982 our @ISA = 'App::Ack::Filter';
4983 }
4984
4985 sub new {
4986 my ( $class ) = @_;
4987
4988 return bless {
4989 matches => [],
4990 big_re => undef,
4991 }, $class;
4992 }
4993
4994 sub add {
4995 my ( $self, $filter ) = @_;
4996
4997 push @{ $self->{matches} }, $filter->{regex};
4998
4999 my $re = join('|', map { "(?:$_)" } @{ $self->{matches} });
5000 $self->{big_re} = qr/$re/;
5001
5002 return;
5003 }
5004
5005 sub filter {
5006 my ( $self, $resource ) = @_;
5007
5008 my $re = $self->{big_re};
5009
5010 return $resource->basename =~ /$re/;
5011 }
5012
5013 sub inspect {
5014 my ( $self ) = @_;
5015
5016 # XXX Needs an explicit return.
5017 }
5018
5019 sub to_string {
5020 my ( $self ) = @_;
5021
5022 # XXX Needs an explicit return.
5023 }
5024
5025 1;
5026 package App::Ack::Filter::IsPath;
5027
5028
5029 use strict;
5030 use warnings;
5031 BEGIN {
5032 our @ISA = 'App::Ack::Filter';
5033 }
5034
5035
5036 sub new {
5037 my ( $class, $filename ) = @_;
5038
5039 return bless {
5040 filename => $filename,
5041 groupname => 'IsPathGroup',
5042 }, $class;
5043 }
5044
5045 sub create_group {
5046 return App::Ack::Filter::IsPathGroup->new();
5047 }
5048
5049 sub filter {
5050 my ( $self, $resource ) = @_;
5051
5052 return $resource->name eq $self->{'filename'};
5053 }
5054
5055 sub inspect {
5056 my ( $self ) = @_;
5057
5058 my $filename = $self->{'filename'};
5059
5060 return ref($self) . " - $filename";
5061 }
5062
5063 sub to_string {
5064 my ( $self ) = @_;
5065
5066 my $filename = $self->{'filename'};
5067
5068 return $filename;
5069 }
5070
5071 1;
5072 package App::Ack::Filter::IsPathGroup;
5073
5074
5075
5076
5077 use strict;
5078 use warnings;
5079 BEGIN {
5080 our @ISA = 'App::Ack::Filter';
5081 }
5082
5083 sub new {
5084 my ( $class ) = @_;
5085
5086 return bless {
5087 data => {},
5088 }, $class;
5089 }
5090
5091 sub add {
5092 my ( $self, $filter ) = @_;
5093
5094 $self->{data}->{ $filter->{filename} } = 1;
5095
5096 return;
5097 }
5098
5099 sub filter {
5100 my ( $self, $resource ) = @_;
5101
5102 my $data = $self->{'data'};
5103
5104 return exists $data->{$resource->name};
5105 }
5106
5107 sub inspect {
5108 my ( $self ) = @_;
5109
5110 return ref($self) . " - $self";
5111 }
5112
5113 sub to_string {
5114 my ( $self ) = @_;
5115
5116 return join(' ', keys %{$self->{data}});
5117 }
5118
5119 1;
5120 package File::Next;
5121
5122 use strict;
5123 use warnings;
5124
5125
5126 our $VERSION = '1.16';
5127
5128
5129
5130 use File::Spec ();
5131
5132 our $name; # name of the current file
5133 our $dir; # dir of the current file
5134
5135 our %files_defaults;
5136 our %skip_dirs;
5137
5138 BEGIN {
5139 %files_defaults = (
5140 file_filter => undef,
5141 descend_filter => undef,
5142 error_handler => sub { CORE::die $_[0] },
5143 warning_handler => sub { CORE::warn @_ },
5144 sort_files => undef,
5145 follow_symlinks => 1,
5146 nul_separated => 0,
5147 );
5148 %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
5149 }
5150
5151
5152 sub files {
5153 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
5154
5155 my ($parms,@queue) = _setup( \%files_defaults, @_ );
5156
5157 return sub {
5158 my $filter = $parms->{file_filter};
5159 while (@queue) {
5160 my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
5161 if ( -f $fullpath || -p _ || $fullpath =~ m{^/dev/fd} ) {
5162 if ( $filter ) {
5163 local $_ = $file;
5164 local $File::Next::dir = $dirname;
5165 local $File::Next::name = $fullpath;
5166 next if not $filter->();
5167 }
5168 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
5169 }
5170 if ( -d _ ) {
5171 unshift( @queue, _candidate_files( $parms, $fullpath ) );
5172 }
5173 } # while
5174
5175 return;
5176 }; # iterator
5177 }
5178
5179
5180
5181
5182
5183
5184 sub from_file {
5185 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
5186
5187 my ($parms,@queue) = _setup( \%files_defaults, @_ );
5188 my $err = $parms->{error_handler};
5189 my $warn = $parms->{warning_handler};
5190
5191 my $filename = $queue[1];
5192
5193 if ( !defined($filename) ) {
5194 $err->( 'Must pass a filename to from_file()' );
5195 return undef;
5196 }
5197
5198 my $fh;
5199 if ( $filename eq '-' ) {
5200 $fh = \*STDIN;
5201 }
5202 else {
5203 if ( !open( $fh, '<', $filename ) ) {
5204 $err->( "Unable to open $filename: $!", $! + 0 );
5205 return undef;
5206 }
5207 }
5208
5209 return sub {
5210 my $filter = $parms->{file_filter};
5211 local $/ = $parms->{nul_separated} ? "\x00" : $/;
5212 while ( my $fullpath = <$fh> ) {
5213 chomp $fullpath;
5214 next unless $fullpath =~ /./;
5215 if ( not ( -f $fullpath || -p _ ) ) {
5216 $warn->( "$fullpath: No such file" );
5217 next;
5218 }
5219
5220 my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );
5221 if ( $filter ) {
5222 local $_ = $file;
5223 local $File::Next::dir = $dirname;
5224 local $File::Next::name = $fullpath;
5225 next if not $filter->();
5226 }
5227 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
5228 } # while
5229 close $fh;
5230
5231 return;
5232 }; # iterator
5233 }
5234
5235 sub _bad_invocation {
5236 my $good = (caller(1))[3];
5237 my $bad = $good;
5238 $bad =~ s/(.+)::/$1->/;
5239 return "$good must not be invoked as $bad";
5240 }
5241
5242 sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }
5243 sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }
5244
5245 sub reslash {
5246 my $path = shift;
5247
5248 my @parts = split( /\//, $path );
5249
5250 return $path if @parts < 2;
5251
5252 return File::Spec->catfile( @parts );
5253 }
5254
5255
5256
5257 sub _setup {
5258 my $defaults = shift;
5259 my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
5260
5261 my %passed_parms = %{$passed_parms};
5262
5263 my $parms = {};
5264 for my $key ( keys %{$defaults} ) {
5265 $parms->{$key} =
5266 exists $passed_parms{$key}
5267 ? delete $passed_parms{$key}
5268 : $defaults->{$key};
5269 }
5270
5271 # Any leftover keys are bogus
5272 for my $badkey ( keys %passed_parms ) {
5273 my $sub = (caller(1))[3];
5274 $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );
5275 }
5276
5277 # If it's not a code ref, assume standard sort
5278 if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {
5279 $parms->{sort_files} = \&sort_standard;
5280 }
5281 my @queue;
5282
5283 for ( @_ ) {
5284 my $start = reslash( $_ );
5285 if (-d $start) {
5286 push @queue, ($start,undef,$start);
5287 }
5288 else {
5289 push @queue, (undef,$start,$start);
5290 }
5291 }
5292
5293 return ($parms,@queue);
5294 }
5295
5296
5297 sub _candidate_files {
5298 my $parms = shift;
5299 my $dirname = shift;
5300
5301 my $dh;
5302 if ( !opendir $dh, $dirname ) {
5303 $parms->{error_handler}->( "$dirname: $!", $! + 0 );
5304 return;
5305 }
5306
5307 my @newfiles;
5308 my $descend_filter = $parms->{descend_filter};
5309 my $follow_symlinks = $parms->{follow_symlinks};
5310 my $sort_sub = $parms->{sort_files};
5311
5312 for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
5313 my $has_stat;
5314
5315 my $fullpath = File::Spec->catdir( $dirname, $file );
5316 if ( !$follow_symlinks ) {
5317 next if -l $fullpath;
5318 $has_stat = 1;
5319 }
5320
5321 # Only do directory checking if we have a descend_filter
5322 if ( $descend_filter ) {
5323 if ( $has_stat ? (-d _) : (-d $fullpath) ) {
5324 local $File::Next::dir = $fullpath;
5325 local $_ = $file;
5326 next if not $descend_filter->();
5327 }
5328 }
5329 if ( $sort_sub ) {
5330 push( @newfiles, [ $dirname, $file, $fullpath ] );
5331 }
5332 else {
5333 push( @newfiles, $dirname, $file, $fullpath );
5334 }
5335 }
5336 closedir $dh;
5337
5338 if ( $sort_sub ) {
5339 return map { @{$_} } sort $sort_sub @newfiles;
5340 }
5341
5342 return @newfiles;
5343 }
5344
5345
5346 1; # End of File::Next