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