package MySQL::Config;

use strict;
use base qw(Exporter);
use vars qw($VERSION $GLOBAL_CNF @EXPORT @EXPORT_OK);

use Carp qw(carp);

$VERSION    = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
$GLOBAL_CNF = "/etc/%s.cnf" unless defined $GLOBAL_CNF;
@EXPORT     = qw(load_defaults);
@EXPORT_OK  = qw(parse_defaults);

# ======================================================================
#                        --- Public Functions ---
# ======================================================================

sub load_defaults {
    my ($conf_file, $groups, $argc, $argv) = @_;
    my ($user_cnf, $ini, $field);
    $ini = { };

    # ------------------------------------------------------------------
    # Sanity checking:
    #   # $conf_file should be a string, defaults to "my"
    #   * $groups should be a ref to an array
    #   * $argc should be a ref to a scalar
    #   * $argv should be a ref to an array
    # ------------------------------------------------------------------
    # This silliness is undocumented; please don't rely on it!
    unless (@_ == 4) {
        $argv      = $argc;
        $argc      = $groups;
        $groups    = $conf_file;
        $conf_file = "my";
    }

    $groups = [ $groups ]
        unless ref $groups eq 'ARRAY';

    if (defined $argc) {
        $argc = \$argc
            unless ref $argc eq 'SCALAR';
    } else {
        $argc = \(my $i = 0);
    }

    $argv = [ $argv ]
        unless ref $argv eq 'ARRAY';

    $user_cnf = "$ENV{HOME}/.$conf_file.cnf";

    # ------------------------------------------------------------------
    # Parse the global config and user's config
    # ------------------------------------------------------------------
    _parse($ini, sprintf $GLOBAL_CNF, $conf_file);
    _parse($ini, $user_cnf);

    # ------------------------------------------------------------------
    # Pull out the appropriate pieces, based on @$groups
    # ------------------------------------------------------------------
    @$groups = keys %$ini unless @$groups;

    for $field (@$groups) {
        my @keys = reverse keys %{ $ini->{ $field } };
        for (@keys) {
            unshift @$argv, 
                sprintf "--%s=%s", $_, $ini->{ $field }->{ $_ };
            $$argc++;
        }
    }

    1;
}

sub parse_defaults {
    my ($conf_file, $groups) = @_;
    my ($count, $argv, %ini);
    $argv = [ ];

    unless (@_ == 2) {
        $groups = $conf_file;
        $conf_file = "my";
    }

    load_defaults($conf_file, $groups, \$count, $argv);

    for (@$argv) {
        /^--(.*)=(.*)$/
            and $ini{ $1 } = $2;
    }

    return wantarray ? %ini : \%ini;
}

# ======================================================================
#                        --- Private Functions ---
# ======================================================================

# ----------------------------------------------------------------------
# _parse($file)
#
# Parses an ini-style file
# ----------------------------------------------------------------------
sub _parse {
    my $ini = shift;
    my $file = shift;
    my $current;
    local ($_, *INI);

    return { } unless -f $file && -r _;

    $ini ||= { };
    unless (open INI, $file) {
        carp "Couldn't oprn $file: $!";
        return { };
    }
    while (<INI>) {
        s/[;#].*$//;
        s/^\s*//;
        s/\s*$//;

        next unless length;

        /^\s*\[(.*)\]\s*$/
            and $current = $1, next;

        my ($n, $v) = split /\s*=\s*/;
        $v = qq("$v") if $v =~ /\s/;

        $ini->{ $current }->{ $n } = $v;
    }

    unless (close INI) {
        carp "Couldn't close $file: $!";
    }

    return $ini;
}

1;

__END__

=head1 NAME

MySQL::Config - Parse and utilize MySQL's /etc/my.cnf and ~/.my.cnf files

=head1 SYNOPSIS

    use MySQL::Config;

    my @groups = qw(client myclient);
    my $argc = 0;
    my @argv = ();

    load_defaults "my", @groups, $argc, @argv;

=head1 DESCRIPTION

MySQL::Config emulates the load_defaults() function from mysqlclient.
Just like load_defaults(), it returns a list primed to be passed to
getopt_long(), a.k.a. Getopt::Long.  load_defaults takes 4 arguments:
a string denoting the name of the config file (which should generally
be "my"); an array of groups which should be returned; a scalar that
will hold the total number of parsed elements; and an array that will
hold the final versions of the extracted name, value pairs.  This
final array will be in a format suitable for processing with
Getopt::Long:

    --user=username
    --password=password

and so on.

load_defaults() has an un-Perlish interface, mostly because it is
exactly the same signature as the version from the C API.  There is
also a function, not exported by default, called parse_defaults(),
which returns a hash of parsed (name, value) pairs (or a hashref
in scalar context):

    use MySQL::Config qw(parse_defaults);

    my %cfg = parse_defaults("my", [ qw(client myclient) ]);

%cfg looks like:

    %cfg = (
        "user" => "username",
        "password" => "password",
    )

and so on.  This might be a more natural interface for some programs;
however, load_defaults() is more true to the original.

=head1 USING SOMETHING OTHER THAN "my" AS THE FIRST STRING

This string controls the name of the configuration file; the names
work out to, basically:

    ~/.${cfg_name}.cnf

and

    /etc/${cnf_name}.cnf

If you are using this module for mysql clients, then this should
probably remain my.  Otherwise, you are free to mangle this however
you choose.

    $ini = parse_defaults("your", [ "foo" ]);

=head1 BUGS / KNOWN ISSUES

The C version of load_defaults() returns elements in the order in
which they are defined in the file; this version returns them in hash
order, with duplicates removed.

=head1 VERSION

$Revision: 1.2 $

=head1 AUTHOR

darren chamberlain E<lt>darren@cpan.orgE<gt>
