#!perl # # inidiff - generate diffs between two .ini files # use strict; use Getopt::Std; use IO::File; use IO::Handle; use App::IniDiff::IniFile; my $prog = $0; $prog =~ s:.*\/::; my $Usage = "Usage: $prog [-q] [-V] [-i] [-M] [-c] [-o outfile] file1 file2 -q don't generate comments indicating new/old values -V Print version number and exit. -i ignore the case of comparisons. -M add ^M to output to support old (pre-NT) Windows/DOS systems -c strip trailing inline comments after semicolon -o file File to save result to (instead of -f file); \"-\" means write to stdout. Compare two .ini files and generate a differences file. "; # add support for --help and --version $Getopt::Std::STANDARD_HELP_VERSION = "true"; my $VERSION = '0.19'; sub VERSION_MESSAGE { print "$prog: version $VERSION\n"; } sub HELP_MESSAGE { print STDERR $Usage; } my %opt; if (!&getopts('o:qViMc', \%opt)) { print STDERR $Usage; exit 1; } my $outFile = defined $opt{'o'} ? $opt{'o'} : '-'; if (defined $opt{'V'}) { print "$prog: version $VERSION\n"; exit 0; } my $genComments = defined $opt{'q'} ? 0 : 1; my $ignoreCase = defined $opt{'i'} ? 1 : 0; my $addM = defined $opt{'M'} ? 1 : 0; my $stripComments = defined $opt{'c'} ? 1 : 0; if (@ARGV < 2) { print STDERR "$prog: wrong number of arguments\n"; die $Usage; } my $file1 = $ARGV[0]; my $file2 = $ARGV[1]; if ($file1 eq '-' && $file2 eq '-') { die "$prog: both files can't be from stdin!\n"; } my $f1 = new IO::File $file1, "r"; if (!defined $f1) { die "$prog: can't open $file1 - $!\n"; } my $f2 = new IO::File $file2, "r"; if (!defined $f2) { die "$prog: can't open $file2 - $!\n"; } my $f1Ini = new App::IniDiff::IniFile($f1, 0, $addM, $stripComments); die "$prog: $file1:$App::IniDiff::IniFile::errorString\n" if (!defined $f1Ini); my $f2Ini = new App::IniDiff::IniFile($f2, 0, $addM, $stripComments); die "$prog: $file1:$App::IniDiff::IniFile::errorString\n" if (!defined $f2Ini); # # Generate the diffs... # # using default () would not allow ^M and stripcomments to be passed in my $dIni = new App::IniDiff::IniFile("", 0, $addM, $stripComments); my %doneKeys = (); my $key1; foreach $key1 (@{$f1Ini->keys}) { my $key2 = $f2Ini->findKey($key1->name); my %doneFields = (); if (defined $key2) { my $keyd = undef; $doneKeys{$key2->name} = $key1->orderId; my $field1; my $field2; foreach $field1 (@{$key1->fields}) { # returns canonicalized (lc) name $field2 = $key2->findField($field1->name); if (defined $field2) { $doneFields{$field2->name} = 1; # if case should be ignored (-i opt) and # they are equivalent otherwise # then do not find them unequal if (($field1->value ne $field2->value) && !(($ignoreCase) and (lc($field1->value) eq lc($field2->value)))) { $keyd = $dIni->addKey(new App::IniDiff::IniFile::Key($key2->name, 0, undef)) if !defined $keyd; # Changed $keyd->addField( new App::IniDiff::IniFile::Field( $field2->name, $field2->value, 0, &comment("Old value: " . $field1->value))); } } else { $keyd = $dIni->addKey(new App::IniDiff::IniFile::Key($key2->name, 0, undef)) if !defined $keyd; # Old - not in new version $keyd->addField( new App::IniDiff::IniFile::Field( $field1->name, $field1->value, 1, &comment("Old value: " . $field1->value))); } } foreach $field2 (@{$key2->fields}) { if (!exists $doneFields{$field2->name}) { $keyd = $dIni->addKey(new App::IniDiff::IniFile::Key($key2->name, 0, undef)) if !defined $keyd; # New addition $keyd->addField( new App::IniDiff::IniFile::Field( $field2->name, $field2->value, 0, &comment('New'))); } } } else { # Whole key is deleted $dIni->addKey(new App::IniDiff::IniFile::Key($key1->name, 1, undef)); } } my $key2; my $lastOrderId = -1; foreach $key2 (@{$f2Ini->keys}) { if (!exists $doneKeys{$key2->name}) { # New key (and contents) my $keyd = $dIni->addKey(new App::IniDiff::IniFile::Key($key2->name, 0, &comment('New'))); # Attempt to the preserve order in the file... $keyd->orderId($lastOrderId += 0.000001); my $field2; foreach $field2 (@{$key2->fields}) { $keyd->addField( new App::IniDiff::IniFile::Field($field2->name, $field2->value, 0, undef)); } } else { $lastOrderId = $doneKeys{$key2->name}; } } my $out; if ($outFile eq '-') { $out = new IO::Handle; if (!$out->fdopen("STDOUT", "w")) { die "$prog: can't fdopen STDOUT - $!\n"; } } else { $out = new IO::File $outFile, 'w'; if (!defined $out) { die "$prog: couldn't open $outFile for writing - $!\n"; } } $dIni->write($out); if (!$out->close) { die "$prog: error writing to STDOUT - $!\n"; } exit 0; sub comment { return $genComments ? $_[0] : undef; } __END__ =head1 NAME inidiff - generate the differences between two C<.ini> files =head1 SYNOPSYS B [B<-q>] [B<-V>] [B<-i>] [B<-M>] [B<-c>] [B<-o outfile>] file1 file2 =head1 DESCRIPTION B reads the two specified C<.ini> files and prints the differences between the two. The output is suitable for use with the B program. Either of file1 or file2 may be "-", indicating standard input should be read. The output consists of key names in brackets followed by the fields that have been added or changed in that key. Keys are separated by blank lines. Deleted keys are indicated by a minus at the end of the line containing the key name, while deleted fields are indicated by a minus after the field name (there is no equals or value after the field name). New keys and fields are preceded by comments indicating they are new. Changed fields are preceded by comments indicating the previous value of the field. All output lines end in a carriage return. See L for information about the format of C<.ini> files and C<.ini> patch files. B: The following naming convention is used here: [key] entry=value Whereas in other documentation of INI files the naming convention is: [section] key=value =head1 OPTIONS =over 4 =item B<-q> Suppresses the generation of comments indicating new keys and fields, and previous values of existing and deleted fields. =item B<-V> Prints the version number - the program then exits immediately. =item B<-i> Ignores case in values for comparison purposes (always does so for key names anyway). =item B<-M> Add ^M to output to support old (pre-NT) Windows/DOS systems. =item B<-c> Strip trailing inline comments after semicolon. =item B<-o> I Allows an output file to be specified. "-" means write to stdout. =back =head1 KNOWN BUGS The only (known) problem with B (and patch files) is that they don't handle changes to keys which have multiple fields with the same name. This kind of thing doesn't seem too common, so it hasn't been a problem. =head1 SEE ALSO L, L, L. =head1 AUTHOR Michael Rendell, Memorial University of Newfoundland =head1 MAINTAINERS Jeremy Squires =head1 SOURCE =over 4 =item * The source for this package is available here: L =back =head1 ACKNOWLEDGEMENTS Michael Rendell, Memorial University of Newfoundland produced the first version of the Regutils package from which this package was derived. =over 4 =item * It is still available from: L =back =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc App::IniDiff::IniFile You can also look for information at: =over 4 =item * RT: CPAN's request tracker (report bugs here) L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 LICENSE AND COPYRIGHT This software is Copyright (c) 1998 Memorial University of Newfoundland This is free software, licensed under: The GNU General Public License, Version 3, July 2007 See F