File-ConfigDir

 view release on metacpan or  search on metacpan

lib/File/ConfigDir.pm  view on Meta::CPAN

package File::ConfigDir;

use strict;
use warnings;
use parent 'Exporter';
use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS);

use Carp qw(croak);
use Config;
use Cwd            ();
use Exporter       ();
use FindBin        ();
use File::Basename ();
use File::Spec     ();

=head1 NAME

File::ConfigDir - Get directories of configuration files

=begin html

<a href="https://travis-ci.org/perl5-utils/File-ConfigDir"><img src="https://travis-ci.org/perl5-utils/File-ConfigDir.svg?branch=master" alt="Travis CI"/></a>
<a href='https://coveralls.io/github/perl5-utils/File-ConfigDir?branch=master'><img src='https://coveralls.io/repos/github/perl5-utils/File-ConfigDir/badge.svg?branch=master' alt='Coverage Status'/></a>

=end html

=cut

$VERSION   = '0.021';
@EXPORT_OK = (
    qw(config_dirs system_cfg_dir desktop_cfg_dir),
    qw(xdg_config_dirs machine_cfg_dir),
    qw(core_cfg_dir site_cfg_dir vendor_cfg_dir),
    qw(locallib_cfg_dir local_cfg_dir),
    qw(here_cfg_dir singleapp_cfg_dir vendorapp_cfg_dir),
    qw(xdg_config_home user_cfg_dir)
);
%EXPORT_TAGS = (
    ALL => [@EXPORT_OK],
);

eval "use List::MoreUtils qw/distinct/;";    ## no strict (BuiltinFunctions::ProhibitStringyEval)
__PACKAGE__->can("distinct") or eval <<'EOP';
    # from PP part of List::MoreUtils
sub distinct(&@) {
    my %h;
    map { $h{$_}++ == 0 ? $_ : () } @_;
}
EOP

=head1 SYNOPSIS

    use File::ConfigDir ':ALL';

    my @cfgdirs = config_dirs();
    my @appcfgdirs = config_dirs('app');

    # install support
    my $site_cfg_dir = (site_cfg_dir())[0];
    my $vendor_cfg_dir = (site_cfg_dir()))[0];

=head1 DESCRIPTION

This module is a helper for installing, reading and finding configuration
file locations. It's intended to work in every supported Perl5 environment
and will always try to Do The Right Thing(TM).

C<File::ConfigDir> is a module to help out when perl modules (especially
applications) need to read and store configuration files from more than
one location. Writing user configuration is easy thanks to
L<File::HomeDir>, but what when the system administrator needs to place
some global configuration or there will be system related configuration
(in C</etc> on UNIX(TM) or C<$ENV{windir}> on Windows(TM)) and some
network configuration in NFS mapped C</etc/p5-app> or
C<$ENV{ALLUSERSPROFILE} . "\\Application Data\\p5-app">, respectively.

C<File::ConfigDir> has no "do what I mean" mode - it's entirely up to the
user to pick the right directory for each particular application.

=head1 EXPORT

Every function listed below can be exported, either by name or using the
tag C<:ALL>.

=head1 SUBROUTINES/METHODS

All functions can take one optional argument as application specific
configuration directory. If given, it will be embedded at the right (TM)
place of the resulting path.

=cut

sub _find_common_base_dir
{
    my ($dira, $dirb) = @_;
    my ($va, $da, undef) = File::Spec->splitpath($dira, 1);
    my ($vb, $db, undef) = File::Spec->splitpath($dirb, 1);
    my @dirsa = File::Spec->splitdir($da);
    my @dirsb = File::Spec->splitdir($db);
    my @commondir;
    my $max = $#dirsa < $#dirsb ? $#dirsa : $#dirsb;
    for my $i (0 .. $max)
    {
        $dirsa[$i] eq $dirsb[$i] or last;
        push(@commondir, $dirsa[$i]);
    }

    File::Spec->catpath($va, File::Spec->catdir(@commondir));
}

=head2 system_cfg_dir

Returns the configuration directory where configuration files of the
operating system resides. For Unices this is C</etc>, for MSWin32 it's
the value of the environment variable C<%windir%>.

=cut

my $system_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs = File::Spec->catdir($^O eq "MSWin32" ? $ENV{windir} : "/etc", @cfg_base);
    @dirs;
};

sub system_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "system_cfg_dir(;\$), not system_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $system_cfg_dir->(@cfg_base);
}

=head2 machine_cfg_dir

Alias for desktop_cfg_dir - deprecated.

=head2 xdg_config_dirs

Alias for desktop_cfg_dir

=head2 desktop_cfg_dir

Returns the configuration directory where configuration files of the
desktop applications resides. For Unices this is C</etc/xdg>, for MSWin32
it's the value of the environment variable C<%ALLUSERSPROFILE%>
concatenated with the basename of the environment variable C<%APPDATA%>.

=cut

my $desktop_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs;
    if ($^O eq "MSWin32")
    {
        my $alluserprof = $ENV{ALLUSERSPROFILE};
        my $appdatabase = File::Basename::basename($ENV{APPDATA});
        @dirs = (File::Spec->catdir($alluserprof, $appdatabase, @cfg_base));
    }
    else
    {
        if ($ENV{XDG_CONFIG_DIRS})
        {
            @dirs = split(":", $ENV{XDG_CONFIG_DIRS});
            @dirs = map { File::Spec->catdir($_, @cfg_base) } @dirs;
        }
        else
        {
            @dirs = (File::Spec->catdir("/etc", "xdg", @cfg_base));
        }
    }
    @dirs;
};

sub desktop_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "desktop_cfg_dir(;\$), not desktop_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $desktop_cfg_dir->(@cfg_base);
}

no warnings 'once';
*machine_cfg_dir = \&desktop_cfg_dir;
*xdg_config_dirs = \&desktop_cfg_dir;
use warnings;

=head2 core_cfg_dir

Returns the C<etc> directory below C<$Config{prefix}>.

=cut

my $core_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs = (File::Spec->catdir($Config{prefix}, "etc", @cfg_base));
    @dirs;
};

sub core_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "core_cfg_dir(;\$), not core_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $core_cfg_dir->(@cfg_base);
}

=head2 site_cfg_dir

Returns the C<etc> directory below C<$Config{sitelib_stem}> or the common
base directory of C<$Config{sitelib}> and C<$Config{sitebin}>.

=cut

my $site_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs;

    if ($Config{sitelib_stem})
    {
        push(@dirs, File::Spec->catdir($Config{sitelib_stem}, "etc", @cfg_base));
    }
    else
    {
        my $sitelib_stem = _find_common_base_dir($Config{sitelib}, $Config{sitebin});
        push(@dirs, File::Spec->catdir($sitelib_stem, "etc", @cfg_base));
    }

    @dirs;
};

sub site_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "site_cfg_dir(;\$), not site_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $site_cfg_dir->(@cfg_base);
}

=head2 vendor_cfg_dir

Returns the C<etc> directory below C<$Config{vendorlib_stem}> or the common
base directory of C<$Config{vendorlib}> and C<$Config{vendorbin}>.

=cut

lib/File/ConfigDir.pm  view on Meta::CPAN

=head2 here_cfg_dir

Returns the path for the C<etc> directory below the current working directory.

=cut

my $here_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs = (File::Spec->catdir(File::Spec->rel2abs(File::Spec->curdir()), @cfg_base, "etc"));
    @dirs;
};

sub here_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "here_cfg_dir(;\$), not here_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $here_cfg_dir->(@cfg_base);
}

my $haveFileHomeDir;

BEGIN
{
    # uncoverable branch false
    defined $haveFileHomeDir or $haveFileHomeDir = eval "use File::HomeDir; 1";
    defined $haveFileHomeDir or $haveFileHomeDir = 0;
}

=head2 user_cfg_dir

Returns the users home folder using L<File::HomeDir>. Without
File::HomeDir, nothing is returned.

=cut

my $user_cfg_dir = sub {
    my @cfg_base = @_;
    my @dirs;

    my $homedir;
    $haveFileHomeDir and $homedir = File::HomeDir->my_home();
    $homedir |= $ENV{HOME} if defined $ENV{HOME};
    $homedir |= File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}) if defined $ENV{HOMEDRIVE} and defined $ENV{HOMEPATH};

    $homedir and @dirs = (File::Spec->catdir($homedir, map { "." . $_ } @cfg_base));

    @dirs;
};

sub user_cfg_dir
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "user_cfg_dir(;\$), not user_cfg_dir(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $user_cfg_dir->(@cfg_base);
}

=head2 xdg_config_home

Returns the user configuration directory for desktop applications.
If C<< $ENV{XDG_CONFIG_HOME} >> is not set, for MSWin32 the value
of C<< $ENV{APPDATA} >> is return and on Unices the C<.config> directory
in the users home folder. Without L<File::HomeDir>, on Unices the returned
list might be empty.

=cut

my $xdg_config_home = sub {
    my @cfg_base = @_;
    my @dirs;

    if ($ENV{XDG_CONFIG_HOME})
    {
        @dirs = split(":", $ENV{XDG_CONFIG_HOME});
        @dirs = map { File::Spec->catdir($_, @cfg_base) } @dirs;
    }
    elsif ($^O eq "MSWin32")
    {
        @dirs = (File::Spec->catdir($ENV{APPDATA}, @cfg_base));
    }
    else
    {
        $haveFileHomeDir and @dirs = (File::Spec->catdir(File::HomeDir->my_home(), ".config", @cfg_base));
    }

    @dirs;
};

sub xdg_config_home
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "xdg_config_home(;\$), not xdg_config_home(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    $xdg_config_home->(@cfg_base);
}

my (@extensible_bases, @pure_bases);
push(@extensible_bases,
    $system_cfg_dir, $desktop_cfg_dir, $local_cfg_dir,    $singleapp_cfg_dir, $vendorapp_cfg_dir, $core_cfg_dir,
    $site_cfg_dir,   $vendor_cfg_dir,  $locallib_cfg_dir, $here_cfg_dir,      $user_cfg_dir,      $xdg_config_home);
push(@pure_bases, 3);

=head2 config_dirs

    @cfgdirs = config_dirs();
    @cfgdirs = config_dirs( 'appname' );

Tries to get all available configuration directories as described above.
Returns those who exists and are readable.

=cut

sub config_dirs
{
    my @cfg_base = @_;
    1 < scalar(@cfg_base)
      and croak "config_dirs(;\$), not config_dirs(" . join(",", ("\$") x scalar(@cfg_base)) . ")";
    my @dirs = ();

    my $pure_idx = 0;
    foreach my $idx (0 .. $#extensible_bases)
    {
        my $pure;
        $pure_idx <= $#pure_bases and $idx == $pure_bases[$pure_idx] and $pure = ++$pure_idx;
        push(@dirs, $extensible_bases[$idx]->(($pure ? () : @cfg_base)));
    }

    @dirs = grep { -d $_ && -r $_ } distinct(@dirs);

    @dirs;
}

=head2 _plug_dir_source

    my $dir_src = sub { return _better_config_dir(@_); }
    File::ConfigDir::_plug_dir_source($dir_src);

    my $pure_src = sub { return _better_config_plain_dir(@_); }
    File::ConfigDir::_plug_dir_source($pure_src, 1); # see 2nd arg is true

Registers more sources to ask for suitable directories to check or search
for config files. Each L</config_dirs> will traverse them in subsequent
invocations, too.

Returns the number of directory sources in case of success. Returns nothing
when C<$dir_src> is not a code ref.

=cut

sub _plug_dir_source
{
    my ($dir_source, $pure) = @_;

    $dir_source or return;
    "CODE" eq ref $dir_source or return;

    push(@extensible_bases, $dir_source);
    $pure and push(@pure_bases, $#extensible_bases);
    1;



( run in 0.726 second using v1.01-cache-2.11-cpan-e1769b4cff6 )