View file File name : ConfigFinder.pm Content :package App::Ack::ConfigFinder; =head1 NAME App::Ack::ConfigFinder =head1 DESCRIPTION A module that contains the logic for locating the various configuration files. =head1 LOCATING CONFIG FILES First, ack looks for a global ackrc. =over =item On Windows, this is `ackrc` in either COMMON_APPDATA or APPDATA. If `ackrc` is present in both directories, ack uses both files in that order. =item On a non-Windows OS, this is `/etc/ackrc`. =back Then, ack looks for a user-specific ackrc if the HOME environment variable is set. This is either F<$HOME/.ackrc> or F<$HOME/_ackrc>. Then, ack looks for a project-specific ackrc file. ack searches up the directory hierarchy for the first `.ackrc` or `_ackrc` file. If this is one of the ackrc files found in the previous steps, it is not loaded again. It is a fatal error if a directory contains both F<.ackrc> and F<_ackrc>. After ack loads the options from the found ackrc files, ack looks at the C<ACKRC_OPTIONS> environment variable. Finally, ack takes settings from the command line. =cut use strict; use warnings; use App::Ack (); use Cwd 3.00 (); use File::Spec 3.00 (); use if ($^O eq 'MSWin32'), 'Win32'; =head1 METHODS =head2 new Creates a new config finder. =cut sub new { my ( $class ) = @_; return bless {}, $class; } sub _remove_redundancies { my @configs = @_; my %seen; my @uniq; foreach my $config (@configs) { my $path = $config->{path}; my $key = -e $path ? Cwd::realpath( $path ) : $path; if ( not $App::Ack::is_windows ) { # On Unix, uniquify on inode. my ($dev, $inode) = (stat $key)[0, 1]; $key = "$dev:$inode" if defined $dev; } push( @uniq, $config ) unless $seen{$key}++; } return @uniq; } sub _check_for_ackrc { return unless defined $_[0]; my @files = grep { -f } map { File::Spec->catfile(@_, $_) } qw(.ackrc _ackrc); App::Ack::die( File::Spec->catdir(@_) . ' contains both .ackrc and _ackrc. Please remove one of those files.' ) if @files > 1; return wantarray ? @files : $files[0]; } # end _check_for_ackrc =head2 $finder->find_config_files Locates config files, and returns a list of them. =cut sub find_config_files { my @config_files; if ( $App::Ack::is_windows ) { push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } ( Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()), Win32::GetFolderPath(Win32::CSIDL_APPDATA()), ); } else { push @config_files, { path => '/etc/ackrc' }; } if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) { push @config_files, { path => $ENV{'ACKRC'} }; } else { push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'}); } my $cwd = Cwd::getcwd(); return () unless defined $cwd; # XXX This should go through some untainted cwd-fetching function, and not get untainted brute-force like this. $cwd =~ /(.+)/; $cwd = $1; my @dirs = File::Spec->splitdir( $cwd ); while ( @dirs ) { my $ackrc = _check_for_ackrc(@dirs); if ( defined $ackrc ) { push @config_files, { project => 1, path => $ackrc }; last; } pop @dirs; } # We only test for existence here, so if the file is deleted out from under us, this will fail later. return _remove_redundancies( @config_files ); } 1;