Prima
view release on metacpan or search on metacpan
Prima/IniFile.pm view on Meta::CPAN
sub create
{
my $class = shift;
my %profile;
%profile = @_ if scalar(@_)%2==0;
%profile = (file => shift) if scalar(@_)%2==1;
%profile = (%profile, @_) if scalar(@_)%2==0;
my $self = {};
bless( $self, $class);
$self-> clean;
$self-> {fileName} = $profile{-file} if exists $profile{-file};
$self-> {fileName} = $profile{file} if exists $profile{file};
$self-> read($self-> {fileName}, %profile) if exists $self-> {fileName};
return $self;
}
sub DESTROY
{
my $self = shift;
$self-> write;
}
sub canonicalize_fname
{
my $p = shift;
return Cwd::abs_path($p) if -d $p;
my $dir = $p;
my $fn;
if ($dir =~ s{[/\\]([^\\/]+)$}{}) {
$fn = $1;
} else {
$fn = $p;
$dir = '.';
}
unless ( scalar( stat $dir)) {
$dir = "";
} else {
$dir = eval { Cwd::abs_path($dir) };
$dir = "." if $@;
$dir = "" unless -d $dir;
$dir =~ s/(\\|\/)$//;
}
return "$dir/$fn";
}
sub read
{
my ($self, $fname, %profile) = @_;
$self-> write; # save the old contents
$self-> clean;
$self-> {fileName} = canonicalize_fname($fname);
eval
{
my $f;
open $f, "<", $fname or do
{
open $f, ">", $fname or die "Cannot create $fname: $!\n";
close $f;
open $f, "<", $fname or die "Cannot open $fname: $!\n";
};
binmode $f, ":utf8";
my @chunks;
my %sectionChunks = ('' => [0]);
my %sectionItems = ('' => []);
my $currentChunk = [];
my $items = {};
my $chunkNum = 0;
my $line = 0;
push @chunks, $currentChunk;
push @{$sectionItems{''}}, $items;
while (<$f>)
{
chomp;
if ( /^\s*\[(.*?)\]/) # new section?
{
my $section = $1;
$currentChunk = [];
$items = {};
push @chunks, $currentChunk;
$chunkNum++;
$line = 0;
if ( exists $sectionChunks{$section})
{
push @{$sectionChunks{$section}}, $chunkNum;
push @{$sectionItems{$section}}, $items;
}
else
{
$sectionChunks{$section} = [$chunkNum];
$sectionItems{$section} = [$items];
}
next;
}
next if /^\s*[;#]/; # comment
next unless /^\s*(.*?)\s*=/;
# another value found
my $item = $1;
if ( exists $items-> {$item})
{
# duplicate
push @{$items-> {$item}}, $line;
}
else
{
# first such $item in this portion of the $section
$items-> {$item} = [$line];
}
}
continue
{
push( @$currentChunk, $_);
$line++;
}
close $f;
push( @{$chunks[-1]}, '') if scalar(@{$chunks[-1]}) && $chunks[-1]-> [-1] !~ /^\s*$/;
$self-> {chunks} = [@chunks];
$self-> {sectionChunks} = {%sectionChunks};
$self-> {sectionItems} = {%sectionItems};
# default values
my $def;
Prima/IniFile.pm view on Meta::CPAN
$self-> {ini}-> replace_values($self-> {section}, $item, ref($val) eq q/ARRAY/ ? @$val : ($val));
}
sub DELETE
{
my ($self, $item) = @_;
$self-> {ini}-> replace_values($self-> {section}, $item);
}
sub CLEAR # Well, dangerous
{
my $self = $_[0];
my @items = $self-> {ini}-> items($self-> {section});
for (@items)
{
$self-> {ini}-> replace_values($self-> {section}, $_);
}
}
sub EXISTS
{
my ($self, $item) = @_;
return $self-> {ini}-> nvalues($self-> {section},$item) > 0;
}
sub FIRSTKEY
{
my $self = $_[0];
$self-> {iterator} = [$self-> {ini}-> items($self-> {section})];
return $self-> NEXTKEY;
}
sub NEXTKEY
{
my $self = $_[0];
unless ( exists $self-> {iterator} && scalar @{$self-> {iterator}})
{
return wantarray ? () : undef;
}
my $key = shift @{$self-> {iterator}};
return wantarray ? ($key, $self-> FETCH($key)) : $key;
}
package Prima::IniFile;
sub section
{
my %tied;
tie %tied, q/Prima::IniFile::Section::Helper::to::Tie/, $_[0], $_[1];
return \%tied;
}
sub write
{
my $self = $_[0];
return unless defined($self-> {fileName}) && $self-> {changed};
my $fname = $self-> {fileName};
eval {
my $f;
open $f, ">", $fname or die "Cannot write to the $fname: $!\n";
binmode $f, ":utf8";
pop @{$self-> {chunks}-> [-1]} if scalar(@{$self-> {chunks}-> [-1]}) && $self-> {chunks}-> [-1]-> [-1] =~ /^\s*$/;
for ( @{$self-> {chunks}})
{
for (@$_) { print $f "$_\n" }
}
push( @{$self-> {chunks}-> [-1]}, '') if scalar(@{$self-> {chunks}-> [-1]}) && $self-> {chunks}-> [-1]-> [-1] !~ /^\s*$/;
close $f;
};
$self-> {changed} = undef if $@;
warn($@) if $@;
}
1;
=pod
=head1 NAME
Prima::IniFile - support of Windows-like initialization files
=head1 DESCRIPTION
The module contains a class, that provides mapping of text initialization file to
a two-level hash structure. The first level
is called sections, which groups the second level hashes, called items.
Sections must have unique keys. The items hashes values are arrays of
text strings. The methods, operated on these arrays are L<get_values>,
L<set_values>, L<add_values> and L<replace_values>.
=head1 SYNOPSIS
use Prima::IniFile;
my $ini = create Prima::IniFile;
my $ini = create Prima::IniFile FILENAME;
my $ini = create Prima::IniFile FILENAME,
default => HASHREF_OR_ARRAYREF;
my $ini = create Prima::IniFile file => FILENAME,
default => HASHREF_OR_ARRAYREF;
my @sections = $ini->sections;
my @items = $ini->items(SECTION);
my @items = $ini->items(SECTION, 1);
my @items = $ini->items(SECTION, all => 1);
my $value = $ini-> get_values(SECTION, ITEM);
my @vals = $ini-> get_values(SECTION, ITEM);
my $nvals = $ini-> nvalues(SECTION, ITEM);
$ini-> set_values(SECTION, ITEM, LIST);
$ini-> add_values(SECTION, ITEM, LIST);
$ini-> replace_values(SECTION, ITEM, LIST);
$ini-> write;
$ini-> clean;
$ini-> read( FILENAME);
$ini-> read( FILENAME, default => HASHREF_OR_ARRAYREF);
my $sec = $ini->section(SECTION);
$sec->{ITEM} = VALUE;
( run in 0.226 second using v1.01-cache-2.11-cpan-0f795438458 )