package Config::Find;

our $VERSION = '0.26';

use strict;
use warnings;

use Carp;

# selects implementation module:
our @ISA;
BEGIN {
    if ($^O=~/Win32/) {
	require Win32;
	my $OS=uc Win32::GetOSName();
	
	if ($OS=~/^WIN95/) {
	    require Config::Find::Win95;
	    @ISA=qw(Config::Find::Win95);
	}
	elsif ($OS=~/^WIN98/) {
	    require Config::Find::Win98;
	    @ISA=qw(Config::Find::Win98);
	}
	elsif ($OS=~/^WINME/) {
	    require Config::Find::WinME;
	    @ISA=qw(Config::Find::WinME);
	}
	elsif ($OS=~/^WINNT/) {
	    require Config::Find::WinNT;
	    @ISA=qw(Config::Find::WinNT);
	}
	elsif ($OS=~/^WIN2000/) {
	    require Config::Find::Win2k;
	    @ISA=qw(Config::Find::Win2k);
	}
	elsif ($OS=~/^WIN2003/) {
	    require Config::Find::Win2k3;
	    @ISA=qw(Config::Find::Win2k3);
	}
	elsif ($OS=~/^WINXP/) {
	    require Config::Find::WinXP;
	    @ISA=qw(Config::Find::WinXP);
	}
	elsif ($OS=~/^WINCE/) {
	    require Config::Find::WinCE;
	    @ISA=qw(Config::Find::WinCE);
	}
	else {
	    croak "Unknow MSWin32 OS '$OS'";
	}
    }
    else {
	require Config::Find::Unix;
	@ISA=qw(Config::Find::Unix);
    }
}

sub find {
    my $class=shift;
    my ($write, $global, $fn, @names)=$class->parse_opts(@_);
    if (defined $fn) {
      return ($write or -f $fn) ? $fn : undef;
    }
    $class->_find($write, $global, @names);
}

sub open {
    my $class=shift;
    my ($write, $global, $fn, @names)=$class->parse_opts(@_);
    defined($fn) or $fn=$class->_find($write, $global, @names);
    $class->_open($write, $global, $fn);
}

sub install {
    my $class=shift;
    my $orig=shift;
    my ($write, $global, $fn, @names)=$class->parse_opts( mode => 'w',
							  @_);
    defined($fn) or $fn=$class->_find($write, $global, @names);
    $class->_install($orig, $write, $global, $fn);
}

sub parse_opts {
    my ($class, %opts)=@_;
    my $fn=$opts{file};
    my @names;
    if (exists $opts{name}) {
	@names=$opts{name};
    }
    elsif (exists $opts{names}) {
	UNIVERSAL::isa($opts{names}, 'ARRAY')
	    or croak "invalid argument for 'names', expecting an array ref";
	@names=@{$opts{names}}
    }
    else {
	@names=$class->guess_script_name();
    }
    my $write;
    if (exists $opts{mode}) {
	if ($opts{mode}=~/^r(ead)?$/i) {
	    # yes, do nothing!
	}
	elsif ($opts{mode}=~/w(rite)?$/i) {
	    $write=1;
	}
	else {
	    croak "invalid option mode => '$opts{mode}'";
	}
    }
    my $global;
    if (exists $opts{scope}) {
	if ($opts{scope}=~/^u(ser)?$/i) {
	    # yes, do nothing!
	}
	elsif ($opts{scope}=~/g(lobal)?$/i) {
	    $global=1;
	}
	else {
	    croak "invalid option scope => '$opts{scope}'";
	}
    }
    return ($write, $global, $fn, @names)
}

1;
__END__

=head1 NAME

Config::Find - Find configuration files in the native OS fashion

=head1 SYNOPSIS

  use Config::Find;

  my $filename=Config::Find->find;

  ...

  my $fn_foo=Config::Find->find( name => 'my_app/foo',
                                 mode => 'write',
                                 scope => 'user' );

  my $fn_bar=Config::Find->find( names => [qw(my_app/bar appbar)] );

  my $fh=Config::Find->open( name => 'foo',
                             scope => 'global',
                             mode => 'w' )


  my $fn=Config::Find->install( 'original/config/file.conf',
                                name => 'foo' );

  my $fn=Config::Find->find( file => $opt_c,
                             name => foo );

=head1 ABSTRACT

Config::Find searchs for configuration files using OS dependant
heuristics.

=head1 DESCRIPTION

Every OS has different rules for configuration files placement, this
module allows to easily find and create your app configuration files
following those rules.

Config::Find references configuration files by the application name or
by the application name and the configuration file name when the app
uses several application files, i.e C<emacs>, C<profile>,
C<apache/httpd>, C<apache/ssl>.

By default the $0 value is used to generate the configuration file
name. To define it explicitly the keywords C<name> or C<names> have to
be used:

=over 4

=item name => C<name> or C<app/file>

picks the first configuration file matching that name.

=item names => [qw(foo bar foo/bar)]

picks the first configuration file matching any of the names passed.

=back

Alternatively, the exact position for the file can be specified with
the C<file> keyword:

=over 4

=item file => C</config/file/name.conf>

explicit position of the configuration file.

If undef is passed this entry is ignored and the search for the
configuration file continues with the apropiate OS rules. This allows
for:

  use Config::Find;
  use Getopt::Std;

  our $opt_c;
  getopts('c:');

  my $fn=Config::Find->find(file => $opt_c)

=back

Methods in this package also accept the optional arguments C<scope>
and C<mode>:

=over 4

=item scope => C<user> or C<global>

Configuration files can be private to the application user or global
to the OS, i.e. in unix there is the global C</etc/profile> and the
user C<~/.profile>.

=item mode => C<read> or C<write>

In C<read> mode already existant file names are returned, in C<write>
mode the file names point to where the configuration file has to be
stored.

=back

=head2 METHODS

All the methods in this package are class methods (you don't need an
object to call them).

=over 4

=item $fn=Config::Find-E<gt>find(%opts)

returns the name of the configuration file.

=item $fh=Config::Find-E<gt>open(%opts)

returns a open file handle for the configuration file. In write mode,
the file and any nonexistant parent directories are created.

=item $fn=Config::Find-E<gt>install($original, %opts)

copies a configuration file to a convenient place.

=back

=head2 EXPORT

None, this module has an OO interface.

=head1 BUGS

Some Win32 OSs are not completely implemented and default to inferior
modes, but hey, this is a work in progress!!!

Contributions, bug reports, feedback and any kind of comments are
welcome.

=head1 SEE ALSO

L<Config::Find::Unix>, L<Config::Find::Win32> for descriptions of the
heuristics used to find the configuration files.

L<Config::Find::Any> for information about adding support for a new
OS.

L<Config::Auto> give me the idea for this module.

=head1 COPYRIGHT AND LICENSE

Copyright 2003-2008 by Salvador FandiE<ntilde>o GarcE<iacute>a
(sfandino@yahoo.com)

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
