App-IniDiff-IniFile
view release on metacpan or search on metacpan
lib/App/IniDiff/IniFile.pm view on Meta::CPAN
=item * package App::IniDiff::IniFile::Field;
=item * package App::IniDiff::IniFile::Key;
=item * package App::IniDiff::IniFile::Filter;
=back
Creates the following data structure:
{
'nextOrderId' => some-number,
'keys' => {
{
'name' => "...",
'orderId' => some number,
'fields' => [
{
'name' => "..."
'value' => "...",
'deleted' => 0 or 1,
'annotation' => "...",
},
...
],
'deleted' => 0 or 1,
'annotation' => "...",
},
...
},
}
=over 4
=item * 'orderId' is used to preserve the order in which keys appear in a file.
=item * 'annotation' is used to decorate inidiff output.
=item * 'deleted' is used when generating, writing, reading, and applying patch files.
=back
Some terms used differ from what is normally used in WinINI file-speak
=over 4
=item * 'key' is INI 'section'
=item * 'field' is INI 'entry'
=item * 'field name' is INI 'key'
=item * 'field value' is INI 'value'
=back
=cut
use vars qw(&new &write &findKey &keys &removekey &addKey);
# added eol to support old end of line \r\n
use vars qw($errorString $eol $commentchars);
{
package App::IniDiff::IniFile::Field;
use strict;
use Carp;
use vars qw(&new &name &canonName &value &deleted &annotation
&setFrom &write &canonicalize );
use vars qw($eol);
# end of line character - eol
$eol = "\n";
sub new
{
my $proto = shift;
confess "new App::IniDiff::IniFile::Field takes 4 arguments" if @_ != 4;
my ($name, $value, $del, $annotation) = @_;
my $field = {
'name' => $name,
'canonName' => canonicalize($name),
'value' => $value,
'deleted' => $del,
'annotation' => $annotation,
};
bless $field, (ref($proto) || $proto);
return $field;
}
sub name {
my $field = shift;
if (@_) { $field->{'name'} = shift }
return $field->{'name'};
}
sub canonName {
my $field = shift;
if (@_) { $field->{'canonName'} = shift }
return $field->{'canonName'};
}
sub value {
my $field = shift;
if (@_) { $field->{'value'} = shift }
return $field->{'value'};
}
sub deleted {
my $field = shift;
if (@_) { $field->{'deleted'} = shift }
return $field->{'deleted'};
}
sub annotation {
my $field = shift;
if (@_) { $field->{'annotation'} = shift }
return $field->{'annotation'};
}
sub setFrom
{
my $field = shift;
my $from = shift;
$field->name($from->name);
$field->value($from->value);
$field->deleted($from->deleted);
$field->annotation($from->annotation);
return $field;
}
sub canonicalize
{
# Called as object method
if (@_ > 0 && ref $_[0]) {
return ${$_[0]}->canonName if (@_ == 1);
}
# Called as object or class method with argument
if (@_ == 2) {
shift;
}
elsif (@_ != 1) {
confess "wrong number of args" if (@_ != 0);
}
my $name = $_[0];
$name =~ tr/A-Z/a-z/;
return $name;
}
sub write
{
my ($field, $fileHandle) = @_;
print $fileHandle "; ", $field->annotation, $eol
if defined $field->annotation;
print $fileHandle $field->name;
if ($field->deleted) {
print $fileHandle "-";
}
elsif (defined $field->value) {
print $fileHandle "=", $field->value;
}
print $fileHandle $eol;
}
} # End package App::IniDiff::IniFile::Field;
{
package App::IniDiff::IniFile::Key;
use strict;
use Carp;
use IO::File;
use vars qw(&new &name &canonName &orderId &deleted &annotation &fields
&canonicalize &findField &addField &appendField &removeField
&write
);
use vars qw($eol);
# end of line character - eol
$eol = "\n";
sub new
{
my $proto = shift;
confess "new App::IniDiff::IniFile::Key takes 3 arguments" if @_ != 3;
my ($name, $del, $annotation) = @_;
my $key = {
'name' => $name,
'canonName' => canonicalize($name),
'orderId' => undef,
'deleted' => $del,
'annotation' => $annotation,
'fields' => [],
};
bless $key, (ref($proto) || $proto);
return $key;
}
sub name {
my $key = shift;
# Do not change canonName (see kludge in App::IniDiff::IniFile::new regarding patches)
if (@_) { $key->{'name'} = shift }
return $key->{'name'};
}
sub canonName {
my $key = shift;
if (@_) { $key->{'canonName'} = shift }
return $key->{'canonName'};
}
sub orderId {
my $key = shift;
if (@_) { $key->{'orderId'} = shift }
return $key->{'orderId'};
}
sub deleted {
my $key = shift;
if (@_) { $key->{'deleted'} = shift }
return $key->{'deleted'};
}
sub annotation {
my $key = shift;
if (@_) { $key->{'annotation'} = shift }
return $key->{'annotation'};
}
sub fields {
my $key = shift;
confess "too many args" if @_ > 0;
return $key->{'fields'};
}
sub canonicalize
{
lib/App/IniDiff/IniFile.pm view on Meta::CPAN
sub addField
{
my $key = shift;
my $field = shift;
my $xfield = $key->findField($field->name);
if (defined $xfield) {
$xfield->setFrom($field);
return $xfield;
}
return $key->appendField($field);
}
sub appendField
{
my $key = shift;
my $field = shift;
push(@{$key->fields}, $field);
return $field;
}
#
# Remove a field, either by name or by reference
#
sub removeField
{
my $found = 0;
my ($key, $arg) = @_;
if (ref $arg) {
my $fieldToast = $arg;
for (my $i = 0 ; $i < @{$key->fields} ; $i++) {
my $field = ${$key->fields}[$i];
if ($field eq $fieldToast) {
splice(@{$key->fields}, $i, 1);
$i--;
$found++;
}
}
}
else {
my $fieldName = App::IniDiff::IniFile::Field->canonicalize($arg);
for (my $i = 0 ; $i < @{$key->fields} ; $i++) {
my $field = ${$key->fields}[$i];
if ($field->canonName eq $fieldName) {
splice(@{$key->fields}, $i, 1);
$i--;
$found++;
}
}
}
return $found;
}
sub write
{
my ($key, $fileHandle) = @_;
my ($del) = $key->deleted ? '-' : '';
print $fileHandle "; ", $key->annotation, $eol
if defined $key->annotation;
print $fileHandle "[", $key->name, "]", $del, $eol;
if (!$key->deleted) {
my $field;
foreach $field (@{$key->fields}) {
$field->write($fileHandle);
}
}
print $fileHandle $eol;
}
} # End package App::IniDiff::IniFile::Key;
# $IniFile package Globals
$errorString = undef;
# end of line character - eol
$eol = "\n";
$commentchars = ';#'; # Allow DOS and Unix style comment.
sub new
{
my $proto = shift;
my $ini = {
# Used to generate monotonically increasing key ids - used
# to reserve order of ini file.
'nextOrderId' => 0,
'keys' => {},
};
bless $ini, (ref($proto) || $proto);
return $ini if (@_ == 0);
confess "new called with too many arguments" if (@_ > 4);
my ($fileHandle, $isPatch, $addM, $stripComments) =
@_; # Patches: allow duplicate key names
my ($key);
my $ok = 1;
$isPatch = 0 if !defined $isPatch;
$addM = 0 if !defined $addM; # add ^M if pre-NT
$stripComments = 0 if ! defined $stripComments;
# strip out trailing inline comments having semicolon
# comment out stripComments if isPatch
# - this may be a problem
# - leave them in unless specifically asked
# $stripComments = 1 if $isPatch; # always strip out comments from patches
if ($addM == 1) {
$eol = "\r\n";
# set the children's eol members to this value as well ...
# not using set() methods - oh dear
$App::IniDiff::IniFile::Key::eol = "\r\n";
$App::IniDiff::IniFile::Field::eol = "\r\n";
}
while (<$fileHandle>) {
chomp;
#
# Strip comments - not in key names ([...]) and not in strings..
#
# if a [key] section
if (/^\s*(\[[^]]+])(.*)$/) {
my ($key, $rest) = ($1, $2);
# do care if it strips comments after [key] section
if ($stripComments) {
$rest =~ s/[$commentchars].*//;
}
$_ = $key . $rest;
}
# not a comment starting with ; and has a " in it
elsif (!/^\s*[$commentchars]/ && /"/) {
# Slow, but perl won't go exponential...
my $line = '';
# walk through non comments and matched quoted strings
# from left to right
# until we hit a comment character or a non matched quote
while (/^([^"$commentchars]*"[^"]*")(.*)/) {
$line .= $1;
$_ = $2;
}
# Some (burnt) ini files have unmatched quotes... rather
# than toast these, we assume they have no comments.
if (!/"/) {
# do not strip comments unless requested
if ($stripComments) {
s/[$commentchars].*//;
}
}
$_ = $line . $_;
}
elsif ($stripComments) {
s/[$commentchars].*//;
}
s/\s*$//; # remove trailing space includes \r...
# not skipping blank lines at this point results in errors when diffing
next if /^$/; # skip blank lines
if (/^\s*\[([^]]+)](-?)$/) {
my ($name, $del) = ($1, $2);
if ($isPatch) {
# Patches are a bit strange as there can be duplicate
# key names - to deal with this, the canonName (hash index)
# is a generated (unique) thing and we fix up the real name
# after the key is created.
$key = $ini->addKey(
new App::IniDiff::IniFile::Key(
( run in 0.693 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )