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