forked from openkylin/libconfig-general-perl
Import Upstream version 2.65
This commit is contained in:
commit
babc3c2725
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,663 @@
|
||||||
|
#
|
||||||
|
# Config::General::Extended - special Class based on Config::General
|
||||||
|
#
|
||||||
|
# Copyright (c) 2000-2022 Thomas Linden <tlinden |AT| cpan.org>.
|
||||||
|
# All Rights Reserved. Std. disclaimer applies.
|
||||||
|
# Licensed under the Artistic License 2.0.
|
||||||
|
#
|
||||||
|
|
||||||
|
# namespace
|
||||||
|
package Config::General::Extended;
|
||||||
|
|
||||||
|
# yes we need the hash support of new() in 1.18 or higher!
|
||||||
|
use Config::General 1.18;
|
||||||
|
|
||||||
|
use FileHandle;
|
||||||
|
use Carp;
|
||||||
|
use Exporter ();
|
||||||
|
use vars qw(@ISA @EXPORT);
|
||||||
|
|
||||||
|
# inherit new() and so on from Config::General
|
||||||
|
@ISA = qw(Config::General Exporter);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
|
||||||
|
$Config::General::Extended::VERSION = "2.07";
|
||||||
|
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
croak "Deprecated method Config::General::Extended::new() called.\n"
|
||||||
|
."Use Config::General::new() instead and set the -ExtendedAccess flag.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub getbypath {
|
||||||
|
my ($this, $path) = @_;
|
||||||
|
my $xconfig = $this->{config};
|
||||||
|
$path =~ s#^/##;
|
||||||
|
$path =~ s#/$##;
|
||||||
|
my @pathlist = split /\//, $path;
|
||||||
|
my $index;
|
||||||
|
foreach my $element (@pathlist) {
|
||||||
|
if($element =~ /^([^\[]*)\[(\d+)\]$/) {
|
||||||
|
$element = $1;
|
||||||
|
$index = $2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$index = undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ref($xconfig) eq "ARRAY") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
elsif (! exists $xconfig->{$element}) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ref($xconfig->{$element}) eq "ARRAY") {
|
||||||
|
if(! defined($index) ) {
|
||||||
|
#croak "$element is an array but you didn't specify an index to access it!\n";
|
||||||
|
$xconfig = $xconfig->{$element};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(exists $xconfig->{$element}->[$index]) {
|
||||||
|
$xconfig = $xconfig->{$element}->[$index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
croak "$element doesn't have an element with index $index!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$xconfig = $xconfig->{$element};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xconfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub obj {
|
||||||
|
#
|
||||||
|
# returns a config object from a given key
|
||||||
|
# or from the current config hash if the $key does not exist
|
||||||
|
# or an empty object if the content of $key is empty.
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
|
||||||
|
# just create the empty object, just in case
|
||||||
|
my $empty = $this->SUPER::new( -ExtendedAccess => 1, -ConfigHash => {}, %{$this->{Params}} );
|
||||||
|
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
if (!$this->{config}->{$key}) {
|
||||||
|
# be cool, create an empty object!
|
||||||
|
return $empty
|
||||||
|
}
|
||||||
|
elsif (ref($this->{config}->{$key}) eq "ARRAY") {
|
||||||
|
my @objlist;
|
||||||
|
foreach my $element (@{$this->{config}->{$key}}) {
|
||||||
|
if (ref($element) eq "HASH") {
|
||||||
|
push @objlist,
|
||||||
|
$this->SUPER::new( -ExtendedAccess => 1,
|
||||||
|
-ConfigHash => $element,
|
||||||
|
%{$this->{Params}} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "element in list \"$key\" does not point to a hash reference!\n";
|
||||||
|
}
|
||||||
|
# else: skip this element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return \@objlist;
|
||||||
|
}
|
||||||
|
elsif (ref($this->{config}->{$key}) eq "HASH") {
|
||||||
|
return $this->SUPER::new( -ExtendedAccess => 1,
|
||||||
|
-ConfigHash => $this->{config}->{$key}, %{$this->{Params}} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# nothing supported
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "key \"$key\" does not point to a hash reference!\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# be cool, create an empty object!
|
||||||
|
return $empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# even return an empty object if $key does not exist
|
||||||
|
return $empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub value {
|
||||||
|
#
|
||||||
|
# returns a value of the config hash from a given key
|
||||||
|
# this can be a hashref or a scalar
|
||||||
|
#
|
||||||
|
my($this, $key, $value) = @_;
|
||||||
|
if (defined $value) {
|
||||||
|
$this->{config}->{$key} = $value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
return $this->{config}->{$key};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "Key \"$key\" does not exist within current object\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub hash {
|
||||||
|
#
|
||||||
|
# returns a value of the config hash from a given key
|
||||||
|
# as hash
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
return %{$this->{config}->{$key}};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "Key \"$key\" does not exist within current object\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub array {
|
||||||
|
#
|
||||||
|
# returns a value of the config hash from a given key
|
||||||
|
# as array
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
return @{$this->{config}->{$key}};
|
||||||
|
}
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "Key \"$key\" does not exist within current object\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub is_hash {
|
||||||
|
#
|
||||||
|
# return true if the given key contains a hashref
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
if (ref($this->{config}->{$key}) eq "HASH") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub is_array {
|
||||||
|
#
|
||||||
|
# return true if the given key contains an arrayref
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
if (ref($this->{config}->{$key}) eq "ARRAY") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub is_scalar {
|
||||||
|
#
|
||||||
|
# returns true if the given key contains a scalar(or number)
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key} && !ref($this->{config}->{$key})) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub exists {
|
||||||
|
#
|
||||||
|
# returns true if the key exists
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub keys {
|
||||||
|
#
|
||||||
|
# returns all keys under in the hash of the specified key, if
|
||||||
|
# it contains keys (so it must be a hash!)
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (!$key) {
|
||||||
|
if (ref($this->{config}) eq "HASH") {
|
||||||
|
return map { $_ } keys %{$this->{config}};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif (exists $this->{config}->{$key} && ref($this->{config}->{$key}) eq "HASH") {
|
||||||
|
return map { $_ } keys %{$this->{config}->{$key}};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub delete {
|
||||||
|
#
|
||||||
|
# delete the given key from the config, if any
|
||||||
|
# and return what is deleted (just as 'delete $hash{key}' does)
|
||||||
|
#
|
||||||
|
my($this, $key) = @_;
|
||||||
|
if (exists $this->{config}->{$key}) {
|
||||||
|
return delete $this->{config}->{$key};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub configfile {
|
||||||
|
#
|
||||||
|
# sets or returns the config filename
|
||||||
|
#
|
||||||
|
my($this,$file) = @_;
|
||||||
|
if ($file) {
|
||||||
|
$this->{configfile} = $file;
|
||||||
|
}
|
||||||
|
return $this->{configfile};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub find {
|
||||||
|
my $this = shift;
|
||||||
|
my $key = shift;
|
||||||
|
return undef unless $this->exists($key);
|
||||||
|
if (@_) {
|
||||||
|
return $this->obj($key)->find(@_);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $this->obj($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub AUTOLOAD {
|
||||||
|
#
|
||||||
|
# returns the representing value, if it is a scalar.
|
||||||
|
#
|
||||||
|
my($this, $value) = @_;
|
||||||
|
my $key = $Config::General::Extended::AUTOLOAD; # get to know how we were called
|
||||||
|
$key =~ s/.*:://; # remove package name!
|
||||||
|
|
||||||
|
if (defined $value) {
|
||||||
|
# just set $key to $value!
|
||||||
|
$this->{config}->{$key} = $value;
|
||||||
|
}
|
||||||
|
elsif (exists $this->{config}->{$key}) {
|
||||||
|
if ($this->is_hash($key)) {
|
||||||
|
croak "Key \"$key\" points to a hash and cannot be automatically accessed\n";
|
||||||
|
}
|
||||||
|
elsif ($this->is_array($key)) {
|
||||||
|
croak "Key \"$key\" points to an array and cannot be automatically accessed\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $this->{config}->{$key};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($this->{StrictObjects}) {
|
||||||
|
croak "Key \"$key\" does not exist within current object\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# be cool
|
||||||
|
return undef; # bugfix rt.cpan.org#42331
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub DESTROY {
|
||||||
|
my $this = shift;
|
||||||
|
$this = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
# keep this one
|
||||||
|
1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Config::General::Extended - Extended access to Config files
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use Config::General;
|
||||||
|
|
||||||
|
$conf = Config::General->new(
|
||||||
|
-ConfigFile => 'configfile',
|
||||||
|
-ExtendedAccess => 1
|
||||||
|
);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This is an internal module which makes it possible to use object
|
||||||
|
oriented methods to access parts of your config file.
|
||||||
|
|
||||||
|
Normally you don't call it directly.
|
||||||
|
|
||||||
|
=head1 METHODS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item configfile('filename')
|
||||||
|
|
||||||
|
Set the filename to be used by B<save> to "filename". It returns the current
|
||||||
|
configured filename if called without arguments.
|
||||||
|
|
||||||
|
|
||||||
|
=item obj('key')
|
||||||
|
|
||||||
|
Returns a new object (of Config::General::Extended Class) from the given key.
|
||||||
|
Short example:
|
||||||
|
Assume you have the following config:
|
||||||
|
|
||||||
|
<individual>
|
||||||
|
<martin>
|
||||||
|
age 23
|
||||||
|
</martin>
|
||||||
|
<joseph>
|
||||||
|
age 56
|
||||||
|
</joseph>
|
||||||
|
</individual>
|
||||||
|
<other>
|
||||||
|
blah blubber
|
||||||
|
blah gobble
|
||||||
|
leer
|
||||||
|
</other>
|
||||||
|
|
||||||
|
and already read it in using B<Config::General::Extended::new()>, then you can get a
|
||||||
|
new object from the "individual" block this way:
|
||||||
|
|
||||||
|
$individual = $conf->obj("individual");
|
||||||
|
|
||||||
|
Now if you call B<getall> on I<$individual> (just for reference) you would get:
|
||||||
|
|
||||||
|
$VAR1 = (
|
||||||
|
martin => { age => 13 }
|
||||||
|
);
|
||||||
|
|
||||||
|
Or, here is another use:
|
||||||
|
|
||||||
|
my $individual = $conf->obj("individual");
|
||||||
|
foreach my $person ($conf->keys("individual")) {
|
||||||
|
$man = $individual->obj($person);
|
||||||
|
print "$person is " . $man->value("age") . " years old\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
See the discussion on B<hash()> and B<value()> below.
|
||||||
|
|
||||||
|
If the key from which you want to create a new object is empty, an empty
|
||||||
|
object will be returned. If you run the following on the above config:
|
||||||
|
|
||||||
|
$obj = $conf->obj("other")->obj("leer");
|
||||||
|
|
||||||
|
Then $obj will be empty, just like if you have had run this:
|
||||||
|
|
||||||
|
$obj = Config::General::Extended->new( () );
|
||||||
|
|
||||||
|
Read operations on this empty object will return nothing or even fail.
|
||||||
|
But you can use an empty object for I<creating> a new config using write
|
||||||
|
operations, i.e.:
|
||||||
|
|
||||||
|
$obj->someoption("value");
|
||||||
|
|
||||||
|
See the discussion on B<AUTOLOAD METHODS> below.
|
||||||
|
|
||||||
|
If the key points to a list of hashes, a list of objects will be
|
||||||
|
returned. Given the following example config:
|
||||||
|
|
||||||
|
<option>
|
||||||
|
name = max
|
||||||
|
</option>
|
||||||
|
<option>
|
||||||
|
name = bea
|
||||||
|
</option>
|
||||||
|
|
||||||
|
you could write code like this to access the list the OOP way:
|
||||||
|
|
||||||
|
my $objlist = $conf->obj("option");
|
||||||
|
foreach my $option (@{$objlist}) {
|
||||||
|
print $option->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Please note that the list will be returned as a reference to an array.
|
||||||
|
|
||||||
|
Empty elements or non-hash elements of the list, if any, will be skipped.
|
||||||
|
|
||||||
|
=item hash('key')
|
||||||
|
|
||||||
|
This method returns a hash(if it B<is> one!) from the config which is referenced by
|
||||||
|
"key". Given the sample config above you would get:
|
||||||
|
|
||||||
|
my %sub_hash = $conf->hash("individual");
|
||||||
|
print Dumper(\%sub_hash);
|
||||||
|
$VAR1 = {
|
||||||
|
martin => { age => 13 }
|
||||||
|
};
|
||||||
|
|
||||||
|
=item array('key')
|
||||||
|
|
||||||
|
This the equivalent of B<hash()> mentioned above, except that it returns an array.
|
||||||
|
Again, we use the sample config mentioned above:
|
||||||
|
|
||||||
|
$other = $conf->obj("other");
|
||||||
|
my @blahs = $other->array("blah");
|
||||||
|
print Dumper(\@blahs);
|
||||||
|
$VAR1 = [ "blubber", "gobble" ];
|
||||||
|
|
||||||
|
|
||||||
|
=item value('key')
|
||||||
|
|
||||||
|
This method returns the scalar value of a given key. Given the following sample
|
||||||
|
config:
|
||||||
|
|
||||||
|
name = arthur
|
||||||
|
age = 23
|
||||||
|
|
||||||
|
you could do something like that:
|
||||||
|
|
||||||
|
print $conf->value("name") . " is " . $conf->value("age") . " years old\n";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You can use this method also to set the value of "key" to something if you give over
|
||||||
|
a hash reference, array reference or a scalar in addition to the key. An example:
|
||||||
|
|
||||||
|
$conf->value("key", \%somehash);
|
||||||
|
# or
|
||||||
|
$conf->value("key", \@somearray);
|
||||||
|
# or
|
||||||
|
$conf->value("key", $somescalar);
|
||||||
|
|
||||||
|
Please note, that this method does not complain about existing values within "key"!
|
||||||
|
|
||||||
|
=item is_hash('key') is_array('key') is_scalar('key')
|
||||||
|
|
||||||
|
As seen above, you can access parts of your current config using hash, array or scalar
|
||||||
|
methods. But you are right if you guess, that this might become problematic, if
|
||||||
|
for example you call B<hash()> on a key which is in real not a hash but a scalar. Under
|
||||||
|
normal circumstances perl would refuse this and die.
|
||||||
|
|
||||||
|
To avoid such behavior you can use one of the methods is_hash() is_array() is_scalar() to
|
||||||
|
check if the value of "key" is really what you expect it to be.
|
||||||
|
|
||||||
|
An example(based on the config example from above):
|
||||||
|
|
||||||
|
if($conf->is_hash("individual") {
|
||||||
|
$individual = $conf->obj("individual");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
die "You need to configure a "individual" block!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=item exists('key')
|
||||||
|
|
||||||
|
This method returns just true if the given key exists in the config.
|
||||||
|
|
||||||
|
|
||||||
|
=item keys('key')
|
||||||
|
|
||||||
|
Returns an array of the keys under the specified "key". If you use the example
|
||||||
|
config above you could do that:
|
||||||
|
|
||||||
|
print Dumper($conf->keys("individual");
|
||||||
|
$VAR1 = [ "martin", "joseph" ];
|
||||||
|
|
||||||
|
If no key name was supplied, then the keys of the object itself will be returned.
|
||||||
|
|
||||||
|
You can use this method in B<foreach> loops as seen in an example above(obj() ).
|
||||||
|
|
||||||
|
|
||||||
|
=item delete('key')
|
||||||
|
|
||||||
|
This method removes the given key and all associated data from the internal
|
||||||
|
hash structure. If 'key' contained data, then this data will be returned,
|
||||||
|
otherwise undef will be returned.
|
||||||
|
|
||||||
|
=item find(@list)
|
||||||
|
|
||||||
|
Given a list of nodes, ->find will search for a tree that branches in
|
||||||
|
just this way, returning the Config::General::Extended object it finds
|
||||||
|
at the bottom if it exists. You can also search partway down the tree
|
||||||
|
and ->find should return where you left off.
|
||||||
|
|
||||||
|
For example, given the values B<find (qw (A B C))> and the following
|
||||||
|
tree (</end> tags omitted for brevity):
|
||||||
|
|
||||||
|
<A>
|
||||||
|
<FOO>
|
||||||
|
...
|
||||||
|
<B>
|
||||||
|
<BAZ>
|
||||||
|
...
|
||||||
|
<C>
|
||||||
|
BAR = shoo
|
||||||
|
|
||||||
|
B<find()> will find the object at I<C> with the value BAR = shoo and
|
||||||
|
return it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
|
||||||
|
=head1 AUTOLOAD METHODS
|
||||||
|
|
||||||
|
Another useful feature is implemented in this class using the B<AUTOLOAD> feature
|
||||||
|
of perl. If you know the keynames of a block within your config, you can access to
|
||||||
|
the values of each individual key using the method notation. See the following example
|
||||||
|
and you will get it:
|
||||||
|
|
||||||
|
We assume the following config:
|
||||||
|
|
||||||
|
<person>
|
||||||
|
name = Moser
|
||||||
|
prename = Peter
|
||||||
|
birth = 12.10.1972
|
||||||
|
</person>
|
||||||
|
|
||||||
|
Now we read it in and process it:
|
||||||
|
|
||||||
|
my $conf = Config::General::Extended->new("configfile");
|
||||||
|
my $person = $conf->obj("person");
|
||||||
|
print $person->prename . " " . $person->name . " is " . $person->age . " years old\n";
|
||||||
|
|
||||||
|
This notation supports only scalar values! You need to make sure, that the block
|
||||||
|
<person> does not contain any subblock or multiple identical options(which will become
|
||||||
|
an array after parsing)!
|
||||||
|
|
||||||
|
If you access a non-existent key this way, Config::General will croak an error.
|
||||||
|
You can turn this behavior off by setting B<-StrictObjects> to 0 or "no". In
|
||||||
|
this case undef will be returned.
|
||||||
|
|
||||||
|
Of course you can use this kind of methods for writing data too:
|
||||||
|
|
||||||
|
$person->name("Neustein");
|
||||||
|
|
||||||
|
This changes the value of the "name" key to "Neustein". This feature behaves exactly like
|
||||||
|
B<value()>, which means you can assign hash or array references as well and that existing
|
||||||
|
values under the given key will be overwritten.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 2000-2022 Thomas Linden
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the Artistic License 2.0.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 BUGS
|
||||||
|
|
||||||
|
none known yet.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Thomas Linden <tlinden |AT| cpan.org>
|
||||||
|
|
||||||
|
=head1 VERSION
|
||||||
|
|
||||||
|
2.07
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
|
@ -0,0 +1,370 @@
|
||||||
|
#
|
||||||
|
# Config::General::Interpolated - special Class based on Config::General
|
||||||
|
#
|
||||||
|
# Copyright (c) 2001 by Wei-Hon Chen <plasmaball@pchome.com.tw>.
|
||||||
|
# Copyright (c) 2000-2022 by Thomas Linden <tlinden |AT| cpan.org>.
|
||||||
|
# All Rights Reserved. Std. disclaimer applies.
|
||||||
|
# Licensed under the terms of the Artistic License 2.0.
|
||||||
|
#
|
||||||
|
|
||||||
|
package Config::General::Interpolated;
|
||||||
|
$Config::General::Interpolated::VERSION = "2.16";
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Carp;
|
||||||
|
use Config::General;
|
||||||
|
use Exporter ();
|
||||||
|
|
||||||
|
|
||||||
|
# Import stuff from Config::General
|
||||||
|
use vars qw(@ISA @EXPORT);
|
||||||
|
@ISA = qw(Config::General Exporter);
|
||||||
|
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
#
|
||||||
|
# overwrite new() with our own version
|
||||||
|
# and call the parent class new()
|
||||||
|
#
|
||||||
|
|
||||||
|
croak "Deprecated method Config::General::Interpolated::new() called.\n"
|
||||||
|
."Use Config::General::new() instead and set the -InterPolateVars flag.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub _set_regex {
|
||||||
|
#
|
||||||
|
# set the regex for finding vars
|
||||||
|
#
|
||||||
|
|
||||||
|
# the following regex is provided by Autrijus Tang
|
||||||
|
# <autrijus@autrijus.org>, and I made some modifications.
|
||||||
|
# thanx, autrijus. :)
|
||||||
|
my $regex = qr{
|
||||||
|
(^|\G|[^\\]) # $1: can be the beginning of the line
|
||||||
|
# or the beginning of next match
|
||||||
|
# but can't begin with a '\'
|
||||||
|
\$ # dollar sign
|
||||||
|
(\{)? # $2: optional opening curly
|
||||||
|
([a-zA-Z0-9][a-zA-Z0-9_\-\.:\+]*) # $3: capturing variable name (fix of #33447+118746)
|
||||||
|
(?(2) # $4: if there's the opening curly...
|
||||||
|
\} # ... match closing curly
|
||||||
|
)
|
||||||
|
}x;
|
||||||
|
return $regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _interpolate {
|
||||||
|
#
|
||||||
|
# interpolate a scalar value and keep the result
|
||||||
|
# on the varstack.
|
||||||
|
#
|
||||||
|
# called directly by Config::General::_parse_value()
|
||||||
|
#
|
||||||
|
my ($this, $config, $key, $value) = @_;
|
||||||
|
my $quote_counter = 100;
|
||||||
|
|
||||||
|
# some dirty trick to circumvent single quoted vars to be interpolated
|
||||||
|
# we remove all quotes and replace them with unique random literals,
|
||||||
|
# which will be replaced after interpolation with the original quotes
|
||||||
|
# fixes bug rt#35766
|
||||||
|
my %quotes;
|
||||||
|
|
||||||
|
if(! $this->{AllowSingleQuoteInterpolation} ) {
|
||||||
|
$value =~ s/(\'[^\']+?\')/
|
||||||
|
my $key = "QUOTE" . ($quote_counter++) . "QUOTE";
|
||||||
|
$quotes{ $key } = $1;
|
||||||
|
$key;
|
||||||
|
/gex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value =~ s{$this->{regex}}{
|
||||||
|
my $con = $1;
|
||||||
|
my $var = $3;
|
||||||
|
my $var_lc = $this->{LowerCaseNames} ? lc($var) : $var;
|
||||||
|
|
||||||
|
if (exists $config->{__stack}->{$var_lc}) {
|
||||||
|
$con . $config->{__stack}->{$var_lc};
|
||||||
|
}
|
||||||
|
elsif ($this->{InterPolateEnv}) {
|
||||||
|
# may lead to vulnerabilities, by default flag turned off
|
||||||
|
if (defined($ENV{$var})) {
|
||||||
|
$con . $ENV{$var};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$con;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif ($this->{StrictVars}) {
|
||||||
|
croak "Use of uninitialized variable (\$$var) while loading config entry: $key = $value\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# be cool
|
||||||
|
$con;
|
||||||
|
}
|
||||||
|
}egx;
|
||||||
|
|
||||||
|
# re-insert unaltered quotes
|
||||||
|
# fixes bug rt#35766
|
||||||
|
foreach my $quote (keys %quotes) {
|
||||||
|
$value =~ s/$quote/$quotes{$quote}/;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
sub _interpolate_hash {
|
||||||
|
#
|
||||||
|
# interpolate a complete hash and keep the results
|
||||||
|
# on the varstack.
|
||||||
|
#
|
||||||
|
# called directly by Config::General::new()
|
||||||
|
#
|
||||||
|
my ($this, $config) = @_;
|
||||||
|
|
||||||
|
# bugfix rt.cpan.org#46184, moved code from _interpolate() to here.
|
||||||
|
if ($this->{InterPolateEnv}) {
|
||||||
|
# may lead to vulnerabilities, by default flag turned off
|
||||||
|
for my $key (keys %ENV){
|
||||||
|
$config->{__stack}->{$key}=$ENV{$key};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $this->_var_hash_stacker($config);
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _var_hash_stacker {
|
||||||
|
#
|
||||||
|
# build a varstack of a given hash ref
|
||||||
|
#
|
||||||
|
my ($this, $config) = @_;
|
||||||
|
|
||||||
|
foreach my $key (keys %{$config}) {
|
||||||
|
next if($key eq "__stack");
|
||||||
|
if (ref($config->{$key}) eq "ARRAY" ) {
|
||||||
|
$config->{$key} = $this->_var_array_stacker($config->{$key}, $key);
|
||||||
|
}
|
||||||
|
elsif (ref($config->{$key}) eq "HASH") {
|
||||||
|
my $tmphash = $config->{$key};
|
||||||
|
$tmphash->{__stack} = $config->{__stack};
|
||||||
|
$config->{$key} = $this->_var_hash_stacker($tmphash);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# SCALAR
|
||||||
|
$config->{__stack}->{$key} = $config->{$key};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _var_array_stacker {
|
||||||
|
#
|
||||||
|
# same as _var_hash_stacker but for arrayrefs
|
||||||
|
#
|
||||||
|
my ($this, $config, $key) = @_;
|
||||||
|
|
||||||
|
my @new;
|
||||||
|
|
||||||
|
foreach my $entry (@{$config}) {
|
||||||
|
if (ref($entry) eq "HASH") {
|
||||||
|
$entry = $this->_var_hash_stacker($entry);
|
||||||
|
}
|
||||||
|
elsif (ref($entry) eq "ARRAY") {
|
||||||
|
# ignore this. Arrays of Arrays cannot be created/supported
|
||||||
|
# with Config::General, because they are not accessible by
|
||||||
|
# any key (anonymous array-ref)
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#### $config->{__stack}->{$key} = $config->{$key};
|
||||||
|
# removed. a array of scalars (eg: option = [1,2,3]) cannot
|
||||||
|
# be used for interpolation (which one shall we use?!), so
|
||||||
|
# we ignore those types of lists.
|
||||||
|
# found by fbicknel, fixes rt.cpan.org#41570
|
||||||
|
}
|
||||||
|
push @new, $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@new;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _clean_stack {
|
||||||
|
#
|
||||||
|
# recursively empty the variable stack
|
||||||
|
#
|
||||||
|
my ($this, $config) = @_;
|
||||||
|
#return $config; # DEBUG
|
||||||
|
foreach my $key (keys %{$config}) {
|
||||||
|
if ($key eq "__stack") {
|
||||||
|
delete $config->{__stack};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if (ref($config->{$key}) eq "ARRAY" ) {
|
||||||
|
$config->{$key} = $this->_clean_array_stack($config->{$key});
|
||||||
|
}
|
||||||
|
elsif (ref($config->{$key}) eq "HASH") {
|
||||||
|
$config->{$key} = $this->_clean_stack($config->{$key});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _clean_array_stack {
|
||||||
|
#
|
||||||
|
# same as _var_hash_stacker but for arrayrefs
|
||||||
|
#
|
||||||
|
my ($this, $config) = @_;
|
||||||
|
|
||||||
|
my @new;
|
||||||
|
|
||||||
|
foreach my $entry (@{$config}) {
|
||||||
|
if (ref($entry) eq "HASH") {
|
||||||
|
$entry = $this->_clean_stack($entry);
|
||||||
|
}
|
||||||
|
elsif (ref($entry) eq "ARRAY") {
|
||||||
|
# ignore this. Arrays of Arrays cannot be created/supported
|
||||||
|
# with Config::General, because they are not accessible by
|
||||||
|
# any key (anonymous array-ref)
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
push @new, $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@new;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Config::General::Interpolated - Parse variables within Config files
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use Config::General;
|
||||||
|
$conf = Config::General->new(
|
||||||
|
-ConfigFile => 'configfile',
|
||||||
|
-InterPolateVars => 1
|
||||||
|
);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This is an internal module which makes it possible to interpolate
|
||||||
|
Perl style variables in your config file (i.e. C<$variable>
|
||||||
|
or C<${variable}>).
|
||||||
|
|
||||||
|
Normally you don't call it directly.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 VARIABLES
|
||||||
|
|
||||||
|
Variables can be defined everywhere in the config and can be used
|
||||||
|
afterwards as the value of an option. Variables cannot be used as
|
||||||
|
keys or as part of keys.
|
||||||
|
|
||||||
|
If you define a variable inside
|
||||||
|
a block or a named block then it is only visible within this block or
|
||||||
|
within blocks which are defined inside this block. Well - let's take a
|
||||||
|
look to an example:
|
||||||
|
|
||||||
|
# sample config which uses variables
|
||||||
|
basedir = /opt/ora
|
||||||
|
user = t_space
|
||||||
|
sys = unix
|
||||||
|
<table intern>
|
||||||
|
instance = INTERN
|
||||||
|
owner = $user # "t_space"
|
||||||
|
logdir = $basedir/log # "/opt/ora/log"
|
||||||
|
sys = macos
|
||||||
|
<procs>
|
||||||
|
misc1 = ${sys}_${instance} # macos_INTERN
|
||||||
|
misc2 = $user # "t_space"
|
||||||
|
</procs>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
This will result in the following structure:
|
||||||
|
|
||||||
|
{
|
||||||
|
'basedir' => '/opt/ora',
|
||||||
|
'user' => 't_space'
|
||||||
|
'sys' => 'unix',
|
||||||
|
'table' => {
|
||||||
|
'intern' => {
|
||||||
|
'sys' => 'macos',
|
||||||
|
'logdir' => '/opt/ora/log',
|
||||||
|
'instance' => 'INTERN',
|
||||||
|
'owner' => 't_space',
|
||||||
|
'procs' => {
|
||||||
|
'misc1' => 'macos_INTERN',
|
||||||
|
'misc2' => 't_space'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
As you can see, the variable B<sys> has been defined twice. Inside
|
||||||
|
the <procs> block a variable ${sys} has been used, which then were
|
||||||
|
interpolated into the value of B<sys> defined inside the <table>
|
||||||
|
block, not the sys variable one level above. If sys were not defined
|
||||||
|
inside the <table> block then the "global" variable B<sys> would have
|
||||||
|
been used instead with the value of "unix".
|
||||||
|
|
||||||
|
Variables inside double quotes will be interpolated, but variables
|
||||||
|
inside single quotes will B<not> interpolated. This is the same
|
||||||
|
behavior as you know of Perl itself.
|
||||||
|
|
||||||
|
In addition you can surround variable names with curly braces to
|
||||||
|
avoid misinterpretation by the parser.
|
||||||
|
|
||||||
|
=head1 NAMING CONVENTIONS
|
||||||
|
|
||||||
|
Variable names must:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item * start with a US-ASCII letter(a-z or A-Z) or a digit (0-9).
|
||||||
|
|
||||||
|
=item * contain only US-ASCII letter(a-z or A-Z), digits (0-9), the dash (-)
|
||||||
|
colon (:), dot (.), underscore (_) and plus (+) characters.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
For added clarity variable names can be surrounded by curly braces.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<Config::General>
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Thomas Linden <tlinden |AT| cpan.org>
|
||||||
|
Autrijus Tang <autrijus@autrijus.org>
|
||||||
|
Wei-Hon Chen <plasmaball@pchome.com.tw>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright 2001 by Wei-Hon Chen E<lt>plasmaball@pchome.com.twE<gt>.
|
||||||
|
Copyright 2002-2022 by Thomas Linden <tlinden |AT| cpan.org>.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the Artistic License 2.0.
|
||||||
|
|
||||||
|
See L<http://www.perl.com/perl/misc/Artistic.html>
|
||||||
|
|
||||||
|
=head1 VERSION
|
||||||
|
|
||||||
|
2.16
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
Changelog
|
||||||
|
example.cfg
|
||||||
|
General/Extended.pm
|
||||||
|
General/Interpolated.pm
|
||||||
|
General.pm
|
||||||
|
Makefile.PL
|
||||||
|
MANIFEST
|
||||||
|
META.yml
|
||||||
|
README
|
||||||
|
t/apache-include.conf
|
||||||
|
t/apache-include-opt.conf
|
||||||
|
t/notincluded.conf.not
|
||||||
|
t/cfg.16
|
||||||
|
t/cfg.16a
|
||||||
|
t/cfg.17
|
||||||
|
t/cfg.19
|
||||||
|
t/cfg.2
|
||||||
|
t/cfg.20.a
|
||||||
|
t/cfg.20.b
|
||||||
|
t/cfg.20.c
|
||||||
|
t/cfg.3
|
||||||
|
t/cfg.34
|
||||||
|
t/cfg.39
|
||||||
|
t/cfg.4
|
||||||
|
t/cfg.40
|
||||||
|
t/cfg.41
|
||||||
|
t/cfg.42
|
||||||
|
t/cfg.43
|
||||||
|
t/cfg.45
|
||||||
|
t/cfg.46
|
||||||
|
t/cfg.5
|
||||||
|
t/cfg.6
|
||||||
|
t/cfg.7
|
||||||
|
t/cfg.8
|
||||||
|
t/cfg.55
|
||||||
|
t/complex/n1.cfg
|
||||||
|
t/complex/n2.cfg
|
||||||
|
t/complex.cfg
|
||||||
|
t/dual-include.conf
|
||||||
|
t/included.conf
|
||||||
|
t/run.t
|
||||||
|
t/sub1/cfg.sub1
|
||||||
|
t/sub1/cfg.sub1b
|
||||||
|
t/sub1/cfg.sub1c
|
||||||
|
t/sub1/cfg.sub1d
|
||||||
|
t/sub1/cfg.sub1e
|
||||||
|
t/sub1/sub2/cfg.sub2
|
||||||
|
t/sub1/sub2/cfg.sub2b
|
||||||
|
t/sub1/sub2/sub3/cfg.sub3
|
||||||
|
t/test.rc
|
||||||
|
t/Tie/IxHash.pm
|
||||||
|
t/Tie/README
|
||||||
|
t/cfg.51
|
||||||
|
t/utf8_bom/bar.cfg
|
||||||
|
t/utf8_bom/foo.cfg
|
||||||
|
t/cfg.58
|
||||||
|
META.json Module JSON meta-data (added by MakeMaker)
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"abstract" : "unknown",
|
||||||
|
"author" : [
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
|
"dynamic_config" : 1,
|
||||||
|
"generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010",
|
||||||
|
"license" : [
|
||||||
|
"artistic_2"
|
||||||
|
],
|
||||||
|
"meta-spec" : {
|
||||||
|
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
|
||||||
|
"version" : 2
|
||||||
|
},
|
||||||
|
"name" : "Config-General",
|
||||||
|
"no_index" : {
|
||||||
|
"directory" : [
|
||||||
|
"t",
|
||||||
|
"inc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prereqs" : {
|
||||||
|
"build" : {
|
||||||
|
"requires" : {
|
||||||
|
"ExtUtils::MakeMaker" : "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configure" : {
|
||||||
|
"requires" : {
|
||||||
|
"ExtUtils::MakeMaker" : "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime" : {
|
||||||
|
"requires" : {
|
||||||
|
"File::Glob" : "0",
|
||||||
|
"File::Spec::Functions" : "0",
|
||||||
|
"FileHandle" : "0",
|
||||||
|
"IO::File" : "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_status" : "stable",
|
||||||
|
"resources" : {
|
||||||
|
"repository" : {
|
||||||
|
"url" : "https://github.com/TLINDEN/Config-General"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version" : "2.65",
|
||||||
|
"x_serialization_backend" : "JSON::PP version 4.04"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
abstract: unknown
|
||||||
|
author:
|
||||||
|
- unknown
|
||||||
|
build_requires:
|
||||||
|
ExtUtils::MakeMaker: '0'
|
||||||
|
configure_requires:
|
||||||
|
ExtUtils::MakeMaker: '0'
|
||||||
|
dynamic_config: 1
|
||||||
|
generated_by: 'ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010'
|
||||||
|
license: artistic_2
|
||||||
|
meta-spec:
|
||||||
|
url: http://module-build.sourceforge.net/META-spec-v1.4.html
|
||||||
|
version: '1.4'
|
||||||
|
name: Config-General
|
||||||
|
no_index:
|
||||||
|
directory:
|
||||||
|
- t
|
||||||
|
- inc
|
||||||
|
requires:
|
||||||
|
File::Glob: '0'
|
||||||
|
File::Spec::Functions: '0'
|
||||||
|
FileHandle: '0'
|
||||||
|
IO::File: '0'
|
||||||
|
resources:
|
||||||
|
repository: https://github.com/TLINDEN/Config-General
|
||||||
|
version: '2.65'
|
||||||
|
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
|
@ -0,0 +1,30 @@
|
||||||
|
#
|
||||||
|
# Makefile.PL - build file for Config::General
|
||||||
|
#
|
||||||
|
# Copyright (c) 2000-2022 Thomas Linden <tom@daemon.de>.
|
||||||
|
# All Rights Reserved. Std. disclaimer applies.
|
||||||
|
# Licensed under the Artistic License 2.0.
|
||||||
|
#
|
||||||
|
|
||||||
|
use ExtUtils::MakeMaker;
|
||||||
|
|
||||||
|
WriteMakefile(
|
||||||
|
'NAME' => 'Config::General',
|
||||||
|
'VERSION_FROM' => 'General.pm',
|
||||||
|
'clean' => {
|
||||||
|
FILES => 't/*.out t/test.cfg *~ */*~'
|
||||||
|
},
|
||||||
|
'PREREQ_PM' => {
|
||||||
|
'IO::File' => 0,
|
||||||
|
'FileHandle' => 0,
|
||||||
|
'File::Spec::Functions' => 0,
|
||||||
|
'File::Glob' => 0
|
||||||
|
},
|
||||||
|
'META_MERGE' => {
|
||||||
|
resources => {
|
||||||
|
repository => 'https://github.com/TLINDEN/Config-General'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
($ExtUtils::MakeMaker::VERSION ge '6.31'?
|
||||||
|
('LICENSE' => 'artistic_2', ) : ()),
|
||||||
|
);
|
|
@ -0,0 +1,107 @@
|
||||||
|
NAME
|
||||||
|
Config::General - Generic Config Module
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
use Config::General;
|
||||||
|
$conf = new Config::General(-ConfigFile => "myconfig.rc");
|
||||||
|
my %config = $conf->getall;
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This module opens a config file and parses it's contents
|
||||||
|
for you. After parsing the module returns a hash structure
|
||||||
|
which contains the representation of the config file.
|
||||||
|
|
||||||
|
The format of config files supported by Config::General is
|
||||||
|
inspired by the well known apache config format, in fact,
|
||||||
|
this module is 100% read-compatible to apache configs, but
|
||||||
|
you can also just use simple name/value pairs in your config
|
||||||
|
files.
|
||||||
|
|
||||||
|
In addition to the capabilities of a apache config file
|
||||||
|
it supports some enhancements such as here-documents, C-
|
||||||
|
style comments or multiline options. It is also possible to
|
||||||
|
save the config back to disk, which makes the module a
|
||||||
|
perfect backend for configuration interfaces.
|
||||||
|
|
||||||
|
It is possible to use variables in config files and there
|
||||||
|
exists also support for object oriented access to the
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
|
||||||
|
to install, type:
|
||||||
|
perl Makefile.PL
|
||||||
|
make
|
||||||
|
make test
|
||||||
|
make install
|
||||||
|
|
||||||
|
to read the complete documentation, type:
|
||||||
|
perldoc Config::General
|
||||||
|
perldoc Config::General::Extended
|
||||||
|
perldoc Config::General::Interpolated
|
||||||
|
|
||||||
|
see some example config files which can
|
||||||
|
be parsed with Config::Genreal in the subdirectory
|
||||||
|
t/cfg.*
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
|
||||||
|
If you are updating from version 1.xx, you might be interested,
|
||||||
|
that some things in the API has changed, which might force you
|
||||||
|
to change your application code. These changes were necessary
|
||||||
|
to clean up the module interface. Now it has a consistent
|
||||||
|
"look and feel" and behaves more naturally. Therefore historic
|
||||||
|
remains were removed.
|
||||||
|
|
||||||
|
Here is a short list:
|
||||||
|
|
||||||
|
o it is no more possible to use Config::General::Extended
|
||||||
|
and Config::General::Interpolated directly. Instead use
|
||||||
|
Config::General and turn on -InterPolateVars and
|
||||||
|
-ExtendedAccess respectively.
|
||||||
|
|
||||||
|
o the method NoMultiOptions() is deprecated. Set the parameter
|
||||||
|
-AllowMultiOptions to false when calling new() to create
|
||||||
|
a new Config::General object.
|
||||||
|
|
||||||
|
o the method save() is deprecated. Use save_file() or
|
||||||
|
save_string() instead.
|
||||||
|
|
||||||
|
o the parameter -file is deprecated. Use -ConfigFile instead.
|
||||||
|
|
||||||
|
o the parameter -hash is deprecated. Use -ConfigHash instead.
|
||||||
|
|
||||||
|
For a more detailed explanation of changes refer to the Changelog.
|
||||||
|
|
||||||
|
|
||||||
|
COPYRIGHT
|
||||||
|
Config::General
|
||||||
|
Config::General::Extended
|
||||||
|
Copyright (c) 2000-2022 by Thomas Linden <tom@daemon.de>
|
||||||
|
|
||||||
|
Config::General::Interpolated
|
||||||
|
Copyright (c) 2001 by Wei-Hon Chen <plasmaball@pchome.com.tw>
|
||||||
|
Copyright (c) 2002-2022 by Thomas Linden <tom@daemon.de>.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it
|
||||||
|
and/or modify it under the terms of the Artistic 2.0 license.
|
||||||
|
|
||||||
|
HOMEPAGE
|
||||||
|
|
||||||
|
The homepage of Config::General is located at:
|
||||||
|
|
||||||
|
http://www.daemon.de/config-general/
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
make test does currently not catch all possible scenarios.
|
||||||
|
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Thomas Linden <tlinden |AT| cpan.org>
|
||||||
|
|
||||||
|
|
||||||
|
VERSION
|
||||||
|
2.65
|
|
@ -0,0 +1,74 @@
|
||||||
|
# -*-sh-*- (ignore, this is just for my operation system, emacs,
|
||||||
|
# to function properly)
|
||||||
|
#
|
||||||
|
# This is an example of a config file supported by Config::General.
|
||||||
|
# It shows almost all features of the format and its flexibility.
|
||||||
|
#
|
||||||
|
# To try it, install Config::General as usual and execute the
|
||||||
|
# following perlscript:
|
||||||
|
#
|
||||||
|
# use Config::General;
|
||||||
|
# use Data::Dumper;
|
||||||
|
# my %conf = ParseConfig(-ConfigFile => "example.cfg", -InterPolateVars => 1);
|
||||||
|
# print Dumper(\%C);'
|
||||||
|
#
|
||||||
|
# This will parse the config and print out a stringified version
|
||||||
|
# of the hash it produces, which can be used in your program.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* c-style comment
|
||||||
|
*/
|
||||||
|
|
||||||
|
# variable assignment
|
||||||
|
option1 = blah
|
||||||
|
option2 blubber
|
||||||
|
option3 = "something special" # this is a comment
|
||||||
|
|
||||||
|
option4 = parameters can be written on \
|
||||||
|
multiple lines
|
||||||
|
|
||||||
|
# duplicate options will be made into an array
|
||||||
|
huc = 12
|
||||||
|
huc = 17
|
||||||
|
huc = 133
|
||||||
|
|
||||||
|
# options can be organized in blocks too
|
||||||
|
<sql>
|
||||||
|
user = hans
|
||||||
|
server = mc200
|
||||||
|
db = maxis
|
||||||
|
passwd = D3rf8d
|
||||||
|
|
||||||
|
# nested blocks are no problem
|
||||||
|
<tablestructure>
|
||||||
|
index int(100000)
|
||||||
|
name char(100)
|
||||||
|
prename char(100)
|
||||||
|
status int(10)
|
||||||
|
</tablestructure>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
# named blocks can also be used
|
||||||
|
<area santa-barbara>
|
||||||
|
# block names containing whitespaces must be quoted
|
||||||
|
<"kyla cole">
|
||||||
|
# blocks maybe empty
|
||||||
|
</"kyla cole">
|
||||||
|
</area>
|
||||||
|
|
||||||
|
# here-docs are fully supported
|
||||||
|
usage <<EOF
|
||||||
|
use with care
|
||||||
|
and don't ask me
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# use of variable interpolation
|
||||||
|
var1 = hoho
|
||||||
|
msg = $var1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# that's it for today.
|
|
@ -0,0 +1,630 @@
|
||||||
|
#
|
||||||
|
# Tie/IxHash.pm
|
||||||
|
#
|
||||||
|
# Indexed hash implementation for Perl
|
||||||
|
#
|
||||||
|
# See below for documentation.
|
||||||
|
#
|
||||||
|
|
||||||
|
require 5.003;
|
||||||
|
|
||||||
|
package Tie::IxHash;
|
||||||
|
use integer;
|
||||||
|
require Tie::Hash;
|
||||||
|
@ISA = qw(Tie::Hash);
|
||||||
|
|
||||||
|
$VERSION = $VERSION = '1.21';
|
||||||
|
|
||||||
|
#
|
||||||
|
# standard tie functions
|
||||||
|
#
|
||||||
|
|
||||||
|
sub TIEHASH {
|
||||||
|
my($c) = shift;
|
||||||
|
my($s) = [];
|
||||||
|
$s->[0] = {}; # hashkey index
|
||||||
|
$s->[1] = []; # array of keys
|
||||||
|
$s->[2] = []; # array of data
|
||||||
|
$s->[3] = 0; # iter count
|
||||||
|
|
||||||
|
bless $s, $c;
|
||||||
|
|
||||||
|
$s->Push(@_) if @_;
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sub DESTROY {} # costly if there's nothing to do
|
||||||
|
|
||||||
|
sub FETCH {
|
||||||
|
my($s, $k) = (shift, shift);
|
||||||
|
return exists( $s->[0]{$k} ) ? $s->[2][ $s->[0]{$k} ] : undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub STORE {
|
||||||
|
my($s, $k, $v) = (shift, shift, shift);
|
||||||
|
|
||||||
|
if (exists $s->[0]{$k}) {
|
||||||
|
my($i) = $s->[0]{$k};
|
||||||
|
$s->[1][$i] = $k;
|
||||||
|
$s->[2][$i] = $v;
|
||||||
|
$s->[0]{$k} = $i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push(@{$s->[1]}, $k);
|
||||||
|
push(@{$s->[2]}, $v);
|
||||||
|
$s->[0]{$k} = $#{$s->[1]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub DELETE {
|
||||||
|
my($s, $k) = (shift, shift);
|
||||||
|
|
||||||
|
if (exists $s->[0]{$k}) {
|
||||||
|
my($i) = $s->[0]{$k};
|
||||||
|
for ($i+1..$#{$s->[1]}) { # reset higher elt indexes
|
||||||
|
$s->[0]{$s->[1][$_]}--; # timeconsuming, is there is better way?
|
||||||
|
}
|
||||||
|
delete $s->[0]{$k};
|
||||||
|
splice @{$s->[1]}, $i, 1;
|
||||||
|
return (splice(@{$s->[2]}, $i, 1))[0];
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub EXISTS {
|
||||||
|
exists $_[0]->[0]{ $_[1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FIRSTKEY {
|
||||||
|
$_[0][3] = 0;
|
||||||
|
&NEXTKEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub NEXTKEY {
|
||||||
|
return $_[0][1][$_[0][3]++] if ($_[0][3] <= $#{$_[0][1]});
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# class functions that provide additional capabilities
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
sub new { TIEHASH(@_) }
|
||||||
|
|
||||||
|
#
|
||||||
|
# add pairs to end of indexed hash
|
||||||
|
# note that if a supplied key exists, it will not be reordered
|
||||||
|
#
|
||||||
|
sub Push {
|
||||||
|
my($s) = shift;
|
||||||
|
while (@_) {
|
||||||
|
$s->STORE(shift, shift);
|
||||||
|
}
|
||||||
|
return scalar(@{$s->[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Push2 {
|
||||||
|
my($s) = shift;
|
||||||
|
$s->Splice($#{$s->[1]}+1, 0, @_);
|
||||||
|
return scalar(@{$s->[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# pop last k-v pair
|
||||||
|
#
|
||||||
|
sub Pop {
|
||||||
|
my($s) = shift;
|
||||||
|
my($k, $v, $i);
|
||||||
|
$k = pop(@{$s->[1]});
|
||||||
|
$v = pop(@{$s->[2]});
|
||||||
|
if (defined $k) {
|
||||||
|
delete $s->[0]{$k};
|
||||||
|
return ($k, $v);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Pop2 {
|
||||||
|
return $_[0]->Splice(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# shift
|
||||||
|
#
|
||||||
|
sub Shift {
|
||||||
|
my($s) = shift;
|
||||||
|
my($k, $v, $i);
|
||||||
|
$k = shift(@{$s->[1]});
|
||||||
|
$v = shift(@{$s->[2]});
|
||||||
|
if (defined $k) {
|
||||||
|
delete $s->[0]{$k};
|
||||||
|
for (keys %{$s->[0]}) {
|
||||||
|
$s->[0]{$_}--;
|
||||||
|
}
|
||||||
|
return ($k, $v);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Shift2 {
|
||||||
|
return $_[0]->Splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# unshift
|
||||||
|
# if a supplied key exists, it will not be reordered
|
||||||
|
#
|
||||||
|
sub Unshift {
|
||||||
|
my($s) = shift;
|
||||||
|
my($k, $v, @k, @v, $len, $i);
|
||||||
|
|
||||||
|
while (@_) {
|
||||||
|
($k, $v) = (shift, shift);
|
||||||
|
if (exists $s->[0]{$k}) {
|
||||||
|
$i = $s->[0]{$k};
|
||||||
|
$s->[1][$i] = $k;
|
||||||
|
$s->[2][$i] = $v;
|
||||||
|
$s->[0]{$k} = $i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push(@k, $k);
|
||||||
|
push(@v, $v);
|
||||||
|
$len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (defined $len) {
|
||||||
|
for (keys %{$s->[0]}) {
|
||||||
|
$s->[0]{$_} += $len;
|
||||||
|
}
|
||||||
|
$i = 0;
|
||||||
|
for (@k) {
|
||||||
|
$s->[0]{$_} = $i++;
|
||||||
|
}
|
||||||
|
unshift(@{$s->[1]}, @k);
|
||||||
|
return unshift(@{$s->[2]}, @v);
|
||||||
|
}
|
||||||
|
return scalar(@{$s->[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Unshift2 {
|
||||||
|
my($s) = shift;
|
||||||
|
$s->Splice(0,0,@_);
|
||||||
|
return scalar(@{$s->[1]});
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# splice
|
||||||
|
#
|
||||||
|
# any existing hash key order is preserved. the value is replaced for
|
||||||
|
# such keys, and the new keys are spliced in the regular fashion.
|
||||||
|
#
|
||||||
|
# supports -ve offsets but only +ve lengths
|
||||||
|
#
|
||||||
|
# always assumes a 0 start offset
|
||||||
|
#
|
||||||
|
sub Splice {
|
||||||
|
my($s, $start, $len) = (shift, shift, shift);
|
||||||
|
my($k, $v, @k, @v, @r, $i, $siz);
|
||||||
|
my($end); # inclusive
|
||||||
|
|
||||||
|
# XXX inline this
|
||||||
|
($start, $end, $len) = $s->_lrange($start, $len);
|
||||||
|
|
||||||
|
if (defined $start) {
|
||||||
|
if ($len > 0) {
|
||||||
|
my(@k) = splice(@{$s->[1]}, $start, $len);
|
||||||
|
my(@v) = splice(@{$s->[2]}, $start, $len);
|
||||||
|
while (@k) {
|
||||||
|
$k = shift(@k);
|
||||||
|
delete $s->[0]{$k};
|
||||||
|
push(@r, $k, shift(@v));
|
||||||
|
}
|
||||||
|
for ($start..$#{$s->[1]}) {
|
||||||
|
$s->[0]{$s->[1][$_]} -= $len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (@_) {
|
||||||
|
($k, $v) = (shift, shift);
|
||||||
|
if (exists $s->[0]{$k}) {
|
||||||
|
# $s->STORE($k, $v);
|
||||||
|
$i = $s->[0]{$k};
|
||||||
|
$s->[1][$i] = $k;
|
||||||
|
$s->[2][$i] = $v;
|
||||||
|
$s->[0]{$k} = $i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push(@k, $k);
|
||||||
|
push(@v, $v);
|
||||||
|
$siz++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (defined $siz) {
|
||||||
|
for ($start..$#{$s->[1]}) {
|
||||||
|
$s->[0]{$s->[1][$_]} += $siz;
|
||||||
|
}
|
||||||
|
$i = $start;
|
||||||
|
for (@k) {
|
||||||
|
$s->[0]{$_} = $i++;
|
||||||
|
}
|
||||||
|
splice(@{$s->[1]}, $start, 0, @k);
|
||||||
|
splice(@{$s->[2]}, $start, 0, @v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @r;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# delete elements specified by key
|
||||||
|
# other elements higher than the one deleted "slide" down
|
||||||
|
#
|
||||||
|
sub Delete {
|
||||||
|
my($s) = shift;
|
||||||
|
|
||||||
|
for (@_) {
|
||||||
|
#
|
||||||
|
# XXX potential optimization: could do $s->DELETE only if $#_ < 4.
|
||||||
|
# otherwise, should reset all the hash indices in one loop
|
||||||
|
#
|
||||||
|
$s->DELETE($_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# replace hash element at specified index
|
||||||
|
#
|
||||||
|
# if the optional key is not supplied the value at index will simply be
|
||||||
|
# replaced without affecting the order.
|
||||||
|
#
|
||||||
|
# if an element with the supplied key already exists, it will be deleted first.
|
||||||
|
#
|
||||||
|
# returns the key of replaced value if it succeeds.
|
||||||
|
#
|
||||||
|
sub Replace {
|
||||||
|
my($s) = shift;
|
||||||
|
my($i, $v, $k) = (shift, shift, shift);
|
||||||
|
if (defined $i and $i <= $#{$s->[1]} and $i >= 0) {
|
||||||
|
if (defined $k) {
|
||||||
|
delete $s->[0]{ $s->[1][$i] };
|
||||||
|
$s->DELETE($k) ; #if exists $s->[0]{$k};
|
||||||
|
$s->[1][$i] = $k;
|
||||||
|
$s->[2][$i] = $v;
|
||||||
|
$s->[0]{$k} = $i;
|
||||||
|
return $k;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$s->[2][$i] = $v;
|
||||||
|
return $s->[1][$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Given an $start and $len, returns a legal start and end (where start <= end)
|
||||||
|
# for the current hash.
|
||||||
|
# Legal range is defined as 0 to $#s+1
|
||||||
|
# $len defaults to number of elts upto end of list
|
||||||
|
#
|
||||||
|
# 0 1 2 ...
|
||||||
|
# | X | X | X ... X | X | X |
|
||||||
|
# -2 -1 (no -0 alas)
|
||||||
|
# X's above are the elements
|
||||||
|
#
|
||||||
|
sub _lrange {
|
||||||
|
my($s) = shift;
|
||||||
|
my($offset, $len) = @_;
|
||||||
|
my($start, $end); # both inclusive
|
||||||
|
my($size) = $#{$s->[1]}+1;
|
||||||
|
|
||||||
|
return undef unless defined $offset;
|
||||||
|
if($offset < 0) {
|
||||||
|
$start = $offset + $size;
|
||||||
|
$start = 0 if $start < 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
($offset > $size) ? ($start = $size) : ($start = $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined $len) {
|
||||||
|
$len = -$len if $len < 0;
|
||||||
|
$len = $size - $start if $len > $size - $start;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$len = $size - $start;
|
||||||
|
}
|
||||||
|
$end = $start + $len - 1;
|
||||||
|
|
||||||
|
return ($start, $end, $len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return keys at supplied indices
|
||||||
|
# Returns all keys if no args.
|
||||||
|
#
|
||||||
|
sub Keys {
|
||||||
|
my($s) = shift;
|
||||||
|
return ( @_ == 1
|
||||||
|
? $s->[1][$_[0]]
|
||||||
|
: ( @_
|
||||||
|
? @{$s->[1]}[@_]
|
||||||
|
: @{$s->[1]} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Returns values at supplied indices
|
||||||
|
# Returns all values if no args.
|
||||||
|
#
|
||||||
|
sub Values {
|
||||||
|
my($s) = shift;
|
||||||
|
return ( @_ == 1
|
||||||
|
? $s->[2][$_[0]]
|
||||||
|
: ( @_
|
||||||
|
? @{$s->[2]}[@_]
|
||||||
|
: @{$s->[2]} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# get indices of specified hash keys
|
||||||
|
#
|
||||||
|
sub Indices {
|
||||||
|
my($s) = shift;
|
||||||
|
return ( @_ == 1 ? $s->[0]{$_[0]} : @{$s->[0]}{@_} );
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# number of k-v pairs in the ixhash
|
||||||
|
# note that this does not equal the highest index
|
||||||
|
# owing to preextended arrays
|
||||||
|
#
|
||||||
|
sub Length {
|
||||||
|
return scalar @{$_[0]->[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Reorder the hash in the supplied key order
|
||||||
|
#
|
||||||
|
# warning: any unsupplied keys will be lost from the hash
|
||||||
|
# any supplied keys that dont exist in the hash will be ignored
|
||||||
|
#
|
||||||
|
sub Reorder {
|
||||||
|
my($s) = shift;
|
||||||
|
my(@k, @v, %x, $i);
|
||||||
|
return unless @_;
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
for (@_) {
|
||||||
|
if (exists $s->[0]{$_}) {
|
||||||
|
push(@k, $_);
|
||||||
|
push(@v, $s->[2][ $s->[0]{$_} ] );
|
||||||
|
$x{$_} = $i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$s->[1] = \@k;
|
||||||
|
$s->[2] = \@v;
|
||||||
|
$s->[0] = \%x;
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub SortByKey {
|
||||||
|
my($s) = shift;
|
||||||
|
$s->Reorder(sort $s->Keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub SortByValue {
|
||||||
|
my($s) = shift;
|
||||||
|
$s->Reorder(sort { $s->FETCH($a) cmp $s->FETCH($b) } $s->Keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Tie::IxHash - ordered associative arrays for Perl
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
# simple usage
|
||||||
|
use Tie::IxHash;
|
||||||
|
tie HASHVARIABLE, Tie::IxHash [, LIST];
|
||||||
|
|
||||||
|
# OO interface with more powerful features
|
||||||
|
use Tie::IxHash;
|
||||||
|
TIEOBJECT = Tie::IxHash->new( [LIST] );
|
||||||
|
TIEOBJECT->Splice( OFFSET [, LENGTH [, LIST]] );
|
||||||
|
TIEOBJECT->Push( LIST );
|
||||||
|
TIEOBJECT->Pop;
|
||||||
|
TIEOBJECT->Shift;
|
||||||
|
TIEOBJECT->Unshift( LIST );
|
||||||
|
TIEOBJECT->Keys( [LIST] );
|
||||||
|
TIEOBJECT->Values( [LIST] );
|
||||||
|
TIEOBJECT->Indices( LIST );
|
||||||
|
TIEOBJECT->Delete( [LIST] );
|
||||||
|
TIEOBJECT->Replace( OFFSET, VALUE, [KEY] );
|
||||||
|
TIEOBJECT->Reorder( LIST );
|
||||||
|
TIEOBJECT->SortByKey;
|
||||||
|
TIEOBJECT->SortByValue;
|
||||||
|
TIEOBJECT->Length;
|
||||||
|
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This Perl module implements Perl hashes that preserve the order in which the
|
||||||
|
hash elements were added. The order is not affected when values
|
||||||
|
corresponding to existing keys in the IxHash are changed. The elements can
|
||||||
|
also be set to any arbitrary supplied order. The familiar perl array
|
||||||
|
operations can also be performed on the IxHash.
|
||||||
|
|
||||||
|
|
||||||
|
=head2 Standard C<TIEHASH> Interface
|
||||||
|
|
||||||
|
The standard C<TIEHASH> mechanism is available. This interface is
|
||||||
|
recommended for simple uses, since the usage is exactly the same as
|
||||||
|
regular Perl hashes after the C<tie> is declared.
|
||||||
|
|
||||||
|
|
||||||
|
=head2 Object Interface
|
||||||
|
|
||||||
|
This module also provides an extended object-oriented interface that can be
|
||||||
|
used for more powerful operations with the IxHash. The following methods
|
||||||
|
are available:
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item FETCH, STORE, DELETE, EXISTS
|
||||||
|
|
||||||
|
These standard C<TIEHASH> methods mandated by Perl can be used directly.
|
||||||
|
See the C<tie> entry in perlfunc(1) for details.
|
||||||
|
|
||||||
|
=item Push, Pop, Shift, Unshift, Splice
|
||||||
|
|
||||||
|
These additional methods resembling Perl functions are available for
|
||||||
|
operating on key-value pairs in the IxHash. The behavior is the same as the
|
||||||
|
corresponding perl functions, except when a supplied hash key already exists
|
||||||
|
in the hash. In that case, the existing value is updated but its order is
|
||||||
|
not affected. To unconditionally alter the order of a supplied key-value
|
||||||
|
pair, first C<DELETE> the IxHash element.
|
||||||
|
|
||||||
|
=item Keys
|
||||||
|
|
||||||
|
Returns an array of IxHash element keys corresponding to the list of supplied
|
||||||
|
indices. Returns an array of all the keys if called without arguments.
|
||||||
|
Note the return value is mostly only useful when used in a list context
|
||||||
|
(since perl will convert it to the number of elements in the array when
|
||||||
|
used in a scalar context, and that may not be very useful).
|
||||||
|
|
||||||
|
If a single argument is given, returns the single key corresponding to
|
||||||
|
the index. This is usable in either scalar or list context.
|
||||||
|
|
||||||
|
=item Values
|
||||||
|
|
||||||
|
Returns an array of IxHash element values corresponding to the list of supplied
|
||||||
|
indices. Returns an array of all the values if called without arguments.
|
||||||
|
Note the return value is mostly only useful when used in a list context
|
||||||
|
(since perl will convert it to the number of elements in the array when
|
||||||
|
used in a scalar context, and that may not be very useful).
|
||||||
|
|
||||||
|
If a single argument is given, returns the single value corresponding to
|
||||||
|
the index. This is usable in either scalar or list context.
|
||||||
|
|
||||||
|
=item Indices
|
||||||
|
|
||||||
|
Returns an array of indices corresponding to the supplied list of keys.
|
||||||
|
Note the return value is mostly only useful when used in a list context
|
||||||
|
(since perl will convert it to the number of elements in the array when
|
||||||
|
used in a scalar context, and that may not be very useful).
|
||||||
|
|
||||||
|
If a single argument is given, returns the single index corresponding to
|
||||||
|
the key. This is usable in either scalar or list context.
|
||||||
|
|
||||||
|
=item Delete
|
||||||
|
|
||||||
|
Removes elements with the supplied keys from the IxHash.
|
||||||
|
|
||||||
|
=item Replace
|
||||||
|
|
||||||
|
Substitutes the IxHash element at the specified index with the supplied
|
||||||
|
value-key pair. If a key is not supplied, simply substitutes the value at
|
||||||
|
index with the supplied value. If an element with the supplied key already
|
||||||
|
exists, it will be removed from the IxHash first.
|
||||||
|
|
||||||
|
=item Reorder
|
||||||
|
|
||||||
|
This method can be used to manipulate the internal order of the IxHash
|
||||||
|
elements by supplying a list of keys in the desired order. Note however,
|
||||||
|
that any IxHash elements whose keys are not in the list will be removed from
|
||||||
|
the IxHash.
|
||||||
|
|
||||||
|
=item Length
|
||||||
|
|
||||||
|
Returns the number of IxHash elements.
|
||||||
|
|
||||||
|
=item SortByKey
|
||||||
|
|
||||||
|
Reorders the IxHash elements by textual comparison of the keys.
|
||||||
|
|
||||||
|
=item SortByValue
|
||||||
|
|
||||||
|
Reorders the IxHash elements by textual comparison of the values.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
|
||||||
|
=head1 EXAMPLE
|
||||||
|
|
||||||
|
use Tie::IxHash;
|
||||||
|
|
||||||
|
# simple interface
|
||||||
|
$t = tie(%myhash, Tie::IxHash, 'a' => 1, 'b' => 2);
|
||||||
|
%myhash = (first => 1, second => 2, third => 3);
|
||||||
|
$myhash{fourth} = 4;
|
||||||
|
@keys = keys %myhash;
|
||||||
|
@values = values %myhash;
|
||||||
|
print("y") if exists $myhash{third};
|
||||||
|
|
||||||
|
# OO interface
|
||||||
|
$t = Tie::IxHash->new(first => 1, second => 2, third => 3);
|
||||||
|
$t->Push(fourth => 4); # same as $myhash{'fourth'} = 4;
|
||||||
|
($k, $v) = $t->Pop; # $k is 'fourth', $v is 4
|
||||||
|
$t->Unshift(neg => -1, zeroth => 0);
|
||||||
|
($k, $v) = $t->Shift; # $k is 'neg', $v is -1
|
||||||
|
@oneandtwo = $t->Splice(1, 2, foo => 100, bar => 101);
|
||||||
|
|
||||||
|
@keys = $t->Keys;
|
||||||
|
@values = $t->Values;
|
||||||
|
@indices = $t->Indices('foo', 'zeroth');
|
||||||
|
@itemkeys = $t->Keys(@indices);
|
||||||
|
@itemvals = $t->Values(@indices);
|
||||||
|
$t->Replace(2, 0.3, 'other');
|
||||||
|
$t->Delete('second', 'zeroth');
|
||||||
|
$len = $t->Length; # number of key-value pairs
|
||||||
|
|
||||||
|
$t->Reorder(reverse @keys);
|
||||||
|
$t->SortByKey;
|
||||||
|
$t->SortByValue;
|
||||||
|
|
||||||
|
|
||||||
|
=head1 BUGS
|
||||||
|
|
||||||
|
You cannot specify a negative length to C<Splice>. Negative indexes are OK,
|
||||||
|
though.
|
||||||
|
|
||||||
|
Indexing always begins at 0 (despite the current C<$[> setting) for
|
||||||
|
all the functions.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 TODO
|
||||||
|
|
||||||
|
Addition of elements with keys that already exist to the end of the IxHash
|
||||||
|
must be controlled by a switch.
|
||||||
|
|
||||||
|
Provide C<TIEARRAY> interface when it stabilizes in Perl.
|
||||||
|
|
||||||
|
Rewrite using XSUBs for efficiency.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Gurusamy Sarathy gsar@umich.edu
|
||||||
|
|
||||||
|
Copyright (c) 1995 Gurusamy Sarathy. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the same terms as Perl itself.
|
||||||
|
|
||||||
|
|
||||||
|
=head1 VERSION
|
||||||
|
|
||||||
|
Version 1.21 20 Nov 1997
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
perl(1)
|
||||||
|
|
||||||
|
=cut
|
|
@ -0,0 +1,7 @@
|
||||||
|
This module exists here just to satisfy 'make test'
|
||||||
|
because it tests the -tie functionality. It is NOT
|
||||||
|
part of Config::General itself, which doesn't depend
|
||||||
|
on it.
|
||||||
|
|
||||||
|
|
||||||
|
Tom
|
|
@ -0,0 +1,7 @@
|
||||||
|
<bit two>
|
||||||
|
IncludeOptional t/included.conf
|
||||||
|
</bit>
|
||||||
|
<bit one>
|
||||||
|
nink ack
|
||||||
|
IncludeOptional t/notincluded.conf
|
||||||
|
</bit>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<bit one>
|
||||||
|
include t/included.conf
|
||||||
|
</bit>
|
||||||
|
<bit two>
|
||||||
|
include "t/included.conf"
|
||||||
|
</bit>
|
|
@ -0,0 +1,32 @@
|
||||||
|
# variable interpolation test
|
||||||
|
me=blah
|
||||||
|
pr=$me/blubber
|
||||||
|
<vars>
|
||||||
|
base = /usr
|
||||||
|
uid = 501
|
||||||
|
</vars>
|
||||||
|
|
||||||
|
base = /opt
|
||||||
|
<etc>
|
||||||
|
base = /usr # set $base to a new value in this scope
|
||||||
|
log = ${base}/log/logfile # use braces
|
||||||
|
<users>
|
||||||
|
home = $base/home/max # $base should be /usr, not /opt !
|
||||||
|
</users>
|
||||||
|
</etc>
|
||||||
|
|
||||||
|
# block(name) test
|
||||||
|
tag = dir
|
||||||
|
mono = teri
|
||||||
|
<$tag>
|
||||||
|
bl = 1
|
||||||
|
</$tag>
|
||||||
|
<$tag mono>
|
||||||
|
bl = 2
|
||||||
|
</$tag>
|
||||||
|
<text $mono>
|
||||||
|
bl = 3
|
||||||
|
</text>
|
||||||
|
<$tag $mono>
|
||||||
|
bl = 3
|
||||||
|
</$tag>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<etc>
|
||||||
|
log = ${HOME}/log/logfile # use braces
|
||||||
|
</etc>
|
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# these options must all in
|
||||||
|
# msg[\d] keys.
|
||||||
|
#
|
||||||
|
msg1 = "Das ist ein Test"
|
||||||
|
msg2 = "Das = ein Test"
|
||||||
|
msg3 "Das ist ein Test"
|
||||||
|
msg4 "Das = ein Test"
|
||||||
|
|
||||||
|
msg6 = <<EOF
|
||||||
|
Das = ein Test
|
||||||
|
EOF
|
||||||
|
|
||||||
|
msg7 <<EOF
|
||||||
|
Das = ein Test
|
||||||
|
msg7
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Nested block test
|
||||||
|
|
||||||
|
<cops>
|
||||||
|
<officer randall>
|
||||||
|
name stein
|
||||||
|
age 25
|
||||||
|
color \#000000
|
||||||
|
</officer>
|
||||||
|
<officer gordon>
|
||||||
|
name bird
|
||||||
|
age 31
|
||||||
|
color \#ffffff
|
||||||
|
</officer>
|
||||||
|
</cops>
|
|
@ -0,0 +1,2 @@
|
||||||
|
seen_cfg.20.a = true
|
||||||
|
<<include t/cfg.20.b>>
|
|
@ -0,0 +1,2 @@
|
||||||
|
seen_cfg.20.b = true
|
||||||
|
<<include t/cfg.20.c>>
|
|
@ -0,0 +1,2 @@
|
||||||
|
seen_cfg.20.c = true
|
||||||
|
last = cfg.20.c
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Array content test
|
||||||
|
domain b0fh.org
|
||||||
|
domain l0pht.com
|
||||||
|
domain infonexus.com
|
|
@ -0,0 +1,18 @@
|
||||||
|
<a>
|
||||||
|
var1 = yes
|
||||||
|
var2 = on
|
||||||
|
var3 = true
|
||||||
|
var4 = no
|
||||||
|
var5 = off
|
||||||
|
var6 = false
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<b>
|
||||||
|
var1 = Yes
|
||||||
|
var2 = On
|
||||||
|
var3 = TRUE
|
||||||
|
var4 = nO
|
||||||
|
var5 = oFf
|
||||||
|
var6 = False
|
||||||
|
</b>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<outer b1>
|
||||||
|
test = foo
|
||||||
|
<inner>
|
||||||
|
ivar = $test
|
||||||
|
</inner>
|
||||||
|
</outer>
|
||||||
|
|
||||||
|
<outer b2>
|
||||||
|
test = bar
|
||||||
|
<inner>
|
||||||
|
ivar = $test
|
||||||
|
</inner>
|
||||||
|
</outer>
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Here-document test
|
||||||
|
|
||||||
|
header = <<EOF
|
||||||
|
<table border="0">
|
||||||
|
</table>
|
||||||
|
EOF
|
|
@ -0,0 +1,7 @@
|
||||||
|
# should generate an error about invalid structure
|
||||||
|
# array of scalars => hashref
|
||||||
|
val = 1
|
||||||
|
val = 2
|
||||||
|
<val 3>
|
||||||
|
x = no
|
||||||
|
</val>
|
|
@ -0,0 +1,6 @@
|
||||||
|
# should generate an error about invalid structure
|
||||||
|
# scalar => hashref
|
||||||
|
val = 1
|
||||||
|
<val 2>
|
||||||
|
x = no
|
||||||
|
</val>
|
|
@ -0,0 +1,13 @@
|
||||||
|
# should generate an error about invalid structure
|
||||||
|
# array of hashrefs => scalar
|
||||||
|
|
||||||
|
<val 1>
|
||||||
|
x = no
|
||||||
|
</val>
|
||||||
|
|
||||||
|
val = 3
|
||||||
|
|
||||||
|
<val 2>
|
||||||
|
x = no
|
||||||
|
</val>
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# should generate an error about invalid structure
|
||||||
|
val = 1
|
||||||
|
<val>
|
||||||
|
x = 2
|
||||||
|
</val>
|
|
@ -0,0 +1,14 @@
|
||||||
|
param1 = value1
|
||||||
|
param2 = value2
|
||||||
|
|
||||||
|
<block1>
|
||||||
|
param2 = value3
|
||||||
|
param4 = $param1 # expect: "value1"
|
||||||
|
param5 = $param2 # expect: "value3"
|
||||||
|
</block1>
|
||||||
|
|
||||||
|
<block2>
|
||||||
|
param6 = $param1 # expect: "value1"
|
||||||
|
param7 = $param2 # expect: "value2"
|
||||||
|
</block2>
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
foo = bar
|
||||||
|
blah = blubber
|
||||||
|
test = $foo 'variable $blah should be kept' and '$foo too'
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Multiline option test
|
||||||
|
command = ssh -f -g orpheus.0x49.org \
|
||||||
|
-l azrael -L:34777samir.okir.da.ru:22 \
|
||||||
|
-L:31773:shane.sol1.rocket.de:22 \
|
||||||
|
'exec sleep 99999990'
|
|
@ -0,0 +1,5 @@
|
||||||
|
dollar = \$foo
|
||||||
|
backslash = contains \\ backslash
|
||||||
|
prize = 18 $
|
||||||
|
hostparam = "\"'wsh.dir'\""
|
||||||
|
bgcolor = \#fff
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Comment test
|
||||||
|
user = tom # a comment right after a line
|
||||||
|
/*
|
||||||
|
* C-style comment (multiline)
|
||||||
|
*/
|
||||||
|
passwd = sakkra
|
||||||
|
<db>
|
||||||
|
/* oneline C-style comment */
|
||||||
|
host = blah.blubber
|
||||||
|
</db>
|
||||||
|
<foo> #
|
||||||
|
bar = baz
|
||||||
|
</foo>
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Case insensitive block test
|
||||||
|
|
||||||
|
<Cops>
|
||||||
|
<OFFICER randall>
|
||||||
|
name stein
|
||||||
|
age 25
|
||||||
|
</officer>
|
||||||
|
</copS>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<cops>
|
||||||
|
<officer randall>
|
||||||
|
name stein
|
||||||
|
age 25
|
||||||
|
</officer>
|
||||||
|
<officer gordon>
|
||||||
|
name bird
|
||||||
|
age 31
|
||||||
|
</officer>
|
||||||
|
</cops>
|
||||||
|
domain nix.to
|
||||||
|
domain b0fh.org
|
||||||
|
domain foo.bar
|
||||||
|
message <<EOF
|
||||||
|
yes. we are not here. you
|
||||||
|
can reach us somewhere in
|
||||||
|
outerspace.
|
||||||
|
EOF
|
||||||
|
nocomment <<EOF
|
||||||
|
Comments in a here-doc should not be treated as comments.
|
||||||
|
/* So this should appear in the output */
|
||||||
|
EOF
|
||||||
|
command = ssh -f -g orpheus.0x49.org \
|
||||||
|
-l azrael -L:34777samir.okir.da.ru:22 \
|
||||||
|
-L:31773:shane.sol1.rocket.de:22 \
|
||||||
|
'exec sleep 99999990'
|
||||||
|
user = tom
|
||||||
|
passwd = sakkra
|
||||||
|
<db>
|
||||||
|
host = blah.blubber
|
||||||
|
</db>
|
||||||
|
|
||||||
|
<beta>
|
||||||
|
user1 hans
|
||||||
|
</beta>
|
||||||
|
|
||||||
|
<beta>
|
||||||
|
user2 max
|
||||||
|
</beta>
|
||||||
|
|
||||||
|
quoted = "this one contains whitespace at the end "
|
||||||
|
|
||||||
|
quotedwithquotes = " holy crap, it contains \"masked quotes\" and 'single quotes' "
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# complexity test
|
||||||
|
var1 = zero # comment
|
||||||
|
var2 = zeppelin /* another comment */
|
||||||
|
/*
|
||||||
|
to be ignored
|
||||||
|
*/
|
||||||
|
line = a\
|
||||||
|
long line
|
||||||
|
var3 = blah
|
||||||
|
set = $var3
|
||||||
|
ignore = \$set
|
||||||
|
quote = this should be 'kept: $set' and not be '$set!'
|
||||||
|
host = gw.intx.foo
|
||||||
|
cmd = mart@${host}:22
|
||||||
|
onflag = yes
|
||||||
|
offflag = No
|
||||||
|
<<include complex/n*.cfg>>
|
||||||
|
a [[weird]] heredoc = <<EOF
|
||||||
|
has to
|
||||||
|
work
|
||||||
|
too!
|
||||||
|
EOF
|
||||||
|
auch ätzendes = muss gehen
|
||||||
|
someflags = LOCK | RW | TAINT
|
||||||
|
imported = got $this from $default config
|
||||||
|
<hansa>
|
||||||
|
<<include complex/n2.cfg>>
|
||||||
|
</hansa>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<a>
|
||||||
|
<b>
|
||||||
|
x = 9323
|
||||||
|
z = 000
|
||||||
|
<m $x>
|
||||||
|
g = $z
|
||||||
|
long = another long \
|
||||||
|
line
|
||||||
|
</m>
|
||||||
|
/*
|
||||||
|
please ignore this */
|
||||||
|
</b>
|
||||||
|
<b>
|
||||||
|
z = rewe
|
||||||
|
</b>
|
||||||
|
</a>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<Directory />
|
||||||
|
mode = 755
|
||||||
|
</Directory>
|
||||||
|
<Files "~/*.pl">
|
||||||
|
Options = +Indexes
|
||||||
|
</Files>
|
||||||
|
nando = 11111
|
||||||
|
<z1>
|
||||||
|
blak = $nando
|
||||||
|
nando = 9999
|
||||||
|
</z1>
|
||||||
|
<x5>
|
||||||
|
klack = $nando
|
||||||
|
</x5>
|
||||||
|
<block 0>
|
||||||
|
value = 0
|
||||||
|
</block>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<bit one>
|
||||||
|
<<include t/included.conf>>
|
||||||
|
</bit>
|
||||||
|
<bit two>
|
||||||
|
<<include t/included.conf>>
|
||||||
|
</bit>
|
|
@ -0,0 +1 @@
|
||||||
|
honk=bonk
|
|
@ -0,0 +1 @@
|
||||||
|
honk=NONONO
|
|
@ -0,0 +1,793 @@
|
||||||
|
# -*-perl-*-
|
||||||
|
# testscript for Config::General Classes by Thomas Linden
|
||||||
|
#
|
||||||
|
# needs to be invoked using the command "make test" from
|
||||||
|
# the Config::General source directory.
|
||||||
|
#
|
||||||
|
# Under normal circumstances every test should succeed.
|
||||||
|
|
||||||
|
|
||||||
|
use Data::Dumper;
|
||||||
|
use Test::More tests => 78;
|
||||||
|
#use Test::More qw(no_plan);
|
||||||
|
|
||||||
|
# ahem, we deliver the test code with a local copy of
|
||||||
|
# the Tie::IxHash module so we can do tests on sorted
|
||||||
|
# hashes without dependency to Tie::IxHash.
|
||||||
|
use lib qw(t);
|
||||||
|
use Tie::IxHash;
|
||||||
|
my @WARNINGS_FOUND;
|
||||||
|
BEGIN {
|
||||||
|
$SIG{__WARN__} = sub { diag( "WARN: ", join( '', @_ ) ); push @WARNINGS_FOUND, @_ };
|
||||||
|
}
|
||||||
|
|
||||||
|
### 1
|
||||||
|
BEGIN { use_ok "Config::General"};
|
||||||
|
require_ok( 'Config::General' );
|
||||||
|
|
||||||
|
### 2 - 7
|
||||||
|
foreach my $num (2..7) {
|
||||||
|
my $cfg = "t/cfg.$num";
|
||||||
|
open T, "<$cfg";
|
||||||
|
my @file = <T>;
|
||||||
|
close T;
|
||||||
|
my $fst = $file[0];
|
||||||
|
chomp $fst;
|
||||||
|
$fst =~ s/\#\s*//g;
|
||||||
|
eval {
|
||||||
|
my $conf = new Config::General($cfg);
|
||||||
|
my %hash = $conf->getall;
|
||||||
|
};
|
||||||
|
ok(!$@, "$fst");
|
||||||
|
}
|
||||||
|
|
||||||
|
### 8
|
||||||
|
my $conf = new Config::General("t/cfg.8");
|
||||||
|
my %hash = $conf->getall;
|
||||||
|
$conf->save_file("t/cfg.out");
|
||||||
|
my $copy = new Config::General("t/cfg.out");
|
||||||
|
my %copyhash = $copy->getall;
|
||||||
|
is_deeply(\%hash, \%copyhash, "Writing Config Hash to disk and compare with original");
|
||||||
|
|
||||||
|
# 8a
|
||||||
|
like($copyhash{nocomment}, qr/this should appear/, "C-comments not processed in here-doc");
|
||||||
|
|
||||||
|
### 9
|
||||||
|
$conf = new Config::General(
|
||||||
|
-ExtendedAccess => 1,
|
||||||
|
-ConfigFile => "t/test.rc");
|
||||||
|
ok($conf, "Creating a new object from config file");
|
||||||
|
|
||||||
|
|
||||||
|
### 10
|
||||||
|
my $conf2 = new Config::General(
|
||||||
|
-ExtendedAccess => 1,
|
||||||
|
-ConfigFile => "t/test.rc",
|
||||||
|
-AllowMultiOptions => "yes"
|
||||||
|
);
|
||||||
|
ok($conf2, "Creating a new object using the hash parameter way");
|
||||||
|
|
||||||
|
|
||||||
|
### 11
|
||||||
|
my $domain = $conf->obj("domain");
|
||||||
|
ok($domain, "Creating a new object from a block");
|
||||||
|
|
||||||
|
|
||||||
|
### 12
|
||||||
|
my $addr = $domain->obj("bar.de");
|
||||||
|
ok($addr, "Creating a new object from a sub block");
|
||||||
|
|
||||||
|
|
||||||
|
### 13
|
||||||
|
my @keys = $conf->keys("domain");
|
||||||
|
ok($#keys > -1, "Getting values from the object");
|
||||||
|
|
||||||
|
|
||||||
|
### 14
|
||||||
|
# test various OO methods
|
||||||
|
my $a;
|
||||||
|
if ($conf->is_hash("domain")) {
|
||||||
|
my $domains = $conf->obj("domain");
|
||||||
|
foreach my $domain ($conf->keys("domain")) {
|
||||||
|
my $domain_obj = $domains->obj($domain);
|
||||||
|
foreach my $address ($domains->keys($domain)) {
|
||||||
|
$a = $domain_obj->value($address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok($a, "Using keys() and values()");
|
||||||
|
|
||||||
|
### 15
|
||||||
|
# test AUTOLOAD methods
|
||||||
|
eval {
|
||||||
|
my $conf3 = new Config::General(
|
||||||
|
-ExtendedAccess => 1,
|
||||||
|
-ConfigHash => { name => "Moser", prename => "Hannes"}
|
||||||
|
);
|
||||||
|
my $n = $conf3->name;
|
||||||
|
my $p = $conf3->prename;
|
||||||
|
$conf3->name("Meier");
|
||||||
|
$conf3->prename("Max");
|
||||||
|
$conf3->save_file("t/test.cfg");
|
||||||
|
};
|
||||||
|
ok (!$@, "Using AUTOLOAD methods");
|
||||||
|
|
||||||
|
|
||||||
|
### 16
|
||||||
|
# testing variable interpolation
|
||||||
|
my $conf16 = new Config::General(-ConfigFile => "t/cfg.16", -InterPolateVars => 1, -StrictVars => 0);
|
||||||
|
my %h16 = $conf16->getall();
|
||||||
|
if($h16{etc}->{log} eq "/usr/log/logfile" and
|
||||||
|
$h16{etc}->{users}->{home} eq "/usr/home/max" and
|
||||||
|
exists $h16{dir}->{teri}->{bl}) {
|
||||||
|
pass("Testing variable interpolation");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail("Testing variable interpolation");
|
||||||
|
}
|
||||||
|
|
||||||
|
### 16.a
|
||||||
|
# testing variable interpolation with %ENV use
|
||||||
|
my $env = "/home/theunexistent";
|
||||||
|
$ENV{HOME} = $env;
|
||||||
|
my $conf16a = new Config::General(-ConfigFile => "t/cfg.16a", -InterPolateVars => 1, -InterPolateEnv => 1, -StrictVars => 0);
|
||||||
|
my %h16a = $conf16a->getall();
|
||||||
|
if($h16a{etc}->{log} eq "$env/log/logfile") {
|
||||||
|
pass("Testing environment variable interpolation");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail("Testing environment variable interpolation");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### 17
|
||||||
|
# testing value pre-setting using a hash
|
||||||
|
my $conf17 = new Config::General(
|
||||||
|
-file => "t/cfg.17",
|
||||||
|
-DefaultConfig => { home => "/exports/home",
|
||||||
|
logs => "/var/backlog",
|
||||||
|
foo => {
|
||||||
|
bar => "quux"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-InterPolateVars => 1,
|
||||||
|
-MergeDuplicateOptions => 1,
|
||||||
|
-MergeDuplicateBlocks => 1
|
||||||
|
);
|
||||||
|
my %h17 = $conf17->getall();
|
||||||
|
ok ($h17{home} eq "/home/users" &&
|
||||||
|
$h17{foo}{quux} eq "quux",
|
||||||
|
"Testing value pre-setting using a hash");
|
||||||
|
|
||||||
|
|
||||||
|
### 18
|
||||||
|
# testing value pre-setting using a string
|
||||||
|
my $conf18 = new Config::General(
|
||||||
|
-file => "t/cfg.17", # reuse the file
|
||||||
|
-DefaultConfig => "home = /exports/home\nlogs = /var/backlog",
|
||||||
|
-MergeDuplicateOptions => 1,
|
||||||
|
-MergeDuplicateBlocks => 1
|
||||||
|
);
|
||||||
|
my %h18 = $conf18->getall();
|
||||||
|
ok ($h18{home} eq "/home/users", "Testing value pre-setting using a string");
|
||||||
|
|
||||||
|
|
||||||
|
### 19
|
||||||
|
# testing various otion/value assignment notations
|
||||||
|
my $conf19 = new Config::General(-file => "t/cfg.19");
|
||||||
|
my %h19 = $conf19->getall();
|
||||||
|
my $works = 1;
|
||||||
|
foreach my $key (keys %h19) {
|
||||||
|
if ($key =~ /\s/) {
|
||||||
|
$works = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok ($works, "Testing various otion/value assignment notations");
|
||||||
|
|
||||||
|
### 20
|
||||||
|
# testing files() method
|
||||||
|
my $conf20 = Config::General->new(
|
||||||
|
-file => "t/cfg.20.a",
|
||||||
|
-MergeDuplicateOptions => 1
|
||||||
|
);
|
||||||
|
my %h20 = $conf20->getall();
|
||||||
|
my %files = map { $_ => 1 } $conf20->files();
|
||||||
|
my %expected_files = map { $_ => 1 } (
|
||||||
|
't/cfg.20.a',
|
||||||
|
't/cfg.20.b',
|
||||||
|
't/cfg.20.c',
|
||||||
|
);
|
||||||
|
is_deeply (\%files, \%expected_files, "testing files() method");
|
||||||
|
|
||||||
|
|
||||||
|
### 22
|
||||||
|
# testing improved IncludeRelative option
|
||||||
|
# First try without -IncludeRelative
|
||||||
|
# this should fail
|
||||||
|
eval {
|
||||||
|
my $conf21 = Config::General->new(
|
||||||
|
-file => "t/sub1/sub2/sub3/cfg.sub3",
|
||||||
|
-MergeDuplicateOptions => 1,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
ok ($@, "prevented from loading relative cfgs without -IncludeRelative");
|
||||||
|
|
||||||
|
|
||||||
|
### 23
|
||||||
|
# Now try with -IncludeRelative
|
||||||
|
# this should fail
|
||||||
|
my $conf22 = Config::General->new(
|
||||||
|
-file => "t/sub1/sub2/sub3/cfg.sub3",
|
||||||
|
-MergeDuplicateOptions => 1,
|
||||||
|
-IncludeRelative => 1,
|
||||||
|
);
|
||||||
|
my %h22 = $conf22->getall;
|
||||||
|
my %expected_h22 = (
|
||||||
|
'sub3_seen' => 'yup',
|
||||||
|
'sub2_seen' => 'yup',
|
||||||
|
'sub2b_seen' => 'yup',
|
||||||
|
'sub1_seen' => 'yup',
|
||||||
|
'sub1b_seen' => 'yup',
|
||||||
|
'fruit' => 'mango',
|
||||||
|
);
|
||||||
|
is_deeply(\%h22, \%expected_h22, "loaded relative to included files");
|
||||||
|
|
||||||
|
|
||||||
|
### 24
|
||||||
|
# Testing IncludeDirectories option
|
||||||
|
my $conf23 = Config::General->new(
|
||||||
|
-String => "<<include t/sub1>>",
|
||||||
|
-IncludeDirectories => 1
|
||||||
|
);
|
||||||
|
my %h23 = $conf23->getall;
|
||||||
|
my %expected_h23 = (
|
||||||
|
fruit => 'mango',
|
||||||
|
sub1_seen => 'yup',
|
||||||
|
sub1b_seen => 'yup',
|
||||||
|
test => 'value',
|
||||||
|
test2 => 'value2',
|
||||||
|
test3 => 'value3'
|
||||||
|
);
|
||||||
|
is_deeply(\%h23, \%expected_h23, "including a directory with -IncludeDirectories");
|
||||||
|
|
||||||
|
|
||||||
|
### 24
|
||||||
|
# Testing IncludeGlob option
|
||||||
|
my $conf24 = Config::General->new(
|
||||||
|
-String => "<<include t/sub1/cfg.sub[123]{c,d,e}>>",
|
||||||
|
-IncludeGlob => 1
|
||||||
|
);
|
||||||
|
my %h24 = $conf24->getall;
|
||||||
|
my %expected_h24 = (
|
||||||
|
test => 'value',
|
||||||
|
test2 => 'value2',
|
||||||
|
test3 => 'value3'
|
||||||
|
);
|
||||||
|
is_deeply(\%h24, \%expected_h24, "including multiple files via glob pattern with -IncludeGlob");
|
||||||
|
|
||||||
|
|
||||||
|
### 25
|
||||||
|
# Testing block and block name quoting
|
||||||
|
my $conf25 = Config::General->new(
|
||||||
|
-String => <<TEST,
|
||||||
|
<block "/">
|
||||||
|
opt1 val1
|
||||||
|
</block>
|
||||||
|
<"block2 /">
|
||||||
|
opt2 val2
|
||||||
|
</"block2 /">
|
||||||
|
<"block 3" "/">
|
||||||
|
opt3 val3
|
||||||
|
</"block 3">
|
||||||
|
<block4 />
|
||||||
|
opt4 val4
|
||||||
|
</block4>
|
||||||
|
TEST
|
||||||
|
-SlashIsDirectory => 1
|
||||||
|
);
|
||||||
|
my %h25 = $conf25->getall;
|
||||||
|
my %expected_h25 = (
|
||||||
|
block => { '/' => { opt1 => 'val1' } },
|
||||||
|
'block2 /' => { opt2 => 'val2' },
|
||||||
|
'block 3' => { '/' => { opt3 => 'val3' } },
|
||||||
|
block4 => { '/' => { opt4 => 'val4' } }
|
||||||
|
);
|
||||||
|
is_deeply(\%h25, \%expected_h25, "block and block name quoting");
|
||||||
|
|
||||||
|
|
||||||
|
### 26
|
||||||
|
# Testing 0-value handling
|
||||||
|
my $conf26 = Config::General->new(
|
||||||
|
-String => <<TEST,
|
||||||
|
<foo 0>
|
||||||
|
0
|
||||||
|
</foo>
|
||||||
|
TEST
|
||||||
|
);
|
||||||
|
my %h26 = $conf26->getall;
|
||||||
|
my %expected_h26 = (
|
||||||
|
foo => { 0 => { 0 => undef } },
|
||||||
|
);
|
||||||
|
is_deeply(\%h26, \%expected_h26, "testing 0-values in block names");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# look if invalid input gets rejected right
|
||||||
|
#
|
||||||
|
|
||||||
|
### 27
|
||||||
|
# testing invalid parameter calls, expected to fail
|
||||||
|
my @pt = (
|
||||||
|
{
|
||||||
|
p => {-ConfigHash => "StringNotHash"},
|
||||||
|
t => "-ConfigHash HASH required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
p => {-String => {}},
|
||||||
|
t => "-String STRING required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
p => {-ConfigFile => {}},
|
||||||
|
t => "-ConfigFile STRING required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
p => {-ConfigFile => "NoFile"},
|
||||||
|
t => "-ConfigFile STRING File must exist and be readable"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
foreach my $C (@pt) {
|
||||||
|
eval {
|
||||||
|
my $cfg = new Config::General(%{$C->{p}});
|
||||||
|
};
|
||||||
|
ok ($@, "check parameter failure handling $C->{t}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 32
|
||||||
|
# check Flagbits
|
||||||
|
my $cfg28 = new Config::General(
|
||||||
|
-String => "Mode = CLEAR | UNSECURE",
|
||||||
|
-FlagBits => {
|
||||||
|
Mode => {
|
||||||
|
CLEAR => 1,
|
||||||
|
STRONG => 1,
|
||||||
|
UNSECURE => "32bit"
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
my %cfg28 = $cfg28->getall();
|
||||||
|
is_deeply(\%cfg28,
|
||||||
|
{
|
||||||
|
'Mode' => {
|
||||||
|
'STRONG' => undef,
|
||||||
|
'UNSECURE' => '32bit',
|
||||||
|
'CLEAR' => 1
|
||||||
|
}}, "Checking -Flagbits resolving");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 33
|
||||||
|
# checking functional interface
|
||||||
|
eval {
|
||||||
|
my %conf = Config::General::ParseConfig(-ConfigFile => "t/test.rc");
|
||||||
|
Config::General::SaveConfig("t/test.rc.out", \%conf);
|
||||||
|
my %next = Config::General::ParseConfig(-ConfigFile => "t/test.rc.out");
|
||||||
|
my @a = sort keys %conf;
|
||||||
|
my @b = sort keys %next;
|
||||||
|
if (@a != @b) {
|
||||||
|
die "Re-parsed result differs from original";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ok(! $@, "Testing functional interface $@");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 34
|
||||||
|
# testing -AutoTrue
|
||||||
|
my $cfg34 = new Config::General(-AutoTrue => 1, -ConfigFile => "t/cfg.34");
|
||||||
|
my %cfg34 = $cfg34->getall();
|
||||||
|
my %expect34 = (
|
||||||
|
'a' => {
|
||||||
|
'var6' => 0,
|
||||||
|
'var3' => 1,
|
||||||
|
'var1' => 1,
|
||||||
|
'var4' => 0,
|
||||||
|
'var2' => 1,
|
||||||
|
'var5' => 0
|
||||||
|
},
|
||||||
|
'b' => {
|
||||||
|
'var6' => 0,
|
||||||
|
'var3' => 1,
|
||||||
|
'var1' => 1,
|
||||||
|
'var4' => 0,
|
||||||
|
'var2' => 1,
|
||||||
|
'var5' => 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
is_deeply(\%cfg34, \%expect34, "Using -AutoTrue");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 35
|
||||||
|
# testing -SplitPolicy
|
||||||
|
my %conf35 = Config::General::ParseConfig(
|
||||||
|
-String =>
|
||||||
|
qq(var1 :: alpha
|
||||||
|
var2 :: beta
|
||||||
|
var3 = gamma # use wrong delimiter by purpose),
|
||||||
|
-SplitPolicy => 'custom',
|
||||||
|
-SplitDelimiter => '\s*::\s*'
|
||||||
|
);
|
||||||
|
my %expect35 = (
|
||||||
|
'var3 = gamma' => undef,
|
||||||
|
'var1' => 'alpha',
|
||||||
|
'var2' => 'beta'
|
||||||
|
);
|
||||||
|
is_deeply(\%conf35, \%expect35, "Using -SplitPolicy and custom -SplitDelimiter");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Include both
|
||||||
|
my $conf36 = Config::General->new( -ConfigFile => "t/dual-include.conf",
|
||||||
|
-IncludeAgain => 1 );
|
||||||
|
my %C36 = $conf36->getall;
|
||||||
|
is_deeply( \%C36, { bit => { one => { honk=>'bonk' },
|
||||||
|
two => { honk=>'bonk' }
|
||||||
|
} }, "Included twice" );
|
||||||
|
|
||||||
|
|
||||||
|
### Include once
|
||||||
|
{
|
||||||
|
my @expected_warning;
|
||||||
|
local $SIG{__WARN__} = sub { push @expected_warning, @_};
|
||||||
|
|
||||||
|
my $conf37 = Config::General->new( "t/dual-include.conf" );
|
||||||
|
my %C37 = $conf37->getall;
|
||||||
|
is_deeply( \%C37, { bit => { one => { honk=>'bonk' },
|
||||||
|
two => {}
|
||||||
|
} }, "Included once-only" );
|
||||||
|
|
||||||
|
is( @expected_warning, 1, "1 Expected warning" );
|
||||||
|
like( $expected_warning[0], qr/File .* already loaded. Use -IncludeAgain to load it again./ms, "Warns about a file already being loaded" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### apache-style Include
|
||||||
|
my $conf38 = Config::General->new( -ConfigFile => "t/apache-include.conf",
|
||||||
|
-IncludeAgain => 1,
|
||||||
|
-UseApacheInclude => 1 );
|
||||||
|
my %C38 = $conf38->getall;
|
||||||
|
is_deeply( \%C38, { bit => { one => { honk=>'bonk' },
|
||||||
|
two => { honk=>'bonk' }
|
||||||
|
} }, "Apache-style include" );
|
||||||
|
|
||||||
|
|
||||||
|
# verify fix for rt#107108, test support for IncludeOptional
|
||||||
|
my $conf38n = Config::General->new( -ConfigFile => "t/apache-include-opt.conf",
|
||||||
|
-IncludeAgain => 1, -IncludeGlob => 1,
|
||||||
|
-UseApacheInclude => 1 );
|
||||||
|
my %C38n = $conf38n->getall;
|
||||||
|
is_deeply( \%C38n, { bit => { one => { nink=>'ack' },
|
||||||
|
two => { honk=>'bonk' }
|
||||||
|
} }, "Apache-style IncludeOptional" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 39 verifies bug rt#27225
|
||||||
|
# testing variable scope.
|
||||||
|
# a variable shall resolve to the value defined in the current
|
||||||
|
# scope, not a previous outer scope.
|
||||||
|
my $conf39 = new Config::General(-ConfigFile => "t/cfg.39", -InterPolateVars => 1, -StrictVars => 0);
|
||||||
|
my %conf39 = $conf39->getall();
|
||||||
|
isnt($conf39{outer}->{b1}->{inner}->{ivar},
|
||||||
|
$conf39{outer}->{b2}->{inner}->{ivar},
|
||||||
|
"Variable scope test");
|
||||||
|
|
||||||
|
### 40 - 42 verify if structural error checks are working
|
||||||
|
foreach my $pos (40 .. 43) {
|
||||||
|
eval {
|
||||||
|
my $conf = new Config::General(-ConfigFile => "t/cfg.$pos");
|
||||||
|
};
|
||||||
|
ok($@ =~ /^Config::General/, "$pos: Structural error checks");
|
||||||
|
}
|
||||||
|
|
||||||
|
my $conf44;
|
||||||
|
eval {
|
||||||
|
$conf44 = new Config::General(-String => [ 'foo bar' ]);
|
||||||
|
};
|
||||||
|
ok(! $@, "-String arrayref");
|
||||||
|
is_deeply({ $conf44->getall }, { foo => 'bar' }, "-String arrayref contents");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# verifies bug rt#35122
|
||||||
|
my $conf45 = new Config::General(-ConfigFile => "t/cfg.45", -InterPolateVars => 1, -StrictVars => 0);
|
||||||
|
my %conf45 = $conf45->getall();
|
||||||
|
my $expect45 = {
|
||||||
|
'block1' => {
|
||||||
|
'param5' => 'value3',
|
||||||
|
'param4' => 'value1',
|
||||||
|
'param2' => 'value3'
|
||||||
|
},
|
||||||
|
'block2' => {
|
||||||
|
'param7' => 'value2',
|
||||||
|
'param6' => 'value1'
|
||||||
|
},
|
||||||
|
'param2' => 'value2',
|
||||||
|
'param1' => 'value1'
|
||||||
|
};
|
||||||
|
is_deeply($expect45, \%conf45, "Variable precedence");
|
||||||
|
|
||||||
|
# verifies bug rt#35766
|
||||||
|
my $conf46 = new Config::General(-ConfigFile => "t/cfg.46", -InterPolateVars => 1, -StrictVars => 0);
|
||||||
|
my %conf46 = $conf46->getall();
|
||||||
|
my $expect46 = {
|
||||||
|
'blah' => 'blubber',
|
||||||
|
'test' => 'bar \'variable $blah should be kept\' and \'$foo too\'',
|
||||||
|
'foo' => 'bar'
|
||||||
|
};
|
||||||
|
is_deeply($expect46, \%conf46, "Variables inside single quotes");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# complexity test
|
||||||
|
# check the combination of various features
|
||||||
|
my $conf47 = new Config::General(
|
||||||
|
-ConfigFile => "t/complex.cfg",
|
||||||
|
-InterPolateVars => 1,
|
||||||
|
-DefaultConfig => { this => "that", default => "imported" },
|
||||||
|
-MergeDuplicateBlocks => 1,
|
||||||
|
-MergeDuplicateOptions => 1,
|
||||||
|
-StrictVars => 1,
|
||||||
|
-SplitPolicy => 'custom',
|
||||||
|
-SplitDelimiter => '\s*=\s*',
|
||||||
|
-IncludeGlob => 1,
|
||||||
|
-IncludeAgain => 1,
|
||||||
|
-IncludeRelative => 1,
|
||||||
|
-AutoTrue => 1,
|
||||||
|
-FlagBits => { someflags => { LOCK => 1, RW => 2, TAINT => 3 } },
|
||||||
|
-StoreDelimiter => ' = ',
|
||||||
|
-SlashIsDirectory => 1,
|
||||||
|
-SaveSorted => 1
|
||||||
|
);
|
||||||
|
my %conf47 = $conf47->getall();
|
||||||
|
my $expect47 = {
|
||||||
|
'var3' => 'blah',
|
||||||
|
'z1' => {
|
||||||
|
'blak' => '11111',
|
||||||
|
'nando' => '9999'
|
||||||
|
},
|
||||||
|
'a' => {
|
||||||
|
'b' => {
|
||||||
|
'm' => {
|
||||||
|
'9323' => {
|
||||||
|
'g' => '000',
|
||||||
|
'long' => 'another long line'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'x' => '9323',
|
||||||
|
'z' => 'rewe'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'onflag' => 1,
|
||||||
|
'var2' => 'zeppelin',
|
||||||
|
'ignore' => '$set', # escaped $ should get to plain $, not \\$!
|
||||||
|
'quote' => 'this should be \'kept: $set\' and not be \'$set!\'',
|
||||||
|
'x5' => {
|
||||||
|
'klack' => '11111'
|
||||||
|
},
|
||||||
|
'set' => 'blah',
|
||||||
|
'line' => 'along line',
|
||||||
|
'this' => 'that',
|
||||||
|
'imported' => 'got that from imported config',
|
||||||
|
'someflags' => {
|
||||||
|
'RW' => 2,
|
||||||
|
'LOCK' => 1,
|
||||||
|
'TAINT' => 3
|
||||||
|
},
|
||||||
|
'var1' => 'zero',
|
||||||
|
'offflag' => 0,
|
||||||
|
'cmd' => 'mart@gw.intx.foo:22',
|
||||||
|
'default' => 'imported',
|
||||||
|
'host' => 'gw.intx.foo',
|
||||||
|
'nando' => '11111',
|
||||||
|
'auch ätzendes' => 'muss gehen',
|
||||||
|
'Directory' => {
|
||||||
|
'/' => {
|
||||||
|
'mode' => '755'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'hansa' => {
|
||||||
|
'z1' => {
|
||||||
|
'blak' => '11111',
|
||||||
|
'nando' => '9999'
|
||||||
|
},
|
||||||
|
'Directory' => {
|
||||||
|
'/' => {
|
||||||
|
'mode' => '755'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'block' => {
|
||||||
|
'0' => {
|
||||||
|
'value' => 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'x5' => {
|
||||||
|
'klack' => '11111'
|
||||||
|
},
|
||||||
|
'Files' => {
|
||||||
|
'~/*.pl' => {
|
||||||
|
'Options' => '+Indexes'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'nando' => '11111'
|
||||||
|
},
|
||||||
|
'block' => {
|
||||||
|
'0' => {
|
||||||
|
'value' => 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Files' => {
|
||||||
|
'~/*.pl' => {
|
||||||
|
'Options' => '+Indexes'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'a [[weird]] heredoc' => 'has to
|
||||||
|
work
|
||||||
|
too!'
|
||||||
|
};
|
||||||
|
#scip
|
||||||
|
is_deeply($expect47, \%conf47, "complexity test");
|
||||||
|
|
||||||
|
# check if sorted save works
|
||||||
|
$conf47->save_file("t/complex.out", \%conf47);
|
||||||
|
open T, "<t/complex.out";
|
||||||
|
my $got47 = join '', <T>;
|
||||||
|
close T;
|
||||||
|
my $sorted = qq(
|
||||||
|
imported = got that from imported config
|
||||||
|
line = along line
|
||||||
|
nando = 11111
|
||||||
|
offflag = 0
|
||||||
|
onflag = 1);
|
||||||
|
if ($got47 =~ /\Q$sorted\E/) {
|
||||||
|
pass("Testing sorted save");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail("Testing sorted save");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tie my %hash48, "Tie::IxHash";
|
||||||
|
my $ostr48 =
|
||||||
|
"zeppelin 1
|
||||||
|
beach 2
|
||||||
|
anathem 3
|
||||||
|
mercury 4\n";
|
||||||
|
my $cfg48 = new Config::General(
|
||||||
|
-String => $ostr48,
|
||||||
|
-Tie => "Tie::IxHash"
|
||||||
|
);
|
||||||
|
%hash48 = $cfg48->getall();
|
||||||
|
my $str48 = $cfg48->save_string(\%hash48);
|
||||||
|
is( $str48, $ostr48, "tied hash test");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# check for undef and -w
|
||||||
|
{
|
||||||
|
my $ostr49 = "foo\n";
|
||||||
|
local $^W = 1;
|
||||||
|
my $cfg49 = new Config::General( -String => $ostr49 );
|
||||||
|
my %hash49 = $cfg49->getall();
|
||||||
|
ok( exists $hash49{foo}, "value for undefined key found");
|
||||||
|
is( $hash49{foo}, undef, "value returned as expected - undef");
|
||||||
|
|
||||||
|
# repeat with interpolation turned on
|
||||||
|
$cfg49 = new Config::General( -String => $ostr49, -InterPolateVars => 1 );
|
||||||
|
%hash49 = $cfg49->getall();
|
||||||
|
ok( exists $hash49{foo}, "value for undefined key found");
|
||||||
|
is( $hash49{foo}, undef, "value returned as expected - undef");
|
||||||
|
$^W = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# verifies bug fix rt#54580
|
||||||
|
# Test handling of values containing *many* single-quoted strings
|
||||||
|
# when -InterPolateVars option is set
|
||||||
|
my $dupcount50 = 2000;
|
||||||
|
my $ostr50;
|
||||||
|
foreach my $counter ( reverse 1 .. $dupcount50 ) {
|
||||||
|
$ostr50 .= " 'luck${counter}'";
|
||||||
|
}
|
||||||
|
$ostr50 =~ s{\A }{};
|
||||||
|
my $cfgsrc50 = 'test_single_many ' . $ostr50;
|
||||||
|
$cfg50 = new Config::General( -String => $cfgsrc50, -InterPolateVars => 1 );
|
||||||
|
%hash50 = $cfg50->getall();
|
||||||
|
is($hash50{test_single_many}, $ostr50, "value with single-quote strings is as expected" );
|
||||||
|
|
||||||
|
|
||||||
|
# check for escaped chars
|
||||||
|
my $cfg51 = new Config::General( -ConfigFile => "t/cfg.51" );
|
||||||
|
my %hash51 = $cfg51->getall();
|
||||||
|
is($hash51{dollar}, '$foo', "keep escaped dollar character");
|
||||||
|
is($hash51{backslash}, 'contains \ backslash', "keep escaped backslash character");
|
||||||
|
is($hash51{prize}, '18 $', "keep un-escaped dollar character");
|
||||||
|
is($hash51{hostparam}, q("'wsh.dir'"), "keep escaped quote character");
|
||||||
|
is($hash51{bgcolor}, '#fff', "keep escaped number sign");
|
||||||
|
|
||||||
|
# now save it to a file and re-read it in and see if everything remains escaped
|
||||||
|
$cfg51->save_file("t/cfg.51.out");
|
||||||
|
$cfg51 = new Config::General( -ConfigFile => "t/cfg.51.out", -InterPolateVars => 1 );
|
||||||
|
my %hash51new = $cfg51->getall();
|
||||||
|
is_deeply(\%hash51, \%hash51new, "compare saved config containing escaped chars");
|
||||||
|
|
||||||
|
|
||||||
|
# check if forced single value arrays remain
|
||||||
|
my $cfg52 = new Config::General( -String => "habeas = [ corpus ]", -ForceArray => 1);
|
||||||
|
my %hash52 = $cfg52->getall();
|
||||||
|
my @array52 = qw(corpus);
|
||||||
|
is_deeply($hash52{habeas}, \@array52, "check -ForceArray single value arrays");
|
||||||
|
$cfg52->save_file("t/cfg.52.out");
|
||||||
|
$cfg52 = new Config::General( -ConfigFile => "t/cfg.52.out", -ForceArray => 1);
|
||||||
|
my %hash52new = $cfg52->getall();
|
||||||
|
is_deeply(\%hash52new, \%hash52, "check -ForceArray single value arrays during save()");
|
||||||
|
|
||||||
|
my $cfg53 = new Config::General(-AllowSingleQuoteInterpolation => 1, -String => "got = 1\nhave = '\$got'", -InterPolateVars => 1 );
|
||||||
|
my %hash53 = $cfg53->getall();
|
||||||
|
is($hash53{have}, "'1'", "check -AllowSingleQuoteInterpolation");
|
||||||
|
|
||||||
|
|
||||||
|
# Make sure no warnings were seen during the test.
|
||||||
|
ok( !@WARNINGS_FOUND, "No unexpected warnings seen" );
|
||||||
|
|
||||||
|
# check if disabling escape chars does work
|
||||||
|
my $cfg54 = new Config::General(-NoEscape => 1, -String => qq(val = \\\$notavar:\\blah\n));
|
||||||
|
my %hash54 = $cfg54->getall();
|
||||||
|
is($hash54{val}, qq(\\\$notavar:\\blah), "check -NoEscape");
|
||||||
|
|
||||||
|
# check for line continuation followed by empty line (rt.cpan.org#39814)
|
||||||
|
my $cfg55 = new Config::General( -ConfigFile => "t/cfg.55" );
|
||||||
|
my %hash55 = $cfg55->getall();
|
||||||
|
is($hash55{b}, "nochop", "check continuation followed by empty line");
|
||||||
|
|
||||||
|
my $cfg56 = Config::General->new();
|
||||||
|
eval {
|
||||||
|
$cfg56->save_file("t/56.out", { "new\nline" => 9, "brack<t" => 8 });
|
||||||
|
};
|
||||||
|
ok($@, "catch special chars in keys");
|
||||||
|
|
||||||
|
|
||||||
|
# UTF8[BOM] tests
|
||||||
|
my $cfg57 = "t/utf8_bom/foo.cfg";
|
||||||
|
my $expected57 = {foo => {"\x{e9}" => "\x{e8}", bar => {"\x{f4}" => "\x{ee}"}}};
|
||||||
|
|
||||||
|
for my $bool (0, 1) {
|
||||||
|
my $conf = Config::General->new(-ConfigFile => $cfg57,
|
||||||
|
-IncludeRelative => 1,
|
||||||
|
-UTF8 => $bool);
|
||||||
|
my %hash = $conf->getall;
|
||||||
|
is_deeply \%hash, $expected57, "-UTF8 => $bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
# IFDEF tests
|
||||||
|
my $cfg58 = "t/cfg.58";
|
||||||
|
my $expected58 = { level => "debug" };
|
||||||
|
my %defs = (
|
||||||
|
scalar => 'TEST',
|
||||||
|
array => ['TEST'],
|
||||||
|
hash => {'TEST' => 1}
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach my $def (keys %defs) {
|
||||||
|
my $conf = Config::General->new(-ConfigFile => $cfg58,
|
||||||
|
-UseApacheIfDefine => 1,
|
||||||
|
-Define => $defs{$def});
|
||||||
|
my %hash = $conf->getall();
|
||||||
|
is_deeply \%hash, $expected58, "UseApacheIfDefine, -Define => $def";
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
fruit = mango
|
||||||
|
sub1_seen = yup
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
sub1b_seen = yup
|
|
@ -0,0 +1 @@
|
||||||
|
test value
|
|
@ -0,0 +1 @@
|
||||||
|
test2 value2
|
|
@ -0,0 +1 @@
|
||||||
|
test3 value3
|
|
@ -0,0 +1,5 @@
|
||||||
|
fruit = pear
|
||||||
|
sub2_seen = yup
|
||||||
|
|
||||||
|
<<include ../cfg.sub1>>
|
||||||
|
<<include ../cfg.sub1b>>
|
|
@ -0,0 +1 @@
|
||||||
|
sub2b_seen = yup
|
|
@ -0,0 +1,5 @@
|
||||||
|
fruit = apple
|
||||||
|
sub3_seen = yup
|
||||||
|
|
||||||
|
<<include ../cfg.sub2>>
|
||||||
|
<<include ../cfg.sub2b>>
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Beispiel .redirect Datei.
|
||||||
|
*
|
||||||
|
* Wenn diese Datei nicht im $HOME des
|
||||||
|
* jeweiligen Benutzers vorhanden ist,
|
||||||
|
* oder wenn die vorhandene Datei aus
|
||||||
|
* irgendeinem Grund ungültig ist(Syntax)
|
||||||
|
* dann wird per Default alles an @domain
|
||||||
|
* zum Benutzer weitergeleitet.
|
||||||
|
*
|
||||||
|
* Syntax:
|
||||||
|
* Domain Blöcke beginnen mit <domain name> und enden
|
||||||
|
* mit </domain> (equivalent zu apache config).
|
||||||
|
* Als Kommentare sind # sowie C-Style erlaubt(so
|
||||||
|
* wie dieser hier).
|
||||||
|
* Näheres zum <domain ...> Block siehe unten.
|
||||||
|
*
|
||||||
|
* Im <var> Block kann man Variablen definieren, auf
|
||||||
|
* die man dann innerhalb der <domain...> Blöcke zu-
|
||||||
|
* greifen kann (siehe <var> sample!)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Im <list name> Block kann man Mailinglisten einrichten
|
||||||
|
* allerdings rudimentär, d.h. es sind eigentlich nur
|
||||||
|
* Verteiler, aber immerhin. Die entsprechende Adresse
|
||||||
|
* muss im dazugehörigen <domain..> Block definiert sein.
|
||||||
|
*
|
||||||
|
* Angegebene Emailadressen werden (zumindest im Moment)
|
||||||
|
* nicht überprüft, also 1:1 übernommen, also Sorgfalt
|
||||||
|
* walten lassen.
|
||||||
|
*
|
||||||
|
* Fragen/Kommentare/Kritik/Flames/Mecker an:
|
||||||
|
* Thomas Linden <tom@daemon.de>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*********************************************************************
|
||||||
|
* Hier kann man Variablen definieren und später mittels
|
||||||
|
* $variablenname verwenden.
|
||||||
|
*********************************************************************
|
||||||
|
*/
|
||||||
|
<var>
|
||||||
|
USER scip # via $USER verwendbar
|
||||||
|
</var>
|
||||||
|
|
||||||
|
host manna
|
||||||
|
host gorky
|
||||||
|
|
||||||
|
/*
|
||||||
|
*********************************************************************
|
||||||
|
* Für jede Domain muss ein <domain name> Block vorhanden sein
|
||||||
|
*********************************************************************
|
||||||
|
*/
|
||||||
|
<domain bar.de>
|
||||||
|
foo max@nasa.gov # foo@bar.de nach max@nasa.gov
|
||||||
|
|
||||||
|
coderz %coderz # coderz@bar.de ist ein Verteiler, der
|
||||||
|
# in <list coderz> definiert ist.
|
||||||
|
|
||||||
|
@ $USER # alles andere an "scip" schicken.
|
||||||
|
# Wenn nicht angegeben, kommen unbekannte
|
||||||
|
# Adressen an den Absender zurück, z.B.
|
||||||
|
# gibtsnet@bar.de würde "Unknown User" ver-
|
||||||
|
# ursachen!
|
||||||
|
</domain>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*********************************************************************
|
||||||
|
* Definition einer "Mailingliste", gültige Empfänger müssen mit
|
||||||
|
* dem Parameter "rcpt" definiert werden. <list> Blöcke sind Domain-
|
||||||
|
* unabhängig, d.h. sie müssen einen eindeutigen Namen haben.
|
||||||
|
*********************************************************************
|
||||||
|
*/
|
||||||
|
<list coderz>
|
||||||
|
rcpt solaar.designer@packetstorm.org
|
||||||
|
rcpt $USER
|
||||||
|
rcpt machine@star.wars.de
|
||||||
|
</list>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<bar>
|
||||||
|
ô = î
|
||||||
|
</bar>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<foo>
|
||||||
|
é = è
|
||||||
|
<<include bar.cfg>>
|
||||||
|
</foo>
|
Loading…
Reference in New Issue