View file File name : ConfigLoader.pm Content :package App::Ack::ConfigLoader; use strict; use warnings; use 5.010; use App::Ack (); use App::Ack::ConfigDefault (); use App::Ack::ConfigFinder (); use App::Ack::Filter (); use App::Ack::Filter::Collection (); use App::Ack::Filter::Default (); use App::Ack::Filter::IsPath (); use File::Spec 3.00 (); use Getopt::Long 2.38 (); use Text::ParseWords 3.1 (); sub configure_parser { my @opts = @_; my @standard = qw( default bundling no_auto_help no_auto_version no_ignore_case ); Getopt::Long::Configure( @standard, @opts ); } sub _generate_ignore_dir { my ( $option_name, $opt ) = @_; my $is_inverted = $option_name =~ /^--no/; return sub { my ( undef, $dir ) = @_; $dir = _remove_directory_separator( $dir ); if ( $dir !~ /:/ ) { $dir = 'is:' . $dir; } my ( $filter_type, $args ) = split /:/, $dir, 2; if ( $filter_type eq 'firstlinematch' ) { App::Ack::die( qq{Invalid filter specification "$filter_type" for option '$option_name'} ); } my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args)); my $collection; my $previous_inversion_matches = $opt->{idirs} && !($is_inverted xor $opt->{idirs}[-1]->is_inverted()); if ( $previous_inversion_matches ) { $collection = $opt->{idirs}[-1]; if ( $is_inverted ) { # This relies on invert of an inverted filter to return the original. $collection = $collection->invert(); } } else { $collection = App::Ack::Filter::Collection->new(); push @{ $opt->{idirs} }, $is_inverted ? $collection->invert() : $collection; } $collection->add($filter); if ( $filter_type eq 'is' ) { $collection->add(App::Ack::Filter::IsPath->new($args)); } }; } sub _remove_directory_separator { my $path = shift; state $dir_sep_chars = $App::Ack::is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) ); $path =~ s/[$dir_sep_chars]$//; return $path; } sub _process_filter_spec { my ( $spec ) = @_; if ( $spec =~ /^(\w+):(\w+):(.*)/ ) { my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 ); return ( $type_name, App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) ); } elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification. my ( $type_name, $extensions ) = ( $1, $2 ); my @extensions = split(/,/, $extensions); foreach my $extension ( @extensions ) { $extension =~ s/^[.]//; } return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) ); } else { App::Ack::die( "Invalid filter specification '$spec'" ); } } sub _uninvert_filter { my ( $opt, @filters ) = @_; return unless defined $opt->{filters} && @filters; # Loop through all the registered filters. If we hit one that # matches this extension and it's inverted, we need to delete it from # the options. for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) { my $opt_filter = @{ $opt->{filters} }[$i]; # XXX Do a real list comparison? This just checks string equivalence. if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) { splice @{ $opt->{filters} }, $i, 1; $i--; } } return; } sub _process_filetypes { my ( $opt, $arg_sources ) = @_; my %additional_specs; my $add_spec = sub { my ( undef, $spec ) = @_; my ( $name, $filter ) = _process_filter_spec($spec); push @{ $App::Ack::mappings{$name} }, $filter; $additional_specs{$name . '!'} = sub { my ( undef, $value ) = @_; my @filters = @{ $App::Ack::mappings{$name} }; if ( not $value ) { @filters = map { $_->invert() } @filters; } else { _uninvert_filter( $opt, @filters ); } push @{ $opt->{'filters'} }, @filters; }; }; my $set_spec = sub { my ( undef, $spec ) = @_; my ( $name, $filter ) = _process_filter_spec($spec); $App::Ack::mappings{$name} = [ $filter ]; $additional_specs{$name . '!'} = sub { my ( undef, $value ) = @_; my @filters = @{ $App::Ack::mappings{$name} }; if ( not $value ) { @filters = map { $_->invert() } @filters; } push @{ $opt->{'filters'} }, @filters; }; }; my $delete_spec = sub { my ( undef, $name ) = @_; delete $App::Ack::mappings{$name}; delete $additional_specs{$name . '!'}; }; my %type_arg_specs = ( 'type-add=s' => $add_spec, 'type-set=s' => $set_spec, 'type-del=s' => $delete_spec, ); configure_parser( 'no_auto_abbrev', 'pass_through' ); foreach my $source (@{$arg_sources}) { my $args = $source->{contents}; if ( ref($args) ) { # $args are modified in place, so no need to munge $arg_sources Getopt::Long::GetOptionsFromArray( $args, %type_arg_specs ); } else { ( undef, $source->{contents} ) = Getopt::Long::GetOptionsFromString( $args, %type_arg_specs ); } } $additional_specs{'k|known-types'} = sub { my @filters = map { @{$_} } values(%App::Ack::mappings); push @{ $opt->{'filters'} }, @filters; }; return \%additional_specs; } sub get_arg_spec { my ( $opt, $extra_specs ) = @_; =begin Adding-Options *** IF YOU ARE MODIFYING ACK PLEASE READ THIS *** If you plan to add a new option to ack, please make sure of the following: * Your new option has a test underneath the t/ directory. * Your new option is explained when a user invokes ack --help. (See App::Ack::show_help) * Your new option is explained when a user invokes ack --man. (See the POD at the end of ./ack) * Add your option to t/config-loader.t * Add your option to t/Util.pm#get_expected_options * Add your option's description and aliases to dev/generate-completion-scripts.pl * Go through the list of options already available, and consider whether your new option can be considered mutex with another option. =end Adding-Options =cut sub _type_handler { my ( $getopt, $value ) = @_; my $cb_value = 1; if ( $value =~ s/^no// ) { $cb_value = 0; } my $callback; { no warnings; $callback = $extra_specs->{ $value . '!' }; } if ( $callback ) { $callback->( $getopt, $cb_value ); } else { App::Ack::die( "Unknown type '$value'" ); } return; } return { 1 => sub { $opt->{1} = $opt->{m} = 1 }, 'A|after-context:-1' => sub { shift; $opt->{A} = _context_value(shift) }, 'B|before-context:-1' => sub { shift; $opt->{B} = _context_value(shift) }, 'C|context:-1' => sub { shift; $opt->{B} = $opt->{A} = _context_value(shift) }, 'break!' => \$opt->{break}, 'c|count' => \$opt->{c}, 'color|colour!' => \$opt->{color}, 'color-match=s' => \$ENV{ACK_COLOR_MATCH}, 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME}, 'color-colno=s' => \$ENV{ACK_COLOR_COLNO}, 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO}, 'column!' => \$opt->{column}, 'create-ackrc' => sub { say for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; }, 'debug' => \$opt->{debug}, 'env!' => sub { my ( undef, $value ) = @_; if ( !$value ) { $opt->{noenv_seen} = 1; } }, f => \$opt->{f}, 'files-from=s' => \$opt->{files_from}, 'filter!' => \$App::Ack::is_filter_mode, flush => sub { $| = 1 }, 'follow!' => \$opt->{follow}, g => \$opt->{g}, 'group!' => sub { shift; $opt->{heading} = $opt->{break} = shift }, 'heading!' => \$opt->{heading}, 'h|no-filename' => \$opt->{h}, 'H|with-filename' => \$opt->{H}, 'i|ignore-case' => sub { $opt->{i} = 1; $opt->{S} = 0; }, 'I|no-ignore-case' => sub { $opt->{i} = 0; $opt->{S} = 0; }, 'ignore-directory|ignore-dir=s' => _generate_ignore_dir('--ignore-dir', $opt), 'ignore-file=s' => sub { my ( undef, $file ) = @_; my ( $filter_type, $args ) = split /:/, $file, 2; my $filter = App::Ack::Filter->create_filter($filter_type, split(/,/, $args//'')); if ( !$opt->{ifiles} ) { $opt->{ifiles} = App::Ack::Filter::Collection->new(); } $opt->{ifiles}->add($filter); }, 'l|files-with-matches' => \$opt->{l}, 'L|files-without-matches' => \$opt->{L}, 'm|max-count=i' => \$opt->{m}, 'match=s' => \$opt->{regex}, 'n|no-recurse' => \$opt->{n}, o => sub { $opt->{output} = '$&' }, 'output=s' => \$opt->{output}, 'pager:s' => sub { my ( undef, $value ) = @_; $opt->{pager} = $value || $ENV{PAGER}; }, 'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt), 'nopager' => sub { $opt->{pager} = undef }, 'passthru' => \$opt->{passthru}, 'print0' => \$opt->{print0}, 'p|proximate:1' => \$opt->{p}, 'P' => sub { $opt->{p} = 0 }, 'Q|literal' => \$opt->{Q}, 'r|R|recurse' => sub { $opt->{n} = 0 }, 'range-start=s' => \$opt->{range_start}, 'range-end=s' => \$opt->{range_end}, 'range-invert!' => \$opt->{range_invert}, 's' => \$opt->{s}, 'show-types' => \$opt->{show_types}, 'S|smart-case!' => sub { my (undef,$value) = @_; $opt->{S} = $value; $opt->{i} = 0 if $value; }, 'sort-files' => \$opt->{sort_files}, 't|type=s' => \&_type_handler, 'T=s' => sub { my ($getopt,$value) = @_; $value="no$value"; _type_handler($getopt,$value); }, 'underline!' => \$opt->{underline}, 'v|invert-match' => \$opt->{v}, 'w|word-regexp' => \$opt->{w}, 'x' => sub { $opt->{files_from} = '-' }, 'help' => sub { App::Ack::show_help(); exit; }, 'help-types' => sub { App::Ack::show_help_types(); exit; }, 'help-colors' => sub { App::Ack::show_help_colors(); exit; }, 'help-rgb-colors' => sub { App::Ack::show_help_rgb(); exit; }, $extra_specs ? %{$extra_specs} : (), }; # arg_specs } sub _context_value { my $val = shift; # Contexts default to 2. return (!defined($val) || ($val < 0)) ? 2 : $val; } sub _process_other { my ( $opt, $extra_specs, $arg_sources ) = @_; my $argv_source; my $is_help_types_active; foreach my $source (@{$arg_sources}) { if ( $source->{name} eq 'ARGV' ) { $argv_source = $source->{contents}; last; } } if ( $argv_source ) { # This *should* always be true, but you never know... configure_parser( 'pass_through' ); Getopt::Long::GetOptionsFromArray( [ @{$argv_source} ], 'help-types' => \$is_help_types_active, ); } my $arg_specs = get_arg_spec( $opt, $extra_specs ); configure_parser(); foreach my $source (@{$arg_sources}) { my ( $source_name, $args ) = @{$source}{qw/name contents/}; my $args_for_source = { %{$arg_specs} }; if ( $source->{is_ackrc} ) { my $illegal = sub { my $name = shift; App::Ack::die( "Option --$name is forbidden in .ackrc files." ); }; $args_for_source = { %{$args_for_source}, 'output=s' => $illegal, 'match=s' => $illegal, }; } if ( $source->{project} ) { my $illegal = sub { my $name = shift; App::Ack::die( "Option --$name is forbidden in project .ackrc files." ); }; $args_for_source = { %{$args_for_source}, 'pager:s' => $illegal, }; } my $ret; if ( ref($args) ) { $ret = Getopt::Long::GetOptionsFromArray( $args, %{$args_for_source} ); } else { ( $ret, $source->{contents} ) = Getopt::Long::GetOptionsFromString( $args, %{$args_for_source} ); } if ( !$ret ) { if ( !$is_help_types_active ) { my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name"; App::Ack::die( "Invalid option $where" ); } } if ( $opt->{noenv_seen} ) { App::Ack::die( "--noenv found in $source_name" ); } } # XXX We need to check on a -- in the middle of a non-ARGV source return; } sub _explode_sources { my ( $sources ) = @_; my @new_sources; my %opt; my $arg_spec = get_arg_spec( \%opt, {} ); my $dummy_sub = sub {}; my $add_type = sub { my ( undef, $arg ) = @_; if ( $arg =~ /(\w+)=/) { $arg_spec->{$1} = $dummy_sub; } else { ( $arg ) = split /:/, $arg; $arg_spec->{$arg} = $dummy_sub; } }; my $del_type = sub { my ( undef, $arg ) = @_; delete $arg_spec->{$arg}; }; configure_parser( 'pass_through' ); foreach my $source (@{$sources}) { my ( $name, $options ) = @{$source}{qw/name contents/}; if ( ref($options) ne 'ARRAY' ) { $source->{contents} = $options = [ Text::ParseWords::shellwords($options) ]; } for my $j ( 0 .. @{$options}-1 ) { next unless $options->[$j] =~ /^-/; my @chunk = ( $options->[$j] ); push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/; $j--; my @copy = @chunk; Getopt::Long::GetOptionsFromArray( [@chunk], 'type-add=s' => $add_type, 'type-set=s' => $add_type, 'type-del=s' => $del_type, %{$arg_spec} ); push @new_sources, { name => $name, contents => \@copy, }; } } return \@new_sources; } sub _compare_opts { my ( $a, $b ) = @_; my $first_a = $a->[0]; my $first_b = $b->[0]; $first_a =~ s/^--?//; $first_b =~ s/^--?//; return $first_a cmp $first_b; } sub _dump_options { my ( $sources ) = @_; $sources = _explode_sources($sources); my %opts_by_source; my @source_names; foreach my $source (@{$sources}) { my $name = $source->{name}; if ( not $opts_by_source{$name} ) { $opts_by_source{$name} = []; push @source_names, $name; } push @{$opts_by_source{$name}}, $source->{contents}; } foreach my $name (@source_names) { my $contents = $opts_by_source{$name}; say $name; say '=' x length($name); say ' ', join(' ', @{$_}) for sort { _compare_opts($a, $b) } @{$contents}; } return; } sub _remove_default_options_if_needed { my ( $sources ) = @_; my $default_index; foreach my $index ( 0 .. $#{$sources} ) { if ( $sources->[$index]{'name'} eq 'Defaults' ) { $default_index = $index; last; } } return $sources unless defined $default_index; my $should_remove = 0; configure_parser( 'no_auto_abbrev', 'pass_through' ); foreach my $index ( $default_index + 1 .. $#{$sources} ) { my $args = $sources->[$index]->{contents}; if (ref($args)) { Getopt::Long::GetOptionsFromArray( $args, 'ignore-ack-defaults' => \$should_remove, ); } else { ( undef, $sources->[$index]{contents} ) = Getopt::Long::GetOptionsFromString( $args, 'ignore-ack-defaults' => \$should_remove, ); } } return $sources unless $should_remove; my @copy = @{$sources}; splice @copy, $default_index, 1; return \@copy; } sub process_args { my $arg_sources = \@_; my %opt = ( pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER}, ); $arg_sources = _remove_default_options_if_needed($arg_sources); # Check for --dump early. foreach my $source (@{$arg_sources}) { if ( $source->{name} eq 'ARGV' ) { my $dump; configure_parser( 'pass_through' ); Getopt::Long::GetOptionsFromArray( $source->{contents}, 'dump' => \$dump, ); if ( $dump ) { _dump_options($arg_sources); exit(0); } } } my $type_specs = _process_filetypes(\%opt, $arg_sources); _check_for_mutex_options( $type_specs ); _process_other(\%opt, $type_specs, $arg_sources); while ( @{$arg_sources} ) { my $source = shift @{$arg_sources}; my $args = $source->{contents}; # All of our sources should be transformed into an array ref if ( ref($args) ) { my $source_name = $source->{name}; if ( $source_name eq 'ARGV' ) { @ARGV = @{$args}; } elsif (@{$args}) { App::Ack::die( "Source '$source_name' has extra arguments!" ); } } else { App::Ack::die( 'The impossible has occurred!' ); } } my $filters = ($opt{filters} ||= []); # Throw the default filter in if no others are selected. if ( not grep { !$_->is_inverted() } @{$filters} ) { push @{$filters}, App::Ack::Filter::Default->new(); } return \%opt; } sub retrieve_arg_sources { my @arg_sources; my $noenv; my $ackrc; configure_parser( 'no_auto_abbrev', 'pass_through' ); Getopt::Long::GetOptions( 'noenv' => \$noenv, 'ackrc=s' => \$ackrc, ); my @files; if ( !$noenv ) { my $finder = App::Ack::ConfigFinder->new; @files = $finder->find_config_files; } if ( $ackrc ) { # We explicitly use open so we get a nice error message. # XXX This is a potential race condition!. if ( open my $fh, '<', $ackrc ) { close $fh; } else { App::Ack::die( "Unable to load ackrc '$ackrc': $!" ); } push( @files, { path => $ackrc } ); } push @arg_sources, { name => 'Defaults', contents => [ App::Ack::ConfigDefault::options_clean() ], }; foreach my $file ( @files) { my @lines = read_rcfile($file->{path}); if ( @lines ) { push @arg_sources, { name => $file->{path}, contents => \@lines, project => $file->{project}, is_ackrc => 1, }; } } push @arg_sources, { name => 'ARGV', contents => [ @ARGV ], }; return @arg_sources; } sub read_rcfile { my $file = shift; return unless defined $file && -e $file; my @lines; open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" ); while ( defined( my $line = <$fh> ) ) { chomp $line; $line =~ s/^\s+//; $line =~ s/\s+$//; next if $line eq ''; next if $line =~ /^\s*#/; push( @lines, $line ); } close $fh or App::Ack::die( "Unable to close $file: $!" ); return @lines; } # Verifies no mutex options were passed. Dies if they were. sub _check_for_mutex_options { my $type_specs = shift; my $mutex = mutex_options(); my ($raw,$used) = _options_used( $type_specs ); my @used = sort { lc $a cmp lc $b } keys %{$used}; for my $i ( @used ) { for my $j ( @used ) { next if $i eq $j; if ( $mutex->{$i}{$j} ) { my $x = $raw->[ $used->{$i} ]; my $y = $raw->[ $used->{$j} ]; App::Ack::die( "Options '$x' and '$y' can't be used together." ); } } } return; } # Processes the command line option and returns a hash of the options that were # used on the command line, using their full name. "--prox" shows up in the hash as "--proximate". sub _options_used { my $type_specs = shift; my %dummy_opt; my $real_spec = get_arg_spec( \%dummy_opt, $type_specs ); # The real argument parsing doesn't check for --type-add, --type-del or --type-set because # they get removed by the argument processing. We have to account for them here. my $sub_dummy = sub {}; $real_spec = { %{$real_spec}, 'type-add=s' => $sub_dummy, 'type-del=s' => $sub_dummy, 'type-set=s' => $sub_dummy, 'ignore-ack-defaults' => $sub_dummy, }; my %parsed; my @raw; my %spec_capture_parsed; my %spec_capture_raw; =pod We have to build two argument specs. To populate the C<%parsed> hash: Capture the arguments that the user has passed in, as parsed by the Getopt::Long::GetOptions function. Aliases are converted down to their short options. If a user passes "--proximate", Getopt::Long converts that to "-p" and we store it as "-p". To populate the C<@raw> array: Capture the arguments raw, without having been converted to their short options. If a user passes "--proximate", we store it in C<@raw> as "--proximate". =cut # Capture the %parsed hash. CAPTURE_PARSED: { my $parsed_pos = 0; my $sub_count = sub { my $arg = shift; $arg = "$arg"; $parsed{$arg} = $parsed_pos++; }; %spec_capture_parsed = ( '<>' => sub { $parsed_pos++ }, # Bump forward one pos for non-options. map { $_ => $sub_count } keys %{$real_spec} ); } # Capture the @raw array. CAPTURE_RAW: { my $raw_pos = 0; %spec_capture_raw = ( '<>' => sub { $raw_pos++ }, # Bump forward one pos for non-options. ); my $sub_count = sub { my $arg = shift; $arg = "$arg"; $raw[$raw_pos] = length($arg) == 1 ? "-$arg" : "--$arg"; $raw_pos++; }; for my $opt_spec ( keys %{$real_spec} ) { my $negatable; my $type; my $default; $negatable = ($opt_spec =~ s/!$//); if ( $opt_spec =~ s/(=[si])$// ) { $type = $1; } if ( $opt_spec =~ s/(:.+)$// ) { $default = $1; } my @aliases = split( /\|/, $opt_spec ); for my $alias ( @aliases ) { $alias .= $type if defined $type; $alias .= $default if defined $default; $alias .= '!' if $negatable; $spec_capture_raw{$alias} = $sub_count; } } } # Parse @ARGV twice, once with each capture spec. configure_parser( 'pass_through' ); # Ignore invalid options. Getopt::Long::GetOptionsFromArray( [@ARGV], %spec_capture_raw ); Getopt::Long::GetOptionsFromArray( [@ARGV], %spec_capture_parsed ); return (\@raw,\%parsed); } sub mutex_options { # This list is machine-generated by dev/crank-mutex. Do not modify it by hand. return { 1 => { m => 1, passthru => 1, }, A => { L => 1, c => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, }, B => { L => 1, c => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, }, C => { L => 1, c => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, }, H => { L => 1, f => 1, g => 1, l => 1, }, L => { A => 1, B => 1, C => 1, H => 1, L => 1, break => 1, c => 1, column => 1, f => 1, g => 1, group => 1, h => 1, heading => 1, l => 1, 'no-filename' => 1, o => 1, output => 1, p => 1, passthru => 1, 'show-types' => 1, v => 1, 'with-filename' => 1, }, break => { L => 1, c => 1, f => 1, g => 1, l => 1, }, c => { A => 1, B => 1, C => 1, L => 1, break => 1, column => 1, f => 1, g => 1, group => 1, heading => 1, m => 1, o => 1, output => 1, p => 1, passthru => 1, }, column => { L => 1, c => 1, f => 1, g => 1, l => 1, o => 1, output => 1, passthru => 1, v => 1, }, f => { A => 1, B => 1, C => 1, H => 1, L => 1, break => 1, c => 1, column => 1, f => 1, 'files-from' => 1, g => 1, group => 1, h => 1, heading => 1, l => 1, m => 1, match => 1, o => 1, output => 1, p => 1, passthru => 1, u => 1, v => 1, x => 1, }, 'files-from' => { f => 1, g => 1, x => 1, }, g => { A => 1, B => 1, C => 1, H => 1, L => 1, break => 1, c => 1, column => 1, f => 1, 'files-from' => 1, g => 1, group => 1, h => 1, heading => 1, l => 1, m => 1, match => 1, o => 1, output => 1, p => 1, passthru => 1, u => 1, x => 1, }, group => { L => 1, c => 1, f => 1, g => 1, l => 1, }, h => { L => 1, f => 1, g => 1, l => 1, }, heading => { L => 1, c => 1, f => 1, g => 1, l => 1, }, l => { A => 1, B => 1, C => 1, H => 1, L => 1, break => 1, column => 1, f => 1, g => 1, group => 1, h => 1, heading => 1, l => 1, 'no-filename' => 1, o => 1, output => 1, p => 1, passthru => 1, 'show-types' => 1, 'with-filename' => 1, }, m => { 1 => 1, c => 1, f => 1, g => 1, passthru => 1, }, match => { f => 1, g => 1, }, 'no-filename' => { L => 1, l => 1, }, o => { A => 1, B => 1, C => 1, L => 1, c => 1, column => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, 'show-types' => 1, v => 1, }, output => { A => 1, B => 1, C => 1, L => 1, c => 1, column => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, 'show-types' => 1, u => 1, v => 1, }, p => { A => 1, B => 1, C => 1, L => 1, c => 1, f => 1, g => 1, l => 1, o => 1, output => 1, p => 1, passthru => 1, }, passthru => { 1 => 1, A => 1, B => 1, C => 1, L => 1, c => 1, column => 1, f => 1, g => 1, l => 1, m => 1, o => 1, output => 1, p => 1, v => 1, }, 'show-types' => { L => 1, l => 1, o => 1, output => 1, }, u => { f => 1, g => 1, output => 1, }, v => { L => 1, column => 1, f => 1, o => 1, output => 1, passthru => 1, }, 'with-filename' => { L => 1, l => 1, }, x => { f => 1, 'files-from' => 1, g => 1, }, }; } # End of mutex_options() 1; # End of App::Ack::ConfigLoader