--- /dev/null
+#
+# Config::General.pm - Generic Config Module
+#
+# Purpose: Provide a convenient way for loading
+# config values from a given file and
+# return it as hash structure
+#
+# Copyright (c) 2000-2009 Thomas Linden <tlinden |AT| cpan.org>.
+# All Rights Reserved. Std. disclaimer applies.
+# Artificial License, same as perl itself. Have fun.
+#
+# namespace
+package Config::General;
+
+use strict;
+use warnings;
+use English '-no_match_vars';
+
+use IO::File;
+use FileHandle;
+use File::Spec::Functions qw(splitpath file_name_is_absolute catfile catpath);
+use File::Glob qw/:glob/;
+
+
+# on debian with perl > 5.8.4 croak() doesn't work anymore without this.
+# There is some require statement which dies 'cause it can't find Carp::Heavy,
+# I really don't understand, what the hell they made, but the debian perl
+# installation is definetly bullshit, damn!
+use Carp::Heavy;
+
+
+use Carp;
+use Exporter;
+
+$Config::General::VERSION = 2.44;
+
+use vars qw(@ISA @EXPORT_OK);
+use base qw(Exporter);
+@EXPORT_OK = qw(ParseConfig SaveConfig SaveConfigString);
+
+sub new {
+ #
+ # create new Config::General object
+ #
+ my($this, @param ) = @_;
+ my $class = ref($this) || $this;
+
+ # define default options
+ my $self = {
+ # sha256 of current date
+ # hopefully this lowers the probability that
+ # this matches any configuration key or value out there
+ # bugfix for rt.40925
+ EOFseparator => 'ad7d7b87f5b81d2a0d5cb75294afeb91aa4801b1f8e8532dc1b633c0e1d47037',
+ SlashIsDirectory => 0,
+ AllowMultiOptions => 1,
+ MergeDuplicateOptions => 0,
+ MergeDuplicateBlocks => 0,
+ LowerCaseNames => 0,
+ ApacheCompatible => 0,
+ UseApacheInclude => 0,
+ IncludeRelative => 0,
+ IncludeDirectories => 0,
+ IncludeGlob => 0,
+ IncludeAgain => 0,
+ AutoLaunder => 0,
+ AutoTrue => 0,
+ AutoTrueFlags => {
+ true => '^(on|yes|true|1)$',
+ false => '^(off|no|false|0)$',
+ },
+ DefaultConfig => {},
+ String => '',
+ level => 1,
+ InterPolateVars => 0,
+ InterPolateEnv => 0,
+ ExtendedAccess => 0,
+ SplitPolicy => 'guess', # also possible: whitespace, equalsign and custom
+ SplitDelimiter => 0, # must be set by the user if SplitPolicy is 'custom'
+ StoreDelimiter => 0, # will be set by me unless user uses 'custom' policy
+ CComments => 1, # by default turned on
+ BackslashEscape => 0, # by default turned off, allows escaping anything using the backslash
+ StrictObjects => 1, # be strict on non-existent keys in OOP mode
+ StrictVars => 1, # be strict on undefined variables in Interpolate mode
+ Tie => q(), # could be set to a perl module for tie'ing new hashes
+ parsed => 0, # internal state stuff for variable interpolation
+ files => {}, # which files we have read, if any
+ UTF8 => 0,
+ SaveSorted => 0
+ };
+
+ # create the class instance
+ bless $self, $class;
+
+ if ($#param >= 1) {
+ # use of the new hash interface!
+ $self->_prepare(@param);
+ }
+ elsif ($#param == 0) {
+ # use of the old style
+ $self->{ConfigFile} = $param[0];
+ if (ref($self->{ConfigFile}) eq 'HASH') {
+ $self->{ConfigHash} = delete $self->{ConfigFile};
+ }
+ }
+ else {
+ # this happens if $#param == -1,1 thus no param was given to new!
+ $self->{config} = $self->_hashref();
+ $self->{parsed} = 1;
+ }
+
+ # find split policy to use for option/value separation
+ $self->_splitpolicy();
+
+ # bless into variable interpolation module if neccessary
+ $self->_blessvars();
+
+ # process as usual
+ if (!$self->{parsed}) {
+ $self->_process();
+ }
+
+ if ($self->{InterPolateVars}) {
+ $self->{config} = $self->_clean_stack($self->{config});
+ }
+
+ # bless into OOP namespace if required
+ $self->_blessoop();
+
+ return $self;
+}
+
+
+
+sub _process {
+ #
+ # call _read() and _parse() on the given config
+ my($self) = @_;
+
+ if ($self->{DefaultConfig} && $self->{InterPolateVars}) {
+ $self->{DefaultConfig} = $self->_interpolate_hash($self->{DefaultConfig}); # FIXME: _hashref() ?
+ }
+ if (exists $self->{StringContent}) {
+ # consider the supplied string as config file
+ $self->_read($self->{StringContent}, 'SCALAR');
+ $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
+ }
+ elsif (exists $self->{ConfigHash}) {
+ if (ref($self->{ConfigHash}) eq 'HASH') {
+ # initialize with given hash
+ $self->{config} = $self->{ConfigHash};
+ $self->{parsed} = 1;
+ }
+ else {
+ croak "Config::General: Parameter -ConfigHash must be a hash reference!\n";
+ }
+ }
+ elsif (ref($self->{ConfigFile}) eq 'GLOB' || ref($self->{ConfigFile}) eq 'FileHandle') {
+ # use the file the glob points to
+ $self->_read($self->{ConfigFile});
+ $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
+ }
+ else {
+ if ($self->{ConfigFile}) {
+ # open the file and read the contents in
+ $self->{configfile} = $self->{ConfigFile};
+ if ( file_name_is_absolute($self->{ConfigFile}) ) {
+ # look if is is an absolute path and save the basename if it is absolute
+ my ($volume, $path, undef) = splitpath($self->{ConfigFile});
+ $path =~ s#/$##; # remove eventually existing trailing slash
+ if (! $self->{ConfigPath}) {
+ $self->{ConfigPath} = [];
+ }
+ unshift @{$self->{ConfigPath}}, catpath($volume, $path, q());
+ }
+ $self->_open($self->{configfile});
+ # now, we parse immdediately, getall simply returns the whole hash
+ $self->{config} = $self->_hashref();
+ $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
+ }
+ else {
+ # hm, no valid config file given, so try it as an empty object
+ $self->{config} = $self->_hashref();
+ $self->{parsed} = 1;
+ }
+ }
+}
+
+
+sub _blessoop {
+ #
+ # bless into ::Extended if neccessary
+ my($self) = @_;
+ if ($self->{ExtendedAccess}) {
+ # we are blessing here again, to get into the ::Extended namespace
+ # for inheriting the methods available overthere, which we doesn't have.
+ bless $self, 'Config::General::Extended';
+ eval {
+ require Config::General::Extended;
+ };
+ if ($EVAL_ERROR) {
+ croak "Config::General: " . $EVAL_ERROR;
+ }
+ }
+# return $self;
+}
+
+sub _blessvars {
+ #
+ # bless into ::Interpolated if neccessary
+ my($self) = @_;
+ if ($self->{InterPolateVars} || $self->{InterPolateEnv}) {
+ # InterPolateEnv implies InterPolateVars
+ $self->{InterPolateVars} = 1;
+
+ # we are blessing here again, to get into the ::InterPolated namespace
+ # for inheriting the methods available overthere, which we doesn't have here.
+ bless $self, 'Config::General::Interpolated';
+ eval {
+ require Config::General::Interpolated;
+ };
+ if ($EVAL_ERROR) {
+ croak "Config::General: " . $EVAL_ERROR;
+ }
+ # pre-compile the variable regexp
+ $self->{regex} = $self->_set_regex();
+ }
+# return $self;
+}
+
+
+sub _splitpolicy {
+ #
+ # find out what split policy to use
+ my($self) = @_;
+ if ($self->{SplitPolicy} ne 'guess') {
+ if ($self->{SplitPolicy} eq 'whitespace') {
+ $self->{SplitDelimiter} = '\s+';
+ if (!$self->{StoreDelimiter}) {
+ $self->{StoreDelimiter} = q( );
+ }
+ }
+ elsif ($self->{SplitPolicy} eq 'equalsign') {
+ $self->{SplitDelimiter} = '\s*=\s*';
+ if (!$self->{StoreDelimiter}) {
+ $self->{StoreDelimiter} = ' = ';
+ }
+ }
+ elsif ($self->{SplitPolicy} eq 'custom') {
+ if (! $self->{SplitDelimiter} ) {
+ croak "Config::General: SplitPolicy set to 'custom' but no SplitDelimiter set.\n";
+ }
+ }
+ else {
+ croak "Config::General: Unsupported SplitPolicy: $self->{SplitPolicy}.\n";
+ }
+ }
+ else {
+ if (!$self->{StoreDelimiter}) {
+ $self->{StoreDelimiter} = q( );
+ }
+ }
+}
+
+sub _prepare {
+ #
+ # prepare the class parameters, mangle them, if there
+ # are options to reset or to override, do it here.
+ my ($self, %conf) = @_;
+
+ # save the parameter list for ::Extended's new() calls
+ $self->{Params} = \%conf;
+
+ # be backwards compatible
+ if (exists $conf{-file}) {
+ $self->{ConfigFile} = delete $conf{-file};
+ }
+ if (exists $conf{-hash}) {
+ $self->{ConfigHash} = delete $conf{-hash};
+ }
+
+ # store input, file, handle, or array
+ if (exists $conf{-ConfigFile}) {
+ $self->{ConfigFile} = delete $conf{-ConfigFile};
+ }
+ if (exists $conf{-ConfigHash}) {
+ $self->{ConfigHash} = delete $conf{-ConfigHash};
+ }
+
+ # store search path for relative configs, if any
+ if (exists $conf{-ConfigPath}) {
+ my $configpath = delete $conf{-ConfigPath};
+ $self->{ConfigPath} = ref $configpath eq 'ARRAY' ? $configpath : [$configpath];
+ }
+
+ # handle options which contains values we need (strings, hashrefs or the like)
+ if (exists $conf{-String} ) {
+ #if (ref(\$conf{-String}) eq 'SCALAR') {
+ if (not ref $conf{-String}) {
+ if ( $conf{-String}) {
+ $self->{StringContent} = $conf{-String};
+ }
+ delete $conf{-String};
+ }
+ # re-implement arrayref support, removed after 2.22 as _read were
+ # re-organized
+ # fixed bug#33385
+ elsif(ref($conf{-String}) eq 'ARRAY') {
+ $self->{StringContent} = join "\n", @{$conf{-String}};
+ }
+ else {
+ croak "Config::General: Parameter -String must be a SCALAR or ARRAYREF!\n";
+ }
+ delete $conf{-String};
+ }
+ if (exists $conf{-Tie}) {
+ if ($conf{-Tie}) {
+ $self->{Tie} = delete $conf{-Tie};
+ $self->{DefaultConfig} = $self->_hashref();
+ }
+ }
+
+ if (exists $conf{-FlagBits}) {
+ if ($conf{-FlagBits} && ref($conf{-FlagBits}) eq 'HASH') {
+ $self->{FlagBits} = 1;
+ $self->{FlagBitsFlags} = $conf{-FlagBits};
+ }
+ delete $conf{-FlagBits};
+ }
+
+ if (exists $conf{-DefaultConfig}) {
+ if ($conf{-DefaultConfig} && ref($conf{-DefaultConfig}) eq 'HASH') {
+ $self->{DefaultConfig} = $conf{-DefaultConfig};
+ }
+ elsif ($conf{-DefaultConfig} && ref($conf{-DefaultConfig}) eq q()) {
+ $self->_read($conf{-DefaultConfig}, 'SCALAR');
+ $self->{DefaultConfig} = $self->_parse($self->_hashref(), $self->{content});
+ $self->{content} = ();
+ }
+ delete $conf{-DefaultConfig};
+ }
+
+ # handle options which may either be true or false
+ # allowing "human" logic about what is true and what is not
+ foreach my $entry (keys %conf) {
+ my $key = $entry;
+ $key =~ s/^\-//;
+ if (! exists $self->{$key}) {
+ croak "Config::General: Unknown parameter: $entry => \"$conf{$entry}\" (key: <$key>)\n";
+ }
+ if ($conf{$entry} =~ /$self->{AutoTrueFlags}->{true}/io) {
+ $self->{$key} = 1;
+ }
+ elsif ($conf{$entry} =~ /$self->{AutoTrueFlags}->{false}/io) {
+ $self->{$key} = 0;
+ }
+ else {
+ # keep it untouched
+ $self->{$key} = $conf{$entry};
+ }
+ }
+
+ if ($self->{MergeDuplicateOptions}) {
+ # override if not set by user
+ if (! exists $conf{-AllowMultiOptions}) {
+ $self->{AllowMultiOptions} = 0;
+ }
+ }
+
+ if ($self->{ApacheCompatible}) {
+ # turn on all apache compatibility options which has
+ # been incorporated during the years...
+ $self->{UseApacheInclude} = 1;
+ $self->{IncludeRelative} = 1;
+ $self->{IncludeDirectories} = 1;
+ $self->{IncludeGlob} = 1;
+ $self->{SlashIsDirectory} = 1;
+ $self->{SplitPolicy} = 'whitespace';
+ $self->{CComments} = 0;
+ $self->{BackslashEscape} = 1;
+ }
+}
+
+sub getall {
+ #
+ # just return the whole config hash
+ #
+ my($this) = @_;
+ return (exists $this->{config} ? %{$this->{config}} : () );
+}
+
+
+sub files {
+ #
+ # return a list of files opened so far
+ #
+ my($this) = @_;
+ return (exists $this->{files} ? keys %{$this->{files}} : () );
+}
+
+
+sub _open {
+ #
+ # open the config file, or expand a directory or glob
+ #
+ my($this, $basefile, $basepath) = @_;
+ my($fh, $configfile);
+
+ if($basepath) {
+ # if this doesn't work we can still try later the global config path to use
+ $configfile = catfile($basepath, $basefile);
+ }
+ else {
+ $configfile = $basefile;
+ }
+
+ if ($this->{IncludeGlob} and $configfile =~ /[*?\[\{\\]/) {
+ # Something like: *.conf (or maybe dir/*.conf) was included; expand it and
+ # pass each expansion through this method again.
+ my @include = grep { -f $_ } bsd_glob($configfile, GLOB_BRACE | GLOB_QUOTE);
+
+ # applied patch by AlexK fixing rt.cpan.org#41030
+ if ( !@include && defined $this->{ConfigPath} ) {
+ foreach my $dir (@{$this->{ConfigPath}}) {
+ my ($volume, $path, undef) = splitpath($basefile);
+ if ( -d catfile( $dir, $path ) ) {
+ push @include, grep { -f $_ } bsd_glob(catfile($dir, $basefile), GLOB_BRACE | GLOB_QUOTE);
+ last;
+ }
+ }
+ }
+
+ if (@include == 1) {
+ $configfile = $include[0];
+ }
+ else {
+ # Multiple results or no expansion results (which is fine,
+ # include foo/* shouldn't fail if there isn't anything matching)
+ local $this->{IncludeGlob};
+ for (@include) {
+ $this->_open($_);
+ }
+ return;
+ }
+ }
+
+ if (!-e $configfile) {
+ my $found;
+ if (defined $this->{ConfigPath}) {
+ # try to find the file within ConfigPath
+ foreach my $dir (@{$this->{ConfigPath}}) {
+ if( -e catfile($dir, $basefile) ) {
+ $configfile = catfile($dir, $basefile);
+ $found = 1;
+ last; # found it
+ }
+ }
+ }
+ if (!$found) {
+ my $path_message = defined $this->{ConfigPath} ? q( within ConfigPath: ) . join(q(.), @{$this->{ConfigPath}}) : q();
+ croak qq{Config::General The file "$basefile" does not exist$path_message!};
+ }
+ }
+
+ local ($RS) = $RS;
+ if (! $RS) {
+ carp(q(\$RS (INPUT_RECORD_SEPARATOR) is undefined. Guessing you want a line feed character));
+ $RS = "\n";
+ }
+
+ if (-d $configfile and $this->{IncludeDirectories}) {
+ # A directory was included; include all the files inside that directory in ASCII order
+ local *INCLUDEDIR;
+ opendir INCLUDEDIR, $configfile or croak "Config::General: Could not open directory $configfile!($!)\n";
+ my @files = sort grep { -f catfile($configfile, $_) } catfile($configfile, $_), readdir INCLUDEDIR;
+ closedir INCLUDEDIR;
+ local $this->{CurrentConfigFilePath} = $configfile;
+ for (@files) {
+ my $file = catfile($configfile, $_);
+ if (! exists $this->{files}->{$file} or $this->{IncludeAgain} ) {
+ # support re-read if used urged us to do so, otherwise ignore the file
+ if ($this->{UTF8}) {
+ $fh = new IO::File;
+ open( $fh, "<:utf8", $file)
+ or croak "Config::General: Could not open $file in UTF8 mode!($!)\n";
+ }
+ else {
+ $fh = IO::File->new( $file, 'r') or croak "Config::General: Could not open $file!($!)\n";
+ }
+ $this->{files}->{"$file"} = 1;
+ $this->_read($fh);
+ }
+ else {
+ warn "File $file already loaded. Use -IncludeAgain to load it again.\n";
+ }
+ }
+ }
+ elsif (-e _) {
+ if (exists $this->{files}->{$configfile} and not $this->{IncludeAgain}) {
+ # do not read the same file twice, just return
+ warn "File $configfile already loaded. Use -IncludeAgain to load it again.\n";
+ return;
+ }
+ else {
+ if ($this->{UTF8}) {
+ $fh = new IO::File;
+ open( $fh, "<:utf8", $configfile)
+ or croak "Config::General: Could not open $configfile in UTF8 mode!($!)\n";
+ }
+ else {
+ $fh = IO::File->new( "$configfile", 'r')
+ or croak "Config::General: Could not open $configfile!($!)\n";
+ }
+
+ $this->{files}->{$configfile} = 1;
+
+ my ($volume, $path, undef) = splitpath($configfile);
+ local $this->{CurrentConfigFilePath} = catpath($volume, $path, q());
+
+ $this->_read($fh);
+ }
+ }
+ return;
+}
+
+
+sub _read {
+ #
+ # store the config contents in @content
+ # and prepare it somewhat for easier parsing later
+ # (comments, continuing lines, and stuff)
+ #
+ my($this, $fh, $flag) = @_;
+ my(@stuff, @content, $c_comment, $longline, $hier, $hierend, @hierdoc);
+ local $_ = q();
+
+ if ($flag && $flag eq 'SCALAR') {
+ if (ref($fh) eq 'ARRAY') {
+ @stuff = @{$fh};
+ }
+ else {
+ @stuff = split /\n/, $fh;
+ }
+ }
+ else {
+ @stuff = <$fh>;
+ }
+
+ foreach (@stuff) {
+ if ($this->{AutoLaunder}) {
+ if (m/^(.*)$/) {
+ $_ = $1;
+ }
+ }
+
+ chomp;
+
+ if ($this->{CComments}) {
+ # look for C-Style comments, if activated
+ if (/(\s*\/\*.*\*\/\s*)/) {
+ # single c-comment on one line
+ s/\s*\/\*.*\*\/\s*//;
+ }
+ elsif (/^\s*\/\*/) {
+ # the beginning of a C-comment ("/*"), from now on ignore everything.
+ if (/\*\/\s*$/) {
+ # C-comment end is already there, so just ignore this line!
+ $c_comment = 0;
+ }
+ else {
+ $c_comment = 1;
+ }
+ }
+ elsif (/\*\//) {
+ if (!$c_comment) {
+ warn "invalid syntax: found end of C-comment without previous start!\n";
+ }
+ $c_comment = 0; # the current C-comment ends here, go on
+ s/^.*\*\///; # if there is still stuff, it will be read
+ }
+ next if($c_comment); # ignore EVERYTHING from now on, IF it IS a C-Comment
+ }
+
+
+ if ($hier) {
+ # inside here-doc, only look for $hierend marker
+ if (/^(\s*)\Q$hierend\E\s*$/) {
+ my $indent = $1; # preserve indentation
+ $hier .= ' ' . $this->{EOFseparator}; # bugfix of rt.40925
+ # _parse will also preserver indentation
+ if ($indent) {
+ foreach (@hierdoc) {
+ s/^$indent//; # i.e. the end was: " EOF" then we remove " " from every here-doc line
+ $hier .= $_ . "\n"; # and store it in $hier
+ }
+ }
+ else {
+ $hier .= join "\n", @hierdoc; # there was no indentation of the end-string, so join it 1:1
+ }
+ push @{$this->{content}}, $hier; # push it onto the content stack
+ @hierdoc = ();
+ undef $hier;
+ undef $hierend;
+ }
+ else {
+ # everything else onto the stack
+ push @hierdoc, $_;
+ }
+ next;
+ }
+
+ ###
+ ### non-heredoc entries from now on
+ ##
+
+ # Remove comments and empty lines
+ s/(?<!\\)#.*$//; # .+ => .* bugfix rt.cpan.org#44600
+ next if /^\s*#/;
+ next if /^\s*$/;
+
+
+ # look for multiline option, indicated by a trailing backslash
+ my $extra = $this->{BackslashEscape} ? '(?<!\\\\)' : q();
+ if (/$extra\\$/) {
+ chop;
+ s/^\s*//;
+ $longline .= $_;
+ next;
+ }
+
+ # remove the \ from all characters if BackslashEscape is turned on
+ # FIXME (rt.cpan.org#33218
+ if ($this->{BackslashEscape}) {
+ s/\\(.)/$1/g;
+ }
+ else {
+ # remove the \ char in front of masked "#", if any
+ s/\\#/#/g;
+ }
+
+
+ # transform explicit-empty blocks to conforming blocks
+ if (!$this->{ApacheCompatible} && /\s*<([^\/]+?.*?)\/>$/) {
+ my $block = $1;
+ if ($block !~ /\"/) {
+ if ($block !~ /\s[^\s]/) {
+ # fix of bug 7957, add quotation to pure slash at the
+ # end of a block so that it will be considered as directory
+ # unless the block is already quoted or contains whitespaces
+ # and no quotes.
+ if ($this->{SlashIsDirectory}) {
+ push @{$this->{content}}, '<' . $block . '"/">';
+ next;
+ }
+ }
+ }
+ my $orig = $_;
+ $orig =~ s/\/>$/>/;
+ $block =~ s/\s\s*.*$//;
+ push @{$this->{content}}, $orig, "</${block}>";
+ next;
+ }
+
+
+ # look for here-doc identifier
+ if ($this->{SplitPolicy} eq 'guess') {
+ if (/^\s*([^=]+?)\s*=\s*<<\s*(.+?)\s*$/) {
+ # try equal sign (fix bug rt#36607)
+ $hier = $1; # the actual here-doc variable name
+ $hierend = $2; # the here-doc identifier, i.e. "EOF"
+ next;
+ }
+ elsif (/^\s*(\S+?)\s+<<\s*(.+?)\s*$/) {
+ # try whitespace
+ $hier = $1; # the actual here-doc variable name
+ $hierend = $2; # the here-doc identifier, i.e. "EOF"
+ next;
+ }
+ }
+ else {
+ # no guess, use one of the configured strict split policies
+ if (/^\s*(.+?)($this->{SplitDelimiter})<<\s*(.+?)\s*$/) {
+ $hier = $1; # the actual here-doc variable name
+ $hierend = $3; # the here-doc identifier, i.e. "EOF"
+ next;
+ }
+ }
+
+
+
+ ###
+ ### any "normal" config lines from now on
+ ###
+
+ if ($longline) {
+ # previous stuff was a longline and this is the last line of the longline
+ s/^\s*//;
+ $longline .= $_;
+ push @{$this->{content}}, $longline; # push it onto the content stack
+ undef $longline;
+ next;
+ }
+ else {
+ # look for include statement(s)
+ my $incl_file;
+ my $path = '';
+ if ( $this->{IncludeRelative} and defined $this->{CurrentConfigFilePath}) {
+ $path = $this->{CurrentConfigFilePath};
+ }
+ elsif (defined $this->{ConfigPath}) {
+ # fetch pathname of base config file, assuming the 1st one is the path of it
+ $path = $this->{ConfigPath}->[0];
+ }
+
+ # bugfix rt.cpan.org#38635: support quoted filenames
+ if ($this->{UseApacheInclude}) {
+ if (/^\s*include\s*(["'])(.*?)(?<!\\)\1$/i) {
+ $incl_file = $2;
+ }
+ elsif (/^\s*include\s+(.+?)\s*$/i) {
+ $incl_file = $1;
+ }
+ }
+ else {
+ if (/^\s*<<include\s+(.+?)>>\s*$/i) {
+ $incl_file = $1;
+ }
+ }
+
+ if ($incl_file) {
+ if ( $this->{IncludeRelative} && $path && !file_name_is_absolute($incl_file) ) {
+ # include the file from within location of $this->{configfile}
+ $this->_open( $incl_file, $path );
+ }
+ else {
+ # include the file from within pwd, or absolute
+ $this->_open($incl_file);
+ }
+ }
+ else {
+ # standard entry, (option = value)
+ push @{$this->{content}}, $_;
+ }
+
+ }
+
+ }
+ return 1;
+}
+
+
+
+
+
+sub _parse {
+ #
+ # parse the contents of the file
+ #
+ my($this, $config, $content) = @_;
+ my(@newcontent, $block, $blockname, $chunk,$block_level);
+ local $_;
+
+ foreach (@{$content}) { # loop over content stack
+ chomp;
+ $chunk++;
+ $_ =~ s/^\s+//; # strip spaces @ end and begin
+ $_ =~ s/\s+$//;
+
+ #
+ # build option value assignment, split current input
+ # using whitespace, equal sign or optionally here-doc
+ # separator EOFseparator
+ my ($option,$value);
+ if (/$this->{EOFseparator}/) {
+ ($option,$value) = split /\s*$this->{EOFseparator}\s*/, $_, 2; # separated by heredoc-finding in _open()
+ }
+ else {
+ if ($this->{SplitPolicy} eq 'guess') {
+ # again the old regex. use equalsign SplitPolicy to get the
+ # 2.00 behavior. the new regexes were too odd.
+ ($option,$value) = split /\s*=\s*|\s+/, $_, 2;
+ }
+ else {
+ # no guess, use one of the configured strict split policies
+ ($option,$value) = split /$this->{SplitDelimiter}/, $_, 2;
+ }
+ }
+
+ if ($value && $value =~ /^"/ && $value =~ /"$/) {
+ $value =~ s/^"//; # remove leading and trailing "
+ $value =~ s/"$//;
+ }
+ if (! defined $block) { # not inside a block @ the moment
+ if (/^<([^\/]+?.*?)>$/) { # look if it is a block
+ $block = $1; # store block name
+ if ($block =~ /^"([^"]+)"$/) {
+ # quoted block, unquote it and do not split
+ $block =~ s/"//g;
+ }
+ else {
+ # If it is a named block store the name separately; allow the block and name to each be quoted
+ if ($block =~ /^(?:"([^"]+)"|(\S+))(?:\s+(?:"([^"]+)"|(.*)))?$/) {
+ $block = $1 || $2;
+ $blockname = $3 || $4;
+ }
+ }
+ if ($this->{InterPolateVars}) {
+ # interpolate block(name), add "<" and ">" to the key, because
+ # it is sure that such keys does not exist otherwise.
+ $block = $this->_interpolate($config, "<$block>", $block);
+ if (defined $blockname) {
+ $blockname = $this->_interpolate($config, "<$blockname>", "$blockname");
+ }
+ }
+ if ($this->{LowerCaseNames}) {
+ $block = lc $block; # only for blocks lc(), if configured via new()
+ }
+ $this->{level} += 1;
+ undef @newcontent;
+ next;
+ }
+ elsif (/^<\/(.+?)>$/) { # it is an end block, but we don't have a matching block!
+ croak "Config::General: EndBlock \"<\/$1>\" has no StartBlock statement (level: $this->{level}, chunk $chunk)!\n";
+ }
+ else { # insert key/value pair into actual node
+ if ($this->{LowerCaseNames}) {
+ $option = lc $option;
+ }
+
+ if (exists $config->{$option}) {
+ if ($this->{MergeDuplicateOptions}) {
+ $config->{$option} = $this->_parse_value($config, $option, $value);
+
+ # bugfix rt.cpan.org#33216
+ if ($this->{InterPolateVars}) {
+ # save pair on local stack
+ $config->{__stack}->{$option} = $config->{$option};
+ }
+ }
+ else {
+ if (! $this->{AllowMultiOptions} ) {
+ # no, duplicates not allowed
+ croak "Config::General: Option \"$option\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
+ }
+ else {
+ # yes, duplicates allowed
+ if (ref($config->{$option}) ne 'ARRAY') { # convert scalar to array
+ my $savevalue = $config->{$option};
+ delete $config->{$option};
+ push @{$config->{$option}}, $savevalue;
+ }
+ eval {
+ # check if arrays are supported by the underlying hash
+ my $i = scalar @{$config->{$option}};
+ };
+ if ($EVAL_ERROR) {
+ $config->{$option} = $this->_parse_value($config, $option, $value);
+ }
+ else {
+ # it's already an array, just push
+ push @{$config->{$option}}, $this->_parse_value($config, $option, $value);
+ }
+ }
+ }
+ }
+ else {
+ # standard config option, insert key/value pair into node
+ $config->{$option} = $this->_parse_value($config, $option, $value);
+
+ if ($this->{InterPolateVars}) {
+ # save pair on local stack
+ $config->{__stack}->{$option} = $config->{$option};
+ }
+ }
+ }
+ }
+ elsif (/^<([^\/]+?.*?)>$/) { # found a start block inside a block, don't forget it
+ $block_level++; # $block_level indicates wether we are still inside a node
+ push @newcontent, $_; # push onto new content stack for later recursive call of _parse()
+ }
+ elsif (/^<\/(.+?)>$/) {
+ if ($block_level) { # this endblock is not the one we are searching for, decrement and push
+ $block_level--; # if it is 0, then the endblock was the one we searched for, see below
+ push @newcontent, $_; # push onto new content stack
+ }
+ else { # calling myself recursively, end of $block reached, $block_level is 0
+ if (defined $blockname) {
+ # a named block, make it a hashref inside a hash within the current node
+
+ if (! exists $config->{$block}) {
+ # Make sure that the hash is not created implicitly
+ $config->{$block} = $this->_hashref();
+
+ if ($this->{InterPolateVars}) {
+ # inherit current __stack to new block
+ $config->{$block}->{__stack} = $this->_copy($config->{__stack});
+ }
+ }
+
+ if (ref($config->{$block}) eq '') {
+ croak "Config::General: Block <$block> already exists as scalar entry!\n";
+ }
+ elsif (ref($config->{$block}) eq 'ARRAY') {
+ croak "Config::General: Cannot append named block <$block $blockname> to array of scalars!\n"
+ ."Block <$block> or scalar '$block' occurs more than once.\n"
+ ."Turn on -MergeDuplicateBlocks or make sure <$block> occurs only once in the config.\n";
+ }
+ elsif (exists $config->{$block}->{$blockname}) {
+ # the named block already exists, make it an array
+ if ($this->{MergeDuplicateBlocks}) {
+ # just merge the new block with the same name as an existing one into
+ # this one.
+ $config->{$block}->{$blockname} = $this->_parse($config->{$block}->{$blockname}, \@newcontent);
+ }
+ else {
+ if (! $this->{AllowMultiOptions}) {
+ croak "Config::General: Named block \"<$block $blockname>\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
+ }
+ else { # preserve existing data
+ my $savevalue = $config->{$block}->{$blockname};
+ delete $config->{$block}->{$blockname};
+ my @ar;
+ if (ref $savevalue eq 'ARRAY') {
+ push @ar, @{$savevalue}; # preserve array if any
+ }
+ else {
+ push @ar, $savevalue;
+ }
+ push @ar, $this->_parse( $this->_hashref(), \@newcontent); # append it
+ $config->{$block}->{$blockname} = \@ar;
+ }
+ }
+ }
+ else {
+ # the first occurence of this particular named block
+ my $tmphash = $this->_hashref();
+
+ if ($this->{InterPolateVars}) {
+ # inherit current __stack to new block
+ $tmphash->{__stack} = $this->_copy($config->{__stack});
+ #$tmphash->{__stack} = $config->{$block}->{__stack};
+ }
+
+ $config->{$block}->{$blockname} = $this->_parse($tmphash, \@newcontent);
+ }
+ }
+ else {
+ # standard block
+ if (exists $config->{$block}) {
+ if (ref($config->{$block}) eq '') {
+ croak "Config::General: Cannot create hashref from <$block> because there is\n"
+ ."already a scalar option '$block' with value '$config->{$block}'\n";
+ }
+
+ # the block already exists, make it an array
+ if ($this->{MergeDuplicateBlocks}) {
+ # just merge the new block with the same name as an existing one into
+ # this one.
+ $config->{$block} = $this->_parse($config->{$block}, \@newcontent);
+ }
+ else {
+ if (! $this->{AllowMultiOptions}) {
+ croak "Config::General: Block \"<$block>\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
+ }
+ else {
+ my $savevalue = $config->{$block};
+ delete $config->{$block};
+ my @ar;
+ if (ref $savevalue eq "ARRAY") {
+ push @ar, @{$savevalue};
+ }
+ else {
+ push @ar, $savevalue;
+ }
+
+ # fixes rt#31529
+ my $tmphash = $this->_hashref();
+ if ($this->{InterPolateVars}) {
+ # inherit current __stack to new block
+ $tmphash->{__stack} = $this->_copy($config->{__stack});
+ }
+
+ push @ar, $this->_parse( $tmphash, \@newcontent);
+
+ $config->{$block} = \@ar;
+ }
+ }
+ }
+ else {
+ # the first occurence of this particular block
+ my $tmphash = $this->_hashref();
+
+ if ($this->{InterPolateVars}) {
+ # inherit current __stack to new block
+ $tmphash->{__stack} = $this->_copy($config->{__stack});
+ }
+
+ $config->{$block} = $this->_parse($tmphash, \@newcontent);
+ }
+ }
+ undef $blockname;
+ undef $block;
+ $this->{level} -= 1;
+ next;
+ }
+ }
+ else { # inside $block, just push onto new content stack
+ push @newcontent, $_;
+ }
+ }
+ if ($block) {
+ # $block is still defined, which means, that it had
+ # no matching endblock!
+ croak "Config::General: Block \"<$block>\" has no EndBlock statement (level: $this->{level}, chunk $chunk)!\n";
+ }
+ return $config;
+}
+
+
+sub _copy {
+ #
+ # copy the contents of one hash into another
+ # to circumvent invalid references
+ # fixes rt.cpan.org bug #35122
+ my($this, $source) = @_;
+ my %hash = ();
+ while (my ($key, $value) = each %{$source}) {
+ $hash{$key} = $value;
+ }
+ return \%hash;
+}
+
+
+sub _parse_value {
+ #
+ # parse the value if value parsing is turned on
+ # by either -AutoTrue and/or -FlagBits
+ # otherwise just return the given value unchanged
+ #
+ my($this, $config, $option, $value) =@_;
+
+ # avoid "Use of uninitialized value"
+ if (! defined $value) {
+ $value = undef; # bigfix rt.cpan.org#42721 q();
+ }
+
+ if ($this->{InterPolateVars}) {
+ $value = $this->_interpolate($config, $option, $value);
+ }
+
+ # make true/false values to 1 or 0 (-AutoTrue)
+ if ($this->{AutoTrue}) {
+ if ($value =~ /$this->{AutoTrueFlags}->{true}/io) {
+ $value = 1;
+ }
+ elsif ($value =~ /$this->{AutoTrueFlags}->{false}/io) {
+ $value = 0;
+ }
+ }
+
+ # assign predefined flags or undef for every flag | flag ... (-FlagBits)
+ if ($this->{FlagBits}) {
+ if (exists $this->{FlagBitsFlags}->{$option}) {
+ my %__flags = map { $_ => 1 } split /\s*\|\s*/, $value;
+ foreach my $flag (keys %{$this->{FlagBitsFlags}->{$option}}) {
+ if (exists $__flags{$flag}) {
+ $__flags{$flag} = $this->{FlagBitsFlags}->{$option}->{$flag};
+ }
+ else {
+ $__flags{$flag} = undef;
+ }
+ }
+ $value = \%__flags;
+ }
+ }
+ return $value;
+}
+
+
+
+
+
+
+sub NoMultiOptions {
+ #
+ # turn AllowMultiOptions off, still exists for backward compatibility.
+ # Since we do parsing from within new(), we must
+ # call it again if one turns NoMultiOptions on!
+ #
+ croak q(Config::Genera: lThe NoMultiOptions() method is deprecated. Set 'AllowMultiOptions' to 'no' instead!);
+}
+
+
+sub save {
+ #
+ # this is the old version of save() whose API interface
+ # has been changed. I'm very sorry 'bout this.
+ #
+ # I'll try to figure out, if it has been called correctly
+ # and if yes, feed the call to Save(), otherwise croak.
+ #
+ my($this, $one, @two) = @_;
+
+ if ( (@two && $one) && ( (scalar @two) % 2 == 0) ) {
+ # @two seems to be a hash
+ my %h = @two;
+ $this->save_file($one, \%h);
+ }
+ else {
+ croak q(Config::General: The save() method is deprecated. Use the new save_file() method instead!);
+ }
+ return;
+}
+
+
+sub save_file {
+ #
+ # save the config back to disk
+ #
+ my($this, $file, $config) = @_;
+ my $fh;
+ my $config_string;
+
+ if (!$file) {
+ croak "Config::General: Filename is required!";
+ }
+ else {
+ if ($this->{UTF8}) {
+ $fh = new IO::File;
+ open($fh, ">:utf8", $file)
+ or croak "Config::General: Could not open $file in UTF8 mode!($!)\n";
+ }
+ else {
+ $fh = IO::File->new( "$file", 'w')
+ or croak "Config::General: Could not open $file!($!)\n";
+ }
+ if (!$config) {
+ if (exists $this->{config}) {
+ $config_string = $this->_store(0, $this->{config});
+ }
+ else {
+ croak "Config::General: No config hash supplied which could be saved to disk!\n";
+ }
+ }
+ else {
+ $config_string = $this->_store(0, $config);
+ }
+
+ if ($config_string) {
+ print {$fh} $config_string;
+ }
+ else {
+ # empty config for whatever reason, I don't care
+ print {$fh} q();
+ }
+
+ close $fh;
+ }
+ return;
+}
+
+
+
+sub save_string {
+ #
+ # return the saved config as a string
+ #
+ my($this, $config) = @_;
+
+ if (!$config || ref($config) ne 'HASH') {
+ if (exists $this->{config}) {
+ return $this->_store(0, $this->{config});
+ }
+ else {
+ croak "Config::General: No config hash supplied which could be saved to disk!\n";
+ }
+ }
+ else {
+ return $this->_store(0, $config);
+ }
+ return;
+}
+
+
+
+sub _store {
+ #
+ # internal sub for saving a block
+ #
+ my($this, $level, $config) = @_;
+ local $_;
+ my $indent = q( ) x $level;
+
+ my $config_string = q();
+
+ if($this->{SaveSorted}) {
+ # ahm, well this might look strange because the two loops
+ # are obviously the same, but I don't know how to call
+ # a foreach() with sort and without sort() on the same
+ # line (I think it's impossible)
+ foreach my $entry (sort keys %{$config}) {
+ if (ref($config->{$entry}) eq 'ARRAY') {
+ foreach my $line (sort @{$config->{$entry}}) {
+ if (ref($line) eq 'HASH') {
+ $config_string .= $this->_write_hash($level, $entry, $line);
+ }
+ else {
+ $config_string .= $this->_write_scalar($level, $entry, $line);
+ }
+ }
+ }
+ elsif (ref($config->{$entry}) eq 'HASH') {
+ $config_string .= $this->_write_hash($level, $entry, $config->{$entry});
+ }
+ else {
+ $config_string .= $this->_write_scalar($level, $entry, $config->{$entry});
+ }
+ }
+ }
+ else {
+ foreach my $entry (keys %{$config}) {
+ if (ref($config->{$entry}) eq 'ARRAY') {
+ foreach my $line (@{$config->{$entry}}) {
+ if (ref($line) eq 'HASH') {
+ $config_string .= $this->_write_hash($level, $entry, $line);
+ }
+ else {
+ $config_string .= $this->_write_scalar($level, $entry, $line);
+ }
+ }
+ }
+ elsif (ref($config->{$entry}) eq 'HASH') {
+ $config_string .= $this->_write_hash($level, $entry, $config->{$entry});
+ }
+ else {
+ $config_string .= $this->_write_scalar($level, $entry, $config->{$entry});
+ }
+ }
+ }
+
+ return $config_string;
+}
+
+
+sub _write_scalar {
+ #
+ # internal sub, which writes a scalar
+ # it returns it, in fact
+ #
+ my($this, $level, $entry, $line) = @_;
+
+ my $indent = q( ) x $level;
+
+ my $config_string;
+
+ if ($line =~ /\n/ || $line =~ /\\$/) {
+ # it is a here doc
+ my $delimiter;
+ my $tmplimiter = 'EOF';
+ while (!$delimiter) {
+ # create a unique here-doc identifier
+ if ($line =~ /$tmplimiter/s) {
+ $tmplimiter .= q(%);
+ }
+ else {
+ $delimiter = $tmplimiter;
+ }
+ }
+ my @lines = split /\n/, $line;
+ $config_string .= $indent . $entry . $this->{StoreDelimiter} . "<<$delimiter\n";
+ foreach (@lines) {
+ $config_string .= $indent . $_ . "\n";
+ }
+ $config_string .= $indent . "$delimiter\n";
+ }
+ else {
+ # a simple stupid scalar entry
+ $line =~ s/#/\\#/g;
+ # bugfix rt.cpan.org#42287
+ if ($line =~ /^\s/ or $line =~ /\s$/) {
+ # need to quote it
+ $line = "\"$line\"";
+ }
+ $config_string .= $indent . $entry . $this->{StoreDelimiter} . $line . "\n";
+ }
+
+ return $config_string;
+}
+
+sub _write_hash {
+ #
+ # internal sub, which writes a hash (block)
+ # it returns it, in fact
+ #
+ my($this, $level, $entry, $line) = @_;
+
+ my $indent = q( ) x $level;
+ my $config_string;
+
+ if ($entry =~ /\s/) {
+ # quote the entry if it contains whitespaces
+ $entry = q(") . $entry . q(");
+ }
+
+ $config_string .= $indent . q(<) . $entry . ">\n";
+ $config_string .= $this->_store($level + 1, $line);
+ $config_string .= $indent . q(</) . $entry . ">\n";
+
+ return $config_string
+}
+
+
+sub _hashref {
+ #
+ # return a probably tied new empty hash ref
+ #
+ my($this) = @_;
+ if ($this->{Tie}) {
+ eval {
+ eval qq{require $this->{Tie}};
+ };
+ if ($EVAL_ERROR) {
+ croak q(Config::General: Could not create a tied hash of type: ) . $this->{Tie} . q(: ) . $EVAL_ERROR;
+ }
+ my %hash;
+ tie %hash, $this->{Tie};
+ return \%hash;
+ }
+ else {
+ return {};
+ }
+}
+
+
+
+#
+# Procedural interface
+#
+sub ParseConfig {
+ #
+ # @_ may contain everything which is allowed for new()
+ #
+ return (new Config::General(@_))->getall();
+}
+
+sub SaveConfig {
+ #
+ # 2 parameters are required, filename and hash ref
+ #
+ my ($file, $hash) = @_;
+
+ if (!$file || !$hash) {
+ croak q{Config::General::SaveConfig(): filename and hash argument required.};
+ }
+ else {
+ if (ref($hash) ne 'HASH') {
+ croak q(Config::General::SaveConfig() The second parameter must be a reference to a hash!);
+ }
+ else {
+ (new Config::General(-ConfigHash => $hash))->save_file($file);
+ }
+ }
+ return;
+}
+
+sub SaveConfigString {
+ #
+ # same as SaveConfig, but return the config,
+ # instead of saving it
+ #
+ my ($hash) = @_;
+
+ if (!$hash) {
+ croak q{Config::General::SaveConfigString(): Hash argument required.};
+ }
+ else {
+ if (ref($hash) ne 'HASH') {
+ croak q(Config::General::SaveConfigString() The parameter must be a reference to a hash!);
+ }
+ else {
+ return (new Config::General(-ConfigHash => $hash))->save_string();
+ }
+ }
+ return;
+}
+
+
+
+# keep this one
+1;
+__END__
+
+
+
+
+
+=head1 NAME
+
+Config::General - Generic Config Module
+
+=head1 SYNOPSIS
+
+ #
+ # the OOP way
+ use Config::General;
+ $conf = new Config::General("rcfile");
+ my %config = $conf->getall;
+
+ #
+ # the procedural way
+ use Config::General qw(ParseConfig SaveConfig SaveConfigString);
+ my %config = ParseConfig("rcfile");
+
+=head1 DESCRIPTION
+
+This module opens a config file and parses its contents for you. The B<new> method
+requires one parameter which needs to be a filename. The method B<getall> returns a hash
+which contains all options and its associated values of your config file.
+
+The format of config files supported by B<Config::General> is inspired by the well known Apache config
+format, in fact, this module is 100% compatible to Apache configs, but you can also just use simple
+ name/value pairs in your config files.
+
+In addition to the capabilities of an Apache config file it supports some enhancements such as here-documents,
+C-style comments or multiline options.
+
+
+=head1 SUBROUTINES/METHODS
+
+=over
+
+=item new()
+
+Possible ways to call B<new()>:
+
+ $conf = new Config::General("rcfile");
+
+ $conf = new Config::General(\%somehash);
+
+ $conf = new Config::General( %options ); # see below for description of possible options
+
+
+This method returns a B<Config::General> object (a hash blessed into "Config::General" namespace.
+All further methods must be used from that returned object. see below.
+
+You can use the new style with hash parameters or the old style which is of course
+still supported. Possible parameters to B<new()> are:
+
+* a filename of a configfile, which will be opened and parsed by the parser
+
+or
+
+* a hash reference, which will be used as the config.
+
+An alternative way to call B<new()> is supplying an option- hash with one or more of
+the following keys set:
+
+=over
+
+=item B<-ConfigFile>
+
+A filename or a filehandle, i.e.:
+
+ -ConfigFile => "rcfile" or -ConfigFile => \$FileHandle
+
+
+
+=item B<-ConfigHash>
+
+A hash reference, which will be used as the config, i.e.:
+
+ -ConfigHash => \%somehash
+
+
+
+=item B<-String>
+
+A string which contains a whole config, or an arrayref
+containing the whole config line by line.
+The parser will parse the contents of the string instead
+of a file. i.e:
+
+ -String => $complete_config
+
+it is also possible to feed an array reference to -String:
+
+ -String => \@config_lines
+
+
+
+=item B<-AllowMultiOptions>
+
+If the value is "no", then multiple identical options are disallowed.
+The default is "yes".
+i.e.:
+
+ -AllowMultiOptions => "yes"
+
+see B<IDENTICAL OPTIONS> for details.
+
+=item B<-LowerCaseNames>
+
+If set to a true value, then all options found in the config will be converted
+to lowercase. This allows you to provide case-in-sensitive configs. The
+values of the options will B<not> lowercased.
+
+
+
+=item B<-UseApacheInclude>
+
+If set to a true value, the parser will consider "include ..." as valid include
+statement (just like the well known Apache include statement).
+
+
+
+=item B<-IncludeRelative>
+
+If set to a true value, included files with a relative path (i.e. "cfg/blah.conf")
+will be opened from within the location of the configfile instead from within the
+location of the script($0). This works only if the configfile has a absolute pathname
+(i.e. "/etc/main.conf").
+
+If the variable B<-ConfigPath> has been set and if the file to be included could
+not be found in the location relative to the current config file, the module
+will search within B<-ConfigPath> for the file. See the description of B<-ConfigPath>
+for more details.
+
+
+=item B<-IncludeDirectories>
+
+If set to a true value, you may specify include a directory, in which case all
+files inside the directory will be loaded in ASCII order. Directory includes
+will not recurse into subdirectories. This is comparable to including a
+directory in Apache-style config files.
+
+
+=item B<-IncludeGlob>
+
+If set to a true value, you may specify a glob pattern for an include to
+include all matching files (e.g. <<include conf.d/*.conf>>). Also note that as
+with standard file patterns, * will not match dot-files, so <<include dir/*>>
+is often more desirable than including a directory with B<-IncludeDirectories>.
+
+
+=item B<-IncludeAgain>
+
+If set to a true value, you will be able to include a sub-configfile
+multiple times. With the default, false, you will get a warning about
+duplicate includes and only the first include will succeed.
+
+Reincluding a configfile can be useful if it contains data that you want to
+be present in multiple places in the data tree. See the example under
+L</INCLUDES>.
+
+Note, however, that there is currently no check for include recursion.
+
+
+=item B<-ConfigPath>
+
+As mentioned above, you can use this variable to specify a search path for relative
+config files which have to be included. Config::General will search within this
+path for the file if it cannot find the file at the location relative to the
+current config file.
+
+To provide multiple search paths you can specify an array reference for the
+path. For example:
+
+ @path = qw(/usr/lib/perl /nfs/apps/lib /home/lib);
+ ..
+ -ConfigPath => \@path
+
+
+
+=item B<-MergeDuplicateBlocks>
+
+If set to a true value, then duplicate blocks, that means blocks and named blocks,
+will be merged into a single one (see below for more details on this).
+The default behavior of Config::General is to create an array if some junk in a
+config appears more than once.
+
+
+=item B<-MergeDuplicateOptions>
+
+If set to a true value, then duplicate options will be merged. That means, if the
+same option occurs more than once, the last one will be used in the resulting
+config hash.
+
+Setting this option implies B<-AllowMultiOptions == false> unless you set
+B<-AllowMultiOptions> explicit to 'true'. In this case duplicate blocks are
+allowed and put into an array but duplicate options will be merged.
+
+
+=item B<-AutoLaunder>
+
+If set to a true value, then all values in your config file will be laundered
+to allow them to be used under a -T taint flag. This could be regarded as circumventing
+the purpose of the -T flag, however, if the bad guys can mess with your config file,
+you have problems that -T will not be able to stop. AutoLaunder will only handle
+a config file being read from -ConfigFile.
+
+
+
+=item B<-AutoTrue>
+
+If set to a true value, then options in your config file, whose values are set to
+true or false values, will be normalised to 1 or 0 respectively.
+
+The following values will be considered as B<true>:
+
+ yes, on, 1, true
+
+The following values will be considered as B<false>:
+
+ no, off, 0, false
+
+This effect is case-insensitive, i.e. both "Yes" or "oN" will result in 1.
+
+
+=item B<-FlagBits>
+
+This option takes one required parameter, which must be a hash reference.
+
+The supplied hash reference needs to define variables for which you
+want to preset values. Each variable you have defined in this hash-ref
+and which occurs in your config file, will cause this variable being
+set to the preset values to which the value in the config file refers to.
+
+Multiple flags can be used, separated by the pipe character |.
+
+Well, an example will clarify things:
+
+ my $conf = new Config::General(
+ -ConfigFile => "rcfile",
+ -FlagBits => {
+ Mode => {
+ CLEAR => 1,
+ STRONG => 1,
+ UNSECURE => "32bit" }
+ }
+ );
+
+In this example we are defining a variable named I<"Mode"> which
+may contain one or more of "CLEAR", "STRONG" and "UNSECURE" as value.
+
+The appropriate config entry may look like this:
+
+ # rcfile
+ Mode = CLEAR | UNSECURE
+
+The parser will create a hash which will be the value of the key "Mode". This
+hash will contain B<all> flags which you have pre-defined, but only those
+which were set in the config will contain the pre-defined value, the other
+ones will be undefined.
+
+The resulting config structure would look like this after parsing:
+
+ %config = (
+ Mode => {
+ CLEAR => 1,
+ UNSECURE => "32bit",
+ STRONG => undef,
+ }
+ );
+
+This method allows the user (or, the "maintainer" of the configfile for your
+application) to set multiple pre-defined values for one option.
+
+Please beware, that all occurrences of those variables will be handled this
+way, there is no way to distinguish between variables in different scopes.
+That means, if "Mode" would also occur inside a named block, it would
+also parsed this way.
+
+Values which are not defined in the hash-ref supplied to the parameter B<-FlagBits>
+and used in the corresponding variable in the config will be ignored.
+
+Example:
+
+ # rcfile
+ Mode = BLAH | CLEAR
+
+would result in this hash structure:
+
+ %config = (
+ Mode => {
+ CLEAR => 1,
+ UNSECURE => undef,
+ STRONG => undef,
+ }
+ );
+
+"BLAH" will be ignored silently.
+
+
+=item B<-DefaultConfig>
+
+This can be a hash reference or a simple scalar (string) of a config. This
+causes the module to preset the resulting config hash with the given values,
+which allows you to set default values for particular config options directly.
+
+
+=item B<-Tie>
+
+B<-Tie> takes the name of a Tie class as argument that each new hash should be
+based off of.
+
+This hash will be used as the 'backing hash' instead of a standard Perl hash,
+which allows you to affect the way, variable storing will be done. You could, for
+example supply a tied hash, say Tie::DxHash, which preserves ordering of the
+keys in the config (which a standard Perl hash won't do). Or, you could supply
+a hash tied to a DBM file to save the parsed variables to disk.
+
+There are many more things to do in tie-land, see L<tie> to get some interesting
+ideas.
+
+If you want to use the B<-Tie> feature together with B<-DefaultConfig> make sure
+that the hash supplied to B<-DefaultConfig> must be tied to the same Tie class.
+
+Make sure that the hash which receives the generated hash structure (e.g. which
+you are using in the assignment: %hash = $config->getall()) must be tied to
+the same Tie class.
+
+Example:
+
+ use Config::General qw(ParseConfig);
+ use Tie::IxHash;
+ tie my %hash, "Tie::IxHash";
+ %hash = ParseConfig(
+ -ConfigFile => shift(),
+ -Tie => "Tie::IxHash"
+ );
+
+
+=item B<-InterPolateVars>
+
+If set to a true value, variable interpolation will be done on your config
+input. See L<Config::General::Interpolated> for more information.
+
+=item B<-InterPolateEnv>
+
+If set to a true value, environment variables can be used in
+configs.
+
+This implies B<-InterPolateVars>.
+
+=item B<-ExtendedAccess>
+
+If set to a true value, you can use object oriented (extended) methods to
+access the parsed config. See L<Config::General::Extended> for more informations.
+
+=item B<-StrictObjects>
+
+By default this is turned on, which causes Config::General to croak with an
+error if you try to access a non-existent key using the OOP-way (B<-ExtendedAcess>
+enabled). If you turn B<-StrictObjects> off (by setting to 0 or "no") it will
+just return an empty object/hash/scalar. This is valid for OOP-access 8via AUTOLOAD
+and for the methods obj(), hash() and value().
+
+
+=item B<-StrictVars>
+
+By default this is turned on, which causes Config::General to croak with an
+error if an undefined variable with B<InterPolateVars> turned on occurs
+in a config. Set to I<false> (i.e. 0) to avoid such error messages.
+
+=item B<-SplitPolicy>
+
+You can influence the way how Config::General decides which part of a line
+in a config file is the key and which one is the value. By default it tries
+its best to guess. That means you can mix equalsign assignments and whitespace
+assignments.
+
+However, somtime you may wish to make it more strictly for some reason. In
+this case you can set B<-SplitPolicy>. The possible values are: 'guess' which
+is the default, 'whitespace' which causes the module to split by whitespace,
+'equalsign' which causes it to split strictly by equal sign, or 'custom'. In the
+latter case you must also set B<-SplitDelimiter> to some regular expression
+of your choice. For example:
+
+ -SplitDelimiter => '\s*:\s*'
+
+will cause the module to split by colon while whitespace which surrounds
+the delimiter will be removed.
+
+Please note that the delimiter used when saving a config (save_file() or save_string())
+will be chosen according to the current B<-SplitPolicy>. If -SplitPolicy is
+set to 'guess' or 'whitespace', 3 spaces will be used to delimit saved
+options. If 'custom' is set, then you need to set B<-StoreDelimiter>.
+
+=item B<-SplitDelimiter>
+
+Set this to any arbitrary regular expression which will be used for option/value
+splitting. B<-SplitPolicy> must be set to 'custom' to make this work.
+
+=item B<-StoreDelimiter>
+
+You can use this parameter to specify a custom delimiter to use when saving
+configs to a file or string. You only need to set it if you want to store
+the config back to disk and if you have B<-SplitPolicy> set to 'custom'.
+
+Be very careful with this parameter.
+
+
+=item B<-CComments>
+
+Config::General is able to notice c-style comments (see section COMMENTS).
+But for some reason you might no need this. In this case you can turn
+this feature off by setting B<-CComments> to a false value('no', 0, 'off').
+
+By default B<-CComments> is turned on.
+
+
+=item B<-BackslashEscape>
+
+If you turn on this parameter, a backslash can be used to escape any special
+character within configurations.
+
+By default it is turned off.
+
+Be careful with this option, as it removes all backslashes after parsing.
+
+B<This option might be removed in future versions>.
+
+=item B<-SlashIsDirectory>
+
+If you turn on this parameter, a single slash as the last character
+of a named block will be considered as a directory name.
+
+By default this flag is turned off, which makes the module somewhat
+incompatible to Apache configs, since such a setup will be normally
+considered as an explicit empty block, just as XML defines it.
+
+For example, if you have the following config:
+
+ <Directory />
+ Index index.awk
+ </Directory>
+
+you will get such an error message from the parser:
+
+ EndBlock "</Directory>" has no StartBlock statement (level: 1, chunk 10)!
+
+This is caused by the fact that the config chunk below will be
+internally converted to:
+
+ <Directory><Directory />
+ Index index.awk
+ </Directory>
+
+Now there is one '</Directory>' too much. The proper solution is
+to use quotation to circumvent this error:
+
+ <Directory "/">
+ Index index.awk
+ </Directory>
+
+However, a raw apache config comes without such quotes. In this
+case you may consider to turn on B<-SlashIsDirectory>.
+
+Please note that this is a new option (incorporated in version 2.30),
+it may lead to various unexpected side effects or other failures.
+You've been warned.
+
+=item B<-ApacheCompatible>
+
+Over the past years a lot of options has been incorporated
+into Config::General to be able to parse real Apache configs.
+
+The new B<-ApacheCompatible> option now makes it possible to
+tweak all options in a way that Apache configs can be parsed.
+
+This is called "apache compatibility mode" - if you will ever
+have problems with parsing Apache configs without this option
+being set, you'll get no help by me. Thanks :)
+
+The following options will be set:
+
+ UseApacheInclude = 1
+ IncludeRelative = 1
+ IncludeDirectories = 1
+ IncludeGlob = 1
+ SlashIsDirectory = 1
+ SplitPolicy = 'equalsign'
+ CComments = 0
+ BackslashEscape = 1
+
+Take a look into the particular documentation sections what
+those options are doing.
+
+Beside setting some options it also turns off support for
+explicit empty blocks.
+
+=item B<-UTF8>
+
+If turned on, all files will be opened in utf8 mode. This may
+not work properly with older versions of Perl.
+
+=item B<-SaveSorted>
+
+If you want to save configs in a sorted manner, turn this
+parameter on. It is not enabled by default.
+
+=back
+
+
+
+
+=item getall()
+
+Returns a hash structure which represents the whole config.
+
+=item files()
+
+Returns a list of all files read in.
+
+=item save_file()
+
+Writes the config hash back to the hard disk. This method takes one or two
+parameters. The first parameter must be the filename where the config
+should be written to. The second parameter is optional, it must be a
+reference to a hash structure, if you set it. If you do not supply this second parameter
+then the internal config hash, which has already been parsed, will be
+used.
+
+Please note that any occurence of comments will be ignored by getall()
+and thus be lost after you call this method.
+
+You need also to know that named blocks will be converted to nested blocks
+(which is the same from the perl point of view). An example:
+
+ <user hans>
+ id 13
+ </user>
+
+will become the following after saving:
+
+ <user>
+ <hans>
+ id 13
+ </hans>
+ </user>
+
+Example:
+
+ $conf_obj->save_file("newrcfile", \%config);
+
+or, if the config has already been parsed, or if it didn't change:
+
+ $conf_obj->save_file("newrcfile");
+
+
+=item save_string()
+
+This method is equivalent to the previous save_file(), but it does not
+store the generated config to a file. Instead it returns it as a string,
+which you can save yourself afterwards.
+
+It takes one optional parameter, which must be a reference to a hash structure.
+If you omit this parameter, the internal config hash, which has already been parsed,
+will be used.
+
+Example:
+
+ my $content = $conf_obj->save_string(\%config);
+
+or:
+
+ my $content = $conf_obj->save_string();
+
+
+=back
+
+
+=head1 CONFIG FILE FORMAT
+
+Lines beginning with B<#> and empty lines will be ignored. (see section COMMENTS!)
+Spaces at the beginning and the end of a line will also be ignored as well as tabulators.
+If you need spaces at the end or the beginning of a value you can surround it with
+double quotes.
+An option line starts with its name followed by a value. An equal sign is optional.
+Some possible examples:
+
+ user max
+ user = max
+ user max
+
+If there are more than one statements with the same name, it will create an array
+instead of a scalar. See the example below.
+
+The method B<getall> returns a hash of all values.
+
+
+=head1 BLOCKS
+
+You can define a B<block> of options. A B<block> looks much like a block
+in the wellknown Apache config format. It starts with E<lt>B<blockname>E<gt> and ends
+with E<lt>/B<blockname>E<gt>. An example:
+
+ <database>
+ host = muli
+ user = moare
+ dbname = modb
+ dbpass = D4r_9Iu
+ </database>
+
+Blocks can also be nested. Here is a more complicated example:
+
+ user = hans
+ server = mc200
+ db = maxis
+ passwd = D3rf$
+ <jonas>
+ user = tom
+ db = unknown
+ host = mila
+ <tablestructure>
+ index int(100000)
+ name char(100)
+ prename char(100)
+ city char(100)
+ status int(10)
+ allowed moses
+ allowed ingram
+ allowed joice
+ </tablestructure>
+ </jonas>
+
+The hash which the method B<getall> returns look like that:
+
+ print Data::Dumper(\%hash);
+ $VAR1 = {
+ 'passwd' => 'D3rf$',
+ 'jonas' => {
+ 'tablestructure' => {
+ 'prename' => 'char(100)',
+ 'index' => 'int(100000)',
+ 'city' => 'char(100)',
+ 'name' => 'char(100)',
+ 'status' => 'int(10)',
+ 'allowed' => [
+ 'moses',
+ 'ingram',
+ 'joice',
+ ]
+ },
+ 'host' => 'mila',
+ 'db' => 'unknown',
+ 'user' => 'tom'
+ },
+ 'db' => 'maxis',
+ 'server' => 'mc200',
+ 'user' => 'hans'
+ };
+
+If you have turned on B<-LowerCaseNames> (see new()) then blocks as in the
+following example:
+
+ <Dir>
+ <AttriBUTES>
+ Owner root
+ </attributes>
+ </dir>
+
+would produce the following hash structure:
+
+ $VAR1 = {
+ 'dir' => {
+ 'attributes' => {
+ 'owner => "root",
+ }
+ }
+ };
+
+As you can see, the keys inside the config hash are normalized.
+
+Please note, that the above config block would result in a
+valid hash structure, even if B<-LowerCaseNames> is not set!
+This is because I<Config::General> does not
+use the block names to check if a block ends, instead it uses an internal
+state counter, which indicates a block end.
+
+If the module cannot find an end-block statement, then this block will be ignored.
+
+
+
+=head1 NAMED BLOCKS
+
+If you need multiple blocks of the same name, then you have to name every block.
+This works much like Apache config. If the module finds a named block, it will
+create a hashref with the left part of the named block as the key containing
+one or more hashrefs with the right part of the block as key containing everything
+inside the block(which may again be nested!). As examples says more than words:
+
+ # given the following sample
+ <Directory /usr/frisco>
+ Limit Deny
+ Options ExecCgi Index
+ </Directory>
+ <Directory /usr/frik>
+ Limit DenyAll
+ Options None
+ </Directory>
+
+ # you will get:
+ $VAR1 = {
+ 'Directory' => {
+ '/usr/frik' => {
+ 'Options' => 'None',
+ 'Limit' => 'DenyAll'
+ },
+ '/usr/frisco' => {
+ 'Options' => 'ExecCgi Index',
+ 'Limit' => 'Deny'
+ }
+ }
+ };
+
+You cannot have more than one named block with the same name because it will
+be stored in a hashref and therefore be overwritten if a block occurs once more.
+
+
+=head1 WHITESPACE IN BLOCKS
+
+The normal behavior of Config::General is to look for whitespace in
+block names to decide if it's a named block or just a simple block.
+
+Sometimes you may need blocknames which have whitespace in their names.
+
+With named blocks this is no problem, as the module only looks for the
+first whitespace:
+
+ <person hugo gera>
+ </person>
+
+would be parsed to:
+
+ $VAR1 = {
+ 'person' => {
+ 'hugo gera' => {
+ },
+ }
+ };
+
+The problem occurs, if you want to have a simple block containing whitespace:
+
+ <hugo gera>
+ </hugo gera>
+
+This would be parsed as a named block, which is not what you wanted. In this
+very case you may use quotation marks to indicate that it is not a named block:
+
+ <"hugo gera">
+ </"hugo gera">
+
+The save() method of the module inserts automatically quotation marks in such
+cases.
+
+
+=head1 EXPLICIT EMPTY BLOCKS
+
+Beside the notation of blocks mentioned above it is possible to use
+explicit empty blocks.
+
+Normally you would write this in your config to define an empty
+block:
+
+ <driver Apache>
+ </driver>
+
+To save writing you can also write:
+
+ <driver Apache/>
+
+which is the very same as above. This works for normal blocks and
+for named blocks.
+
+
+
+=head1 IDENTICAL OPTIONS
+
+You may have more than one line of the same option with different values.
+
+Example:
+ log log1
+ log log2
+ log log2
+
+You will get a scalar if the option occurred only once or an array if it occurred
+more than once. If you expect multiple identical options, then you may need to
+check if an option occurred more than once:
+
+ $allowed = $hash{jonas}->{tablestructure}->{allowed};
+ if(ref($allowed) eq "ARRAY") {
+ @ALLOWED = @{$allowed};
+ else {
+ @ALLOWED = ($allowed);
+ }
+
+The same applies to blocks and named blocks too (they are described in more detail
+below). For example, if you have the following config:
+
+ <dir blah>
+ user max
+ </dir>
+ <dir blah>
+ user hannes
+ </dir>
+
+then you would end up with a data structure like this:
+
+ $VAR1 = {
+ 'dir' => {
+ 'blah' => [
+ {
+ 'user' => 'max'
+ },
+ {
+ 'user' => 'hannes'
+ }
+ ]
+ }
+ };
+
+As you can see, the two identical blocks are stored in a hash which contains
+an array(-reference) of hashes.
+
+Under some rare conditions you might not want this behavior with blocks (and
+named blocks too). If you want to get one single hash with the contents of
+both identical blocks, then you need to turn the B<new()> parameter B<-MergeDuplicateBlocks>
+on (see above). The parsed structure of the example above would then look like
+this:
+
+ $VAR1 = {
+ 'dir' => {
+ 'blah' => {
+ 'user' => [
+ 'max',
+ 'hannes'
+ ]
+ }
+ }
+ };
+
+As you can see, there is only one hash "dir->{blah}" containing multiple
+"user" entries. As you can also see, turning on B<-MergeDuplicateBlocks>
+does not affect scalar options (i.e. "option = value"). In fact you can
+tune merging of duplicate blocks and options independent from each other.
+
+If you don't want to allow more than one identical options, you may turn it off
+by setting the flag I<AllowMultiOptions> in the B<new()> method to "no".
+If turned off, Config::General will complain about multiple occurring options
+with identical names!
+
+
+
+=head1 LONG LINES
+
+If you have a config value, which is too long and would take more than one line,
+you can break it into multiple lines by using the backslash character at the end
+of the line. The Config::General module will concatenate those lines to one single-value.
+
+Example:
+
+command = cat /var/log/secure/tripwire | \
+ mail C<-s> "report from tripwire" \
+ honey@myotherhost.nl
+
+command will become:
+ "cat /var/log/secure/tripwire | mail C<-s> 'report from twire' honey@myotherhost.nl"
+
+
+=head1 HERE DOCUMENTS
+
+You can also define a config value as a so called "here-document". You must tell
+the module an identifier which idicates the end of a here document. An
+identifier must follow a "<<".
+
+Example:
+
+ message <<EOF
+ we want to
+ remove the
+ homedir of
+ root.
+ EOF
+
+Everything between the two "EOF" strings will be in the option I<message>.
+
+There is a special feature which allows you to use indentation with here documents.
+You can have any amount of whitespace or tabulators in front of the end
+identifier. If the module finds spaces or tabs then it will remove exactly those
+amount of spaces from every line inside the here-document.
+
+Example:
+
+ message <<EOF
+ we want to
+ remove the
+ homedir of
+ root.
+ EOF
+
+After parsing, message will become:
+
+ we want to
+ remove the
+ homedir of
+ root.
+
+because there were the string " " in front of EOF, which were cut from every
+line inside the here-document.
+
+
+
+=head1 INCLUDES
+
+You can include an external file at any posision in your config file using the following statement
+in your config file:
+
+ <<include externalconfig.rc>>
+
+If you turned on B<-UseApacheInclude> (see B<new()>), then you can also use the following
+statement to include an external file:
+
+ include externalconfig.rc
+
+This file will be inserted at the position where it was found as if the contents of this file
+were directly at this position.
+
+You can also recursively include files, so an included file may include another one and so on.
+Beware that you do not recursively load the same file, you will end with an error message like
+"too many open files in system!".
+
+By default included files with a relative pathname will be opened from within the current
+working directory. Under some circumstances it maybe possible to
+open included files from the directory, where the configfile resides. You need to turn on
+the option B<-IncludeRelative> (see B<new()>) if you want that. An example:
+
+ my $conf = Config::General(
+ -ConfigFile => "/etc/crypt.d/server.cfg"
+ -IncludeRelative => 1
+ );
+
+ /etc/crypt.d/server.cfg:
+ <<include acl.cfg>>
+
+In this example Config::General will try to include I<acl.cfg> from I</etc/crypt.d>:
+
+ /etc/crypt.d/acl.cfg
+
+The default behavior (if B<-IncludeRelative> is B<not> set!) will be to open just I<acl.cfg>,
+wherever it is, i.e. if you did a chdir("/usr/local/etc"), then Config::General will include:
+
+ /usr/local/etc/acl.cfg
+
+Include statements can be case insensitive (added in version 1.25).
+
+Include statements will be ignored within C-Comments and here-documents.
+
+By default, a config file will only be included the first time it is
+referenced. If you wish to include a file in multiple places, set
+B</-IncludeAgain> to true. But be warned: this may lead to infinite loops,
+so make sure, you're not including the same file from within itself!
+
+Example:
+
+ # main.cfg
+ <object billy>
+ class=Some::Class
+ <printers>
+ include printers.cfg
+ </printers>
+ # ...
+ </object>
+ <object bob>
+ class=Another::Class
+ <printers>
+ include printers.cfg
+ </printers>
+ # ...
+ </object>
+
+Now C<printers.cfg> will be include in both the C<billy> and C<bob> objects.
+
+You will have to be careful to not recursively include a file. Behaviour
+in this case is undefined.
+
+
+
+=head1 COMMENTS
+
+A comment starts with the number sign B<#>, there can be any number of spaces and/or
+tab stops in front of the #.
+
+A comment can also occur after a config statement. Example:
+
+ username = max # this is the comment
+
+If you want to comment out a large block you can use C-style comments. A B</*> signals
+the begin of a comment block and the B<*/> signals the end of the comment block.
+Example:
+
+ user = max # valid option
+ db = tothemax
+ /*
+ user = andors
+ db = toand
+ */
+
+In this example the second options of user and db will be ignored. Please beware of the fact,
+if the Module finds a B</*> string which is the start of a comment block, but no matching
+end block, it will ignore the whole rest of the config file!
+
+B<NOTE:> If you require the B<#> character (number sign) to remain in the option value, then
+you can use a backslash in front of it, to escape it. Example:
+
+ bgcolor = \#ffffcc
+
+In this example the value of $config{bgcolor} will be "#ffffcc", Config::General will not treat
+the number sign as the begin of a comment because of the leading backslash.
+
+Inside here-documents escaping of number signs is NOT required!
+
+
+=head1 OBJECT ORIENTED INTERFACE
+
+There is a way to access a parsed config the OO-way.
+Use the module B<Config::General::Extended>, which is
+supplied with the Config::General distribution.
+
+=head1 VARIABLE INTERPOLATION
+
+You can use variables inside your config files if you like. To do
+that you have to use the module B<Config::General::Interpolated>,
+which is supplied with the Config::General distribution.
+
+
+=head1 EXPORTED FUNCTIONS
+
+Config::General exports some functions too, which makes it somewhat
+easier to use it, if you like this.
+
+How to import the functions:
+
+ use Config::General qw(ParseConfig SaveConfig SaveConfigString);
+
+=over
+
+=item B<ParseConfig()>
+
+This function takes exactly all those parameters, which are
+allowed to the B<new()> method of the standard interface.
+
+Example:
+
+ use Config::General qw(ParseConfig);
+ my %config = ParseConfig(-ConfigFile => "rcfile", -AutoTrue => 1);
+
+
+=item B<SaveConfig()>
+
+This function requires two arguments, a filename and a reference
+to a hash structure.
+
+Example:
+
+ use Config::General qw(SaveConfig);
+ ..
+ SaveConfig("rcfile", \%some_hash);
+
+
+=item B<SaveConfigString()>
+
+This function requires a reference to a config hash as parameter.
+It generates a configuration based on this hash as the object-interface
+method B<save_string()> does.
+
+Example:
+
+ use Config::General qw(ParseConfig SaveConfigString);
+ my %config = ParseConfig(-ConfigFile => "rcfile");
+ .. # change %config something
+ my $content = SaveConfigString(\%config);
+
+
+=back
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+No environment variables will be used.
+
+=head1 SEE ALSO
+
+I recommend you to read the following documents, which are supplied with Perl:
+
+ perlreftut Perl references short introduction
+ perlref Perl references, the rest of the story
+ perldsc Perl data structures intro
+ perllol Perl data structures: arrays of arrays
+
+ Config::General::Extended Object oriented interface to parsed configs
+ Config::General::Interpolated Allows to use variables inside config files
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright (c) 2000-2009 Thomas Linden
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 BUGS AND LIMITATIONS
+
+See rt.cpan.org for current bugs, if any.
+
+=head1 INCOMPATIBILITIES
+
+None known.
+
+=head1 DIAGNOSTICS
+
+To debug Config::General use the Perl debugger, see L<perldebug>.
+
+=head1 DEPENDENCIES
+
+Config::General depends on the modules L<FileHandle>,
+L<File::Spec::Functions>, L<File::Glob>, which all are
+shipped with Perl.
+
+=head1 AUTHOR
+
+Thomas Linden <tlinden |AT| cpan.org>
+
+=head1 VERSION
+
+2.44
+
+=cut
+