## Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).# All rights reserved.# This component and the accompanying materials are made available# under the terms of the License "Eclipse Public License v1.0"# which accompanies this distribution, and is available# at the URL "http://www.eclipse.org/legal/epl-v10.html".## Initial Contributors:# Nokia Corporation - initial contribution.## Contributors:## Description: # This module implements the MD5 version of the Evalid's compare#package EvalidMD5;use strict;use Carp;use File::Find;use IO::File;use Sys::Hostname;use FindBin;use lib "$FindBin::Bin";require Exporter;use File::Basename;use vars qw($VERSION );$VERSION = 0.02;use EvalidCompare;# Public# MD5Generate## Inputs# $iLeftDir - Left side directory or File containing listing of left side# $iResultsFile - Name of file to write the list of files and MD5 checksum to# $iExclude - Reference to array of regular expression patterns to exclude# $iInclude - Reference to array of regular expression patterns to include# $iListFile - Filename to read for list of files from [Optional]# $iDumpDir - Directory to generate dump of comparison data [Optional]## Outputs## Description# This function generates the MD5 checksum filesub MD5Generate{ my ($iLeftDir, $iResultsFile, $iExclude, $iInclude, $iListFile, $iDumpDir) = @_; my (@iLeftFiles); # This should have been check before getting here but to be on the safe side # check again, as it should never overwrite or append to the file croak "$iResultsFile already exists" if (-e $iResultsFile); croak "$iListFile does exist" if ((defined($iListFile) && (!-e $iListFile))); if (-d $iLeftDir){ # Make sure all the \ are / for the RegEx $iLeftDir =~ s/\\/\//g; # Make sure it does not have a / on the end $iLeftDir =~ s/\/$//; # If $iFileList is defined then use the filelist to generate @iLeftFiles if (defined($iListFile)) { # Generate list of files @iLeftFiles = &FilterDir($iLeftDir,$iExclude,$iInclude, $iListFile); } else { # Generate the the directory listing @iLeftFiles = &FilterDir($iLeftDir,$iExclude,$iInclude); } } else { croak "$iLeftDir is not a directory"; } # Open the results file for writing my ($fResultsFile) = new IO::File; croak "Cannot for $iResultsFile for writing" unless ($fResultsFile->open("> $iResultsFile")); # Write headers to the file print $fResultsFile "Host:".&hostname()."\n"; print $fResultsFile "Username:".$ENV{'USERNAME'}."\n"; print $fResultsFile "Date-Time:".scalar(localtime)."\n"; print $fResultsFile "Version:".$VERSION."\n"; print $fResultsFile "Directory:".$iLeftDir."\n"; print $fResultsFile "FileList:".$iListFile."\n" if (defined($iListFile)); print $fResultsFile "Exclusion(s):"; foreach my $iExc (sort @$iExclude) { print $fResultsFile "$iExc "; } print $fResultsFile "\n"; print $fResultsFile "Inclusion(s):"; foreach my $iInc (@$iInclude) { print $fResultsFile "$iInc "; } print $fResultsFile "\n"; print $fResultsFile "----------------\n"; # Write out sorted list of files with MD5 Checksums foreach my $iFile (sort @iLeftFiles) { my ($MD5, $type) = &EvalidCompare::GenerateSignature($iLeftDir."/".$iFile, $iDumpDir); print $fResultsFile $iFile." TYPE=".$type." MD5=".$MD5."\n"; } $fResultsFile->close;}# MD5Compare## Inputs# $iLeftFile - Left side File containing listing and MD5 of left side# $iRightFile - Right side File containing listing and MD5 of right side# $iVerbose - Verbose Flag# $iLog - Logfile name## Outputs# %iCommon - hash relative filenames with values of file type that are in both directories and Compare results/types# %iDiff - hash relative filenames with values of file type and directory side infomation## Descriptionsub MD5Compare{ my ($iLeftFile) = shift; my ($iRightFile) = shift; my ($iVerbose) = defined($_[0]) ? shift : 0; my ($iLog) = defined($_[0]) ? shift : *STDOUT; my (%iCommon, %iDiff); my (%iLeftFiles, %iRightFiles); my (%iLeftHeaders, %iRightHeaders); # Backup check the files are available to read. croak "$iLeftFile is not a file" unless (-f $iLeftFile); croak "$iRightFile is not a file" unless(-f $iRightFile); #Read the files &ReadMD5File($iLeftFile, \%iLeftFiles, \%iLeftHeaders); &ReadMD5File($iRightFile, \%iRightFiles, \%iRightHeaders); # Check Critical headers foreach my $iHeader (sort keys %iLeftHeaders) { # Warning is certain headers are not identical if ($iHeader =~ /^Version|Exclusion\(s\)|Inclusion\(s\)/) { print $iLog "WARNING:$iHeader is different\n" if ($iLeftHeaders{$iHeader} ne $iRightHeaders{$iHeader}); } } # A Hash is used to combine the two directory listing using the filename as the key my %iCombinedFiles; # Enter the files from the left side listing and check the right side critical header are the same # Note all filenames are turned to lower case as this is designed to only wotk on Windows foreach my $iFile (sort keys %iLeftFiles) { $iCombinedFiles{$iFile} = "Left"; } # Enter the files from the right side listing # Note all filenames are turned to lower case as this is designed to only wotk on Windows foreach my $iFile (sort keys %iRightFiles) { # Check to see if any entry for this file exists on the left side if ((defined ($iCombinedFiles{$iFile})) && ( $iCombinedFiles{$iFile} eq "Left")) { # Yes, so add to the Common set # Check if the MD5 checksum matches # The [0] element is the file type if ($iLeftFiles{$iFile}[0] ne $iRightFiles{$iFile}[0]) { $iCommon{$iFile} = [$iLeftFiles{$iFile}[0]." to ".$iRightFiles{$iFile}[0], "Type Changed"]; } elsif ($iLeftFiles{$iFile}[1] eq $iRightFiles{$iFile}[1]) { $iCommon{$iFile} = [$iLeftFiles{$iFile}[0],"OK"]; } else { $iCommon{$iFile} = [$iLeftFiles{$iFile}[0],"Different"]; } # The filename key is not needed any more as both sides have been processed, so delete the hash entry delete $iCombinedFiles{$iFile}; } elsif (!defined($iCombinedFiles{$iFile})) { # No, the key is not defined, so this filename is only in the right side $iDiff{$iFile} = [$iRightFiles{$iFile}[0],"Right"]; # The filename key is not needed any more as both sides have been processed, so delete the hash entry delete $iCombinedFiles{$iFile}; } } # Add the files left in the hash to the Left side only list foreach my $iFile (sort keys %iCombinedFiles) { $iDiff{$iFile} = [$iLeftFiles{$iFile}[0],"Left"]; } # Return References to the Arrays return (\%iCommon, \%iLeftHeaders, \%iRightHeaders, \%iDiff);}# FilterDir## Inputs# $iDir - Directory to process# $iExclude - Reference to array of regular expression patterns to exclude# $iInclude - Reference to array of regular expression patterns to include# $iListFile - Filename to read for list of files from [Optional]## Outputs# @iFinalFileList - Filtered list relative filenames## Description# This function produces a filtered list of filenames in the specified directory or from filesub FilterDir{ my ($iDir,$iExclude,$iInclude, $iListFile) = @_; my (@iFileList, @iFinalFileList, $iFileName); if(defined($iListFile)) { open LIST, "$iListFile" or croak "Cannot open $iListFile\n"; while(<LIST>) { next if /^\s+$/; # skip blank lines my $iFileName = $iDir."/".$_; chomp $iFileName; # Remove new line if(-e $iFileName) { # The listed file exists add to the @iFileList push @iFileList, $iFileName; } else { print "Warning: Cannot find $iFileName\n"; } } close LIST; } else { # Produce full filelist listing without directory names find sub { push @iFileList, $File::Find::name if (!-d) ;}, $iDir; } foreach my $iFile ( @iFileList) { my $iExcludeFile = 0; # Remove the specified directory path from the front of the filename $iFile =~ s#^$iDir/##; # Process all Exclude RegEx to see if this file matches foreach my $iExcludeRegEx (@$iExclude) { if ($iFile =~ /$iExcludeRegEx/i) { # Mark this file to be excluded from the final list $iExcludeFile = 1; } } # Process all Include RegEx to see if this file matches foreach my $iIncludeRegEx (@$iInclude) { if ($iFile =~ /$iIncludeRegEx/i) { # Mark this file to be Included in the final list $iExcludeFile = 0; } } # Added the file to the final list based on the flag push @iFinalFileList, lc($iFile) unless $iExcludeFile; } return @iFinalFileList;}# MD5ComparePrint## Inputs# $iCommon - Reference to Hash of common file names and the result the comparision# $iLeftHeaders - Reference to Hash contain the left side# $iRightHeaders - Reference to Hash contain the right side# $iDiff - Reference to Hash of relative filenames with values of file type and left/right directory side# $iLog - Logfile name## Outputs## Description# This function prints the output of a Comparesub MD5ComparePrint{ my ($iCommon) = shift; my ($iLeftHeaders) = shift; my ($iRightHeaders) = shift; my ($iDiff) = shift; my ($iLog) = shift; my ($iFailed) = 0; # Count of the failed comparisions my ($iPassed) = 0; # Count of the Passed comparisions my ($iLeft) = 0; # Count of the Passed comparisions my ($iRight) = 0; # Count of the Passed comparisions my ($sec, $min, $hour, $mday, $mon, $year) = localtime(time); printf $iLog "\n----------------\n%02d:%02d %02d/%02d/%04d\n", $hour, $min, $mday, $mon+1, $year+1900; print $iLog "evalid\nLeft Side=$ARGV[0]\nRight Side=$ARGV[1]\n"; print $iLog "\nLeft side information\n"; foreach my $iHeader (sort keys %$iLeftHeaders) { print $iLog $iHeader.":".$$iLeftHeaders{$iHeader}."\n"; } print $iLog "\nRight side information\n"; foreach my $iHeader (sort keys %$iRightHeaders) { print $iLog $iHeader.":".$$iRightHeaders{$iHeader}."\n"; } print $iLog "\n"; foreach my $iFile (sort keys %$iCommon) { if ($$iCommon{$iFile}[1] eq "OK") { print $iLog "Passed:$iFile (".$$iCommon{$iFile}[0].")\n"; $iPassed++; } } print $iLog "\n"; foreach my $iFile (sort keys %$iCommon) { if ($$iCommon{$iFile}[1] eq "Type Changed") { print $iLog "Type Changed:$iFile (".$$iCommon{$iFile}[0].")\n"; $iFailed++; } } print $iLog "\n"; foreach my $iFile (sort keys %$iCommon) { if ($$iCommon{$iFile}[1] eq "Different") { print $iLog "Failed:$iFile (".$$iCommon{$iFile}[0].")\n"; $iFailed++; } } print $iLog "\n"; foreach my $iFile (sort keys %$iDiff) { if ($$iDiff{$iFile}[1] eq "Left") { print $iLog "Missing Left:$iFile (".$$iDiff{$iFile}[0].")\n"; $iLeft++; } } print $iLog "\n"; foreach my $iFile (sort keys %$iDiff) { if ($$iDiff{$iFile}[1] eq "Right") { print $iLog "Missing Right:$iFile (".$$iDiff{$iFile}[0].")\n"; $iRight++; } } print $iLog "\n\nSummary\n"; print $iLog "Total files processed=".($iPassed+$iFailed+$iRight+$iLeft)."\n"; print $iLog "Files Passed=$iPassed\n"; print $iLog "Files Failed=$iFailed\n"; print $iLog "Missing Files in Left=".$iRight."\n"; print $iLog "Missing Files in Right=".$iLeft."\n"; return ($iFailed);}# NameOnly# # Inputs# $filename - may contain path and file extension## Outputs# $nameOnly - filename without extension and path## Description# This routine is used to extract the name of the file# only from a filename that may include a path and file extension# sub NameOnly{ my ($filename) = @_; my $nameOnly = basename($filename); $nameOnly =~ s/(\w+.*)\.\w+$/$1/; return $nameOnly;}# MD5CompareZipDel## Inputs# $iCommon - Reference to Hash of common file names and the result the comparision# $iDiff - Reference to Hash of relative filenames with values of file type and left/right directory side# $iLeft - filename of Left side directory# $iRight - filename of Right side directory## Outputs## Description# This function prints the output of a Compare results in a format ready for creating the a# Zip and batch file to upgrade the left side to the equivalent of the right side.sub MD5CompareZipDel{ my ($iCommon) = shift; my ($iDiff) = shift; my ($iLeft) = shift; my ($iRight) = shift; # Build a suitable name for outputfiles based on input filenames $iLeft = NameOnly($iLeft); $iRight = NameOnly($iRight); my ($iBasename) = $iLeft."_to_".$iRight; open DELLIST, ">del_$iBasename.bat"; open ZIPLIST, ">zip_$iBasename.log"; open ZIPBAT, ">zip_$iBasename.bat"; foreach my $iFile (sort keys %$iCommon) { if (($$iCommon{$iFile}[1] eq "Different") || ($$iCommon{$iFile}[1] eq "Type Changed")) { print ZIPLIST "$iFile\n"; } } foreach my $iFile (sort keys %$iDiff) { if ($$iDiff{$iFile}[1] eq "Right") { print ZIPLIST "$iFile\n"; } else { # DEL needs the / to be \ $iFile =~ s/\//\\/g; print DELLIST "del /F $iFile\n"; } } print ZIPBAT "zip $iBasename.zip -@<zip_$iBasename.log\n"; close DELLIST; close ZIPLIST; close ZIPBAT;}# Private# ReadMD5File## Inputs# $iResultsFile - Results filename to process# $iResults - Reference to Results Hash# $iHeaders - Reference to Headers Hash## Outputs## Description# This function reads the conntent of the MD5 File in to a Hashsub ReadMD5File{ my ($iResultsFile, $iResults, $iHeaders) = @_; # Open the results file for reading open (INPUT, "$iResultsFile") or croak "Cannot for $iResultsFile for reading"; while (<INPUT>) { if (/^(\S+?):(.*)/) { $$iHeaders{$1} = $2; } elsif (/^(\S+?)\sTYPE=(.*?)\sMD5=(.*)/){ $$iResults{$1} = [$2,$3]; } } close INPUT;}1;