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