package MySQL::Config; # ---------------------------------------------------------------------- # $Id: Config.pm,v 1.2 2003/09/24 12:25:36 dlc Exp $ # ---------------------------------------------------------------------- # MySQL::Config - # Copyright (C) 2003 darren chamberlain # ---------------------------------------------------------------------- use strict; use base qw(Exporter); use vars qw($VERSION $REVISION $GLOBAL_CNF @EXPORT @EXPORT_OK); use Carp qw(carp); use File::Spec; $VERSION = '1.03'; $REVISION = 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 # ------------------------------------------------------------------ $conf_file = "my" unless defined $conf_file && ! ref($conf_file); $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 defined $argv; $argv = [ $argv ] unless ref $argv eq 'ARRAY'; $user_cnf = File::Spec->catfile($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 = map { $_->[0] } @$ini unless @$groups; $groups = join '|', map { quotemeta($_) } @$groups; push @$argv, map { $$argc++; sprintf "--%s=%s", $_->[1], $_->[2] } grep { $_->[0] =~ /$groups/ } @$ini; 1; } sub parse_defaults { my ($conf_file, $groups) = @_; my ($count, $argv, %ini); $argv = [ ]; load_defaults($conf_file, $groups, \$count, $argv); for (@$argv) { next unless /^--(.*?)=(.*)$/; my $name = $1; my $value = $2; if ($value =~ /=/) { $ini{ $name } = { } unless exists($ini{ $name }) && ref($ini{ $name }) eq 'HASH'; my ($subname, $subvalue) = split /\s*=\s*/, $value, 2; $ini{ $name }->{ $subname } = $subvalue; } else { $ini{ $name } = $value; } } return wantarray ? %ini : \%ini; } # ====================================================================== # --- Private Functions --- # ====================================================================== # ---------------------------------------------------------------------- # _parse($file) # # Parses an ini-style file into an array of [ group, name, value ] # array refs. # ---------------------------------------------------------------------- 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 open $file: $!"; return { }; } while () { s/[;#].*$//; s/^\s*//; s/\s*$//; next unless length; /^\s*\[(.*)\]\s*$/ and $current = $1, next; $_ = join '=', $_, 1 unless /=/; my ($n, $v) = split /\s*=\s*/, $_, 2; if ($v =~ /\s/) { $v =~ s/"/\\"/g; $v = qq("$v") } push @$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 C emulates the C function from F. Just like C, it will fill an aray with long options, ready to be parsed by C, a.k.a. C. =head1 THE my.cnf FILE MySQL's F file is a mechanism for storing and reusing command line arguments. These command line arguments are grouped into I using a simple INI-style format: ; file: ~/.my.cnf [client] user = darren host = db1 pager = less -SignMEX [mytop] color = 1 header = 0 Each element in C<[>, C<]> pairs is a I, and each call to C will specify 0 or more groups from which to grab options. For example, grabbing the I group from the above config file would return the I, I, and I items. These items will be formatted as command line options, e.g., I<--user=darren>. =head1 USING MySQL::Config =head2 load_defaults("name", \@groups, \$count, \@ary) C takes 4 arguments: a string denoting the name of the config file (which should generally be I); a reference to an array of groups from which options should be returned; a reference to a scalar that will hold the total number of parsed elements; and a reference to 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 C: --user=username --password=password and so on. If the final array reference is missing, C<@ARGV> will be used. Options will be pushed onto the end of the array, leaving what is already in place undisturbed. The scalar (the third argument to C) will contain the number of elements parsed from the config files. =head2 parse_defaults("name", \@groups) C 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 C, 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", \@groups; C<%cfg> looks like: %cfg = ( "user" => "username", "password" => "password", ) and so on. This might be a more natural interface for some programs; however, C is more true to the original. Because C flattens the arguments into a hash, it makes special allowances for variables that contain multiple C<=>; these are turned into nested hashes. For example, the MySQL's I option can contain name value pairs, like so: set-variable = key_buffer=16M set-variable = max_allowed_packet=1M set-variable = table_cache=64 set-variable = sort_buffer=512K set-variable = net_buffer_length=8K set-variable = myisam_sort_buffer_size=8M These will be turned into a nested hash like this: 'set-variable' => { 'myisam_sort_buffer_size' => '8M', 'sort_buffer' => '512K', 'max_allowed_packet' => '16M', 'key_buffer' => '16M', 'table_cache' => 64, 'net_buffer_length' => '8K' }, This is not done for C, as C will correctly handle variables with embedded C<=> if the option is passed a hash reference. =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 F<~/.${cfg_name}.cnf> and F. If you are using this module for mysql clients, then this should probably remain I. Otherwise, you are free to mangle this however you choose: $ini = parse_defaults 'superapp', [ 'foo' ]; =head1 SUPPORT C is supported by the author. =head1 VERSION This is C, revision $Revision: 1.2 $. =head1 AUTHOR darren chamberlain Edarren@cpan.orgE =head1 COPYRIGHT (C) 2003 darren chamberlain This library is free software; you may distribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L