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