|
1 # |
|
2 # Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of the License "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # This module implements the MD5 version of the Evalid's compare |
|
16 # |
|
17 |
|
18 package EvalidMD5; |
|
19 |
|
20 use strict; |
|
21 use Carp; |
|
22 use File::Find; |
|
23 use IO::File; |
|
24 use Sys::Hostname; |
|
25 |
|
26 use FindBin; |
|
27 use lib "$FindBin::Bin"; |
|
28 require Exporter; |
|
29 use File::Basename; |
|
30 |
|
31 use vars qw($VERSION ); |
|
32 $VERSION = 0.02; |
|
33 |
|
34 use EvalidCompare; |
|
35 |
|
36 # Public |
|
37 |
|
38 # MD5Generate |
|
39 # |
|
40 # Inputs |
|
41 # $iLeftDir - Left side directory or File containing listing of left side |
|
42 # $iResultsFile - Name of file to write the list of files and MD5 checksum to |
|
43 # $iExclude - Reference to array of regular expression patterns to exclude |
|
44 # $iInclude - Reference to array of regular expression patterns to include |
|
45 # $iListFile - Filename to read for list of files from [Optional] |
|
46 # $iDumpDir - Directory to generate dump of comparison data [Optional] |
|
47 # |
|
48 # Outputs |
|
49 # |
|
50 # Description |
|
51 # This function generates the MD5 checksum file |
|
52 |
|
53 sub MD5Generate |
|
54 { |
|
55 my ($iLeftDir, $iResultsFile, $iExclude, $iInclude, $iListFile, $iDumpDir) = @_; |
|
56 |
|
57 my (@iLeftFiles); |
|
58 |
|
59 # This should have been check before getting here but to be on the safe side |
|
60 # check again, as it should never overwrite or append to the file |
|
61 croak "$iResultsFile already exists" if (-e $iResultsFile); |
|
62 croak "$iListFile does exist" if ((defined($iListFile) && (!-e $iListFile))); |
|
63 |
|
64 if (-d $iLeftDir){ |
|
65 # Make sure all the \ are / for the RegEx |
|
66 $iLeftDir =~ s/\\/\//g; |
|
67 # Make sure it does not have a / on the end |
|
68 $iLeftDir =~ s/\/$//; |
|
69 # If $iFileList is defined then use the filelist to generate @iLeftFiles |
|
70 if (defined($iListFile)) |
|
71 { |
|
72 # Generate list of files |
|
73 @iLeftFiles = &FilterDir($iLeftDir,$iExclude,$iInclude, $iListFile); |
|
74 } else { |
|
75 # Generate the the directory listing |
|
76 @iLeftFiles = &FilterDir($iLeftDir,$iExclude,$iInclude); |
|
77 } |
|
78 } else { |
|
79 croak "$iLeftDir is not a directory"; |
|
80 } |
|
81 |
|
82 # Open the results file for writing |
|
83 my ($fResultsFile) = new IO::File; |
|
84 croak "Cannot for $iResultsFile for writing" unless ($fResultsFile->open("> $iResultsFile")); |
|
85 |
|
86 # Write headers to the file |
|
87 print $fResultsFile "Host:".&hostname()."\n"; |
|
88 print $fResultsFile "Username:".$ENV{'USERNAME'}."\n"; |
|
89 print $fResultsFile "Date-Time:".scalar(localtime)."\n"; |
|
90 print $fResultsFile "Version:".$VERSION."\n"; |
|
91 print $fResultsFile "Directory:".$iLeftDir."\n"; |
|
92 print $fResultsFile "FileList:".$iListFile."\n" if (defined($iListFile)); |
|
93 print $fResultsFile "Exclusion(s):"; |
|
94 foreach my $iExc (sort @$iExclude) |
|
95 { |
|
96 print $fResultsFile "$iExc "; |
|
97 } |
|
98 print $fResultsFile "\n"; |
|
99 |
|
100 print $fResultsFile "Inclusion(s):"; |
|
101 foreach my $iInc (@$iInclude) |
|
102 { |
|
103 print $fResultsFile "$iInc "; |
|
104 } |
|
105 print $fResultsFile "\n"; |
|
106 |
|
107 print $fResultsFile "----------------\n"; |
|
108 |
|
109 # Write out sorted list of files with MD5 Checksums |
|
110 foreach my $iFile (sort @iLeftFiles) |
|
111 { |
|
112 my ($MD5, $type) = &EvalidCompare::GenerateSignature($iLeftDir."/".$iFile, $iDumpDir); |
|
113 print $fResultsFile $iFile." TYPE=".$type." MD5=".$MD5."\n"; |
|
114 } |
|
115 |
|
116 $fResultsFile->close; |
|
117 } |
|
118 |
|
119 |
|
120 # MD5Compare |
|
121 # |
|
122 # Inputs |
|
123 # $iLeftFile - Left side File containing listing and MD5 of left side |
|
124 # $iRightFile - Right side File containing listing and MD5 of right side |
|
125 # $iVerbose - Verbose Flag |
|
126 # $iLog - Logfile name |
|
127 # |
|
128 # Outputs |
|
129 # %iCommon - hash relative filenames with values of file type that are in both directories and Compare results/types |
|
130 # %iDiff - hash relative filenames with values of file type and directory side infomation |
|
131 # |
|
132 # Description |
|
133 |
|
134 sub MD5Compare |
|
135 { |
|
136 my ($iLeftFile) = shift; |
|
137 my ($iRightFile) = shift; |
|
138 my ($iVerbose) = defined($_[0]) ? shift : 0; |
|
139 my ($iLog) = defined($_[0]) ? shift : *STDOUT; |
|
140 |
|
141 my (%iCommon, %iDiff); |
|
142 |
|
143 my (%iLeftFiles, %iRightFiles); |
|
144 my (%iLeftHeaders, %iRightHeaders); |
|
145 |
|
146 # Backup check the files are available to read. |
|
147 croak "$iLeftFile is not a file" unless (-f $iLeftFile); |
|
148 croak "$iRightFile is not a file" unless(-f $iRightFile); |
|
149 |
|
150 #Read the files |
|
151 &ReadMD5File($iLeftFile, \%iLeftFiles, \%iLeftHeaders); |
|
152 &ReadMD5File($iRightFile, \%iRightFiles, \%iRightHeaders); |
|
153 |
|
154 # Check Critical headers |
|
155 foreach my $iHeader (sort keys %iLeftHeaders) |
|
156 { |
|
157 # Warning is certain headers are not identical |
|
158 if ($iHeader =~ /^Version|Exclusion\(s\)|Inclusion\(s\)/) |
|
159 { |
|
160 print $iLog "WARNING:$iHeader is different\n" if ($iLeftHeaders{$iHeader} ne $iRightHeaders{$iHeader}); |
|
161 } |
|
162 } |
|
163 |
|
164 # A Hash is used to combine the two directory listing using the filename as the key |
|
165 my %iCombinedFiles; |
|
166 |
|
167 # Enter the files from the left side listing and check the right side critical header are the same |
|
168 # Note all filenames are turned to lower case as this is designed to only wotk on Windows |
|
169 foreach my $iFile (sort keys %iLeftFiles) |
|
170 { |
|
171 $iCombinedFiles{$iFile} = "Left"; |
|
172 } |
|
173 |
|
174 # Enter the files from the right side listing |
|
175 # Note all filenames are turned to lower case as this is designed to only wotk on Windows |
|
176 foreach my $iFile (sort keys %iRightFiles) |
|
177 { |
|
178 # Check to see if any entry for this file exists on the left side |
|
179 if ((defined ($iCombinedFiles{$iFile})) && ( $iCombinedFiles{$iFile} eq "Left")) |
|
180 { |
|
181 # Yes, so add to the Common set |
|
182 # Check if the MD5 checksum matches |
|
183 # The [0] element is the file type |
|
184 if ($iLeftFiles{$iFile}[0] ne $iRightFiles{$iFile}[0]) |
|
185 { |
|
186 $iCommon{$iFile} = [$iLeftFiles{$iFile}[0]." to ".$iRightFiles{$iFile}[0], "Type Changed"]; |
|
187 } elsif ($iLeftFiles{$iFile}[1] eq $iRightFiles{$iFile}[1]) { |
|
188 $iCommon{$iFile} = [$iLeftFiles{$iFile}[0],"OK"]; |
|
189 } else { |
|
190 $iCommon{$iFile} = [$iLeftFiles{$iFile}[0],"Different"]; |
|
191 } |
|
192 # The filename key is not needed any more as both sides have been processed, so delete the hash entry |
|
193 delete $iCombinedFiles{$iFile}; |
|
194 } elsif (!defined($iCombinedFiles{$iFile})) { |
|
195 # No, the key is not defined, so this filename is only in the right side |
|
196 $iDiff{$iFile} = [$iRightFiles{$iFile}[0],"Right"]; |
|
197 # The filename key is not needed any more as both sides have been processed, so delete the hash entry |
|
198 delete $iCombinedFiles{$iFile}; |
|
199 } |
|
200 } |
|
201 |
|
202 # Add the files left in the hash to the Left side only list |
|
203 foreach my $iFile (sort keys %iCombinedFiles) |
|
204 { |
|
205 $iDiff{$iFile} = [$iLeftFiles{$iFile}[0],"Left"]; |
|
206 } |
|
207 |
|
208 # Return References to the Arrays |
|
209 return (\%iCommon, \%iLeftHeaders, \%iRightHeaders, \%iDiff); |
|
210 } |
|
211 |
|
212 # FilterDir |
|
213 # |
|
214 # Inputs |
|
215 # $iDir - Directory to process |
|
216 # $iExclude - Reference to array of regular expression patterns to exclude |
|
217 # $iInclude - Reference to array of regular expression patterns to include |
|
218 # $iListFile - Filename to read for list of files from [Optional] |
|
219 # |
|
220 # Outputs |
|
221 # @iFinalFileList - Filtered list relative filenames |
|
222 # |
|
223 # Description |
|
224 # This function produces a filtered list of filenames in the specified directory or from file |
|
225 |
|
226 sub FilterDir |
|
227 { |
|
228 my ($iDir,$iExclude,$iInclude, $iListFile) = @_; |
|
229 |
|
230 my (@iFileList, @iFinalFileList, $iFileName); |
|
231 |
|
232 if(defined($iListFile)) |
|
233 { |
|
234 open LIST, "$iListFile" or croak "Cannot open $iListFile\n"; |
|
235 while(<LIST>) |
|
236 { |
|
237 next if /^\s+$/; # skip blank lines |
|
238 my $iFileName = $iDir."/".$_; |
|
239 chomp $iFileName; # Remove new line |
|
240 if(-e $iFileName) |
|
241 { |
|
242 # The listed file exists add to the @iFileList |
|
243 push @iFileList, $iFileName; |
|
244 } else { |
|
245 print "Warning: Cannot find $iFileName\n"; |
|
246 } |
|
247 } |
|
248 close LIST; |
|
249 } else { |
|
250 # Produce full filelist listing without directory names |
|
251 find sub { push @iFileList, $File::Find::name if (!-d) ;}, $iDir; |
|
252 } |
|
253 |
|
254 foreach my $iFile ( @iFileList) |
|
255 { |
|
256 my $iExcludeFile = 0; |
|
257 |
|
258 # Remove the specified directory path from the front of the filename |
|
259 $iFile =~ s#^$iDir/##; |
|
260 |
|
261 # Process all Exclude RegEx to see if this file matches |
|
262 foreach my $iExcludeRegEx (@$iExclude) |
|
263 { |
|
264 if ($iFile =~ /$iExcludeRegEx/i) |
|
265 { |
|
266 # Mark this file to be excluded from the final list |
|
267 $iExcludeFile = 1; |
|
268 } |
|
269 } |
|
270 |
|
271 # Process all Include RegEx to see if this file matches |
|
272 foreach my $iIncludeRegEx (@$iInclude) |
|
273 { |
|
274 if ($iFile =~ /$iIncludeRegEx/i) |
|
275 { |
|
276 # Mark this file to be Included in the final list |
|
277 $iExcludeFile = 0; |
|
278 } |
|
279 } |
|
280 |
|
281 # Added the file to the final list based on the flag |
|
282 push @iFinalFileList, lc($iFile) unless $iExcludeFile; |
|
283 } |
|
284 |
|
285 return @iFinalFileList; |
|
286 |
|
287 } |
|
288 |
|
289 # MD5ComparePrint |
|
290 # |
|
291 # Inputs |
|
292 # $iCommon - Reference to Hash of common file names and the result the comparision |
|
293 # $iLeftHeaders - Reference to Hash contain the left side |
|
294 # $iRightHeaders - Reference to Hash contain the right side |
|
295 # $iDiff - Reference to Hash of relative filenames with values of file type and left/right directory side |
|
296 # $iLog - Logfile name |
|
297 # |
|
298 # Outputs |
|
299 # |
|
300 # Description |
|
301 # This function prints the output of a Compare |
|
302 sub MD5ComparePrint |
|
303 { |
|
304 my ($iCommon) = shift; |
|
305 my ($iLeftHeaders) = shift; |
|
306 my ($iRightHeaders) = shift; |
|
307 my ($iDiff) = shift; |
|
308 my ($iLog) = shift; |
|
309 |
|
310 my ($iFailed) = 0; # Count of the failed comparisions |
|
311 my ($iPassed) = 0; # Count of the Passed comparisions |
|
312 my ($iLeft) = 0; # Count of the Passed comparisions |
|
313 my ($iRight) = 0; # Count of the Passed comparisions |
|
314 |
|
315 |
|
316 my ($sec, $min, $hour, $mday, $mon, $year) = localtime(time); |
|
317 printf $iLog "\n----------------\n%02d:%02d %02d/%02d/%04d\n", $hour, $min, $mday, $mon+1, $year+1900; |
|
318 print $iLog "evalid\nLeft Side=$ARGV[0]\nRight Side=$ARGV[1]\n"; |
|
319 print $iLog "\nLeft side information\n"; |
|
320 foreach my $iHeader (sort keys %$iLeftHeaders) |
|
321 { |
|
322 print $iLog $iHeader.":".$$iLeftHeaders{$iHeader}."\n"; |
|
323 } |
|
324 print $iLog "\nRight side information\n"; |
|
325 foreach my $iHeader (sort keys %$iRightHeaders) |
|
326 { |
|
327 print $iLog $iHeader.":".$$iRightHeaders{$iHeader}."\n"; |
|
328 } |
|
329 print $iLog "\n"; |
|
330 |
|
331 foreach my $iFile (sort keys %$iCommon) |
|
332 { |
|
333 if ($$iCommon{$iFile}[1] eq "OK") |
|
334 { |
|
335 print $iLog "Passed:$iFile (".$$iCommon{$iFile}[0].")\n"; |
|
336 $iPassed++; |
|
337 } |
|
338 } |
|
339 print $iLog "\n"; |
|
340 |
|
341 foreach my $iFile (sort keys %$iCommon) |
|
342 { |
|
343 if ($$iCommon{$iFile}[1] eq "Type Changed") |
|
344 { |
|
345 print $iLog "Type Changed:$iFile (".$$iCommon{$iFile}[0].")\n"; |
|
346 $iFailed++; |
|
347 } |
|
348 } |
|
349 print $iLog "\n"; |
|
350 |
|
351 foreach my $iFile (sort keys %$iCommon) |
|
352 { |
|
353 if ($$iCommon{$iFile}[1] eq "Different") |
|
354 { |
|
355 print $iLog "Failed:$iFile (".$$iCommon{$iFile}[0].")\n"; |
|
356 $iFailed++; |
|
357 } |
|
358 } |
|
359 print $iLog "\n"; |
|
360 |
|
361 foreach my $iFile (sort keys %$iDiff) |
|
362 { |
|
363 if ($$iDiff{$iFile}[1] eq "Left") |
|
364 { |
|
365 print $iLog "Missing Left:$iFile (".$$iDiff{$iFile}[0].")\n"; |
|
366 $iLeft++; |
|
367 } |
|
368 } |
|
369 print $iLog "\n"; |
|
370 |
|
371 foreach my $iFile (sort keys %$iDiff) |
|
372 { |
|
373 if ($$iDiff{$iFile}[1] eq "Right") |
|
374 { |
|
375 print $iLog "Missing Right:$iFile (".$$iDiff{$iFile}[0].")\n"; |
|
376 $iRight++; |
|
377 } |
|
378 } |
|
379 |
|
380 print $iLog "\n\nSummary\n"; |
|
381 print $iLog "Total files processed=".($iPassed+$iFailed+$iRight+$iLeft)."\n"; |
|
382 print $iLog "Files Passed=$iPassed\n"; |
|
383 print $iLog "Files Failed=$iFailed\n"; |
|
384 print $iLog "Missing Files in Left=".$iRight."\n"; |
|
385 print $iLog "Missing Files in Right=".$iLeft."\n"; |
|
386 |
|
387 return ($iFailed); |
|
388 } |
|
389 # NameOnly |
|
390 # |
|
391 # Inputs |
|
392 # $filename - may contain path and file extension |
|
393 # |
|
394 # Outputs |
|
395 # $nameOnly - filename without extension and path |
|
396 # |
|
397 # Description |
|
398 # This routine is used to extract the name of the file |
|
399 # only from a filename that may include a path and file extension |
|
400 # |
|
401 sub NameOnly |
|
402 { |
|
403 my ($filename) = @_; |
|
404 my $nameOnly = basename($filename); |
|
405 $nameOnly =~ s/(\w+.*)\.\w+$/$1/; |
|
406 return $nameOnly; |
|
407 } |
|
408 |
|
409 # MD5CompareZipDel |
|
410 # |
|
411 # Inputs |
|
412 # $iCommon - Reference to Hash of common file names and the result the comparision |
|
413 # $iDiff - Reference to Hash of relative filenames with values of file type and left/right directory side |
|
414 # $iLeft - filename of Left side directory |
|
415 # $iRight - filename of Right side directory |
|
416 # |
|
417 # Outputs |
|
418 # |
|
419 # Description |
|
420 # This function prints the output of a Compare results in a format ready for creating the a |
|
421 # Zip and batch file to upgrade the left side to the equivalent of the right side. |
|
422 sub MD5CompareZipDel |
|
423 { |
|
424 my ($iCommon) = shift; |
|
425 my ($iDiff) = shift; |
|
426 my ($iLeft) = shift; |
|
427 my ($iRight) = shift; |
|
428 |
|
429 # Build a suitable name for outputfiles based on input filenames |
|
430 $iLeft = NameOnly($iLeft); |
|
431 $iRight = NameOnly($iRight); |
|
432 my ($iBasename) = $iLeft."_to_".$iRight; |
|
433 |
|
434 open DELLIST, ">del_$iBasename.bat"; |
|
435 open ZIPLIST, ">zip_$iBasename.log"; |
|
436 open ZIPBAT, ">zip_$iBasename.bat"; |
|
437 |
|
438 foreach my $iFile (sort keys %$iCommon) |
|
439 { |
|
440 if (($$iCommon{$iFile}[1] eq "Different") || ($$iCommon{$iFile}[1] eq "Type Changed")) |
|
441 { |
|
442 print ZIPLIST "$iFile\n"; |
|
443 } |
|
444 } |
|
445 |
|
446 foreach my $iFile (sort keys %$iDiff) |
|
447 { |
|
448 if ($$iDiff{$iFile}[1] eq "Right") |
|
449 { |
|
450 print ZIPLIST "$iFile\n"; |
|
451 } else { |
|
452 # DEL needs the / to be \ |
|
453 $iFile =~ s/\//\\/g; |
|
454 print DELLIST "del /F $iFile\n"; |
|
455 } |
|
456 } |
|
457 |
|
458 print ZIPBAT "zip $iBasename.zip -@<zip_$iBasename.log\n"; |
|
459 |
|
460 close DELLIST; |
|
461 close ZIPLIST; |
|
462 close ZIPBAT; |
|
463 |
|
464 } |
|
465 |
|
466 |
|
467 # Private |
|
468 |
|
469 # ReadMD5File |
|
470 # |
|
471 # Inputs |
|
472 # $iResultsFile - Results filename to process |
|
473 # $iResults - Reference to Results Hash |
|
474 # $iHeaders - Reference to Headers Hash |
|
475 # |
|
476 # Outputs |
|
477 # |
|
478 # Description |
|
479 # This function reads the conntent of the MD5 File in to a Hash |
|
480 sub ReadMD5File |
|
481 { |
|
482 my ($iResultsFile, $iResults, $iHeaders) = @_; |
|
483 |
|
484 # Open the results file for reading |
|
485 open (INPUT, "$iResultsFile") or croak "Cannot for $iResultsFile for reading"; |
|
486 while (<INPUT>) |
|
487 { |
|
488 if (/^(\S+?):(.*)/) |
|
489 { |
|
490 $$iHeaders{$1} = $2; |
|
491 } elsif (/^(\S+?)\sTYPE=(.*?)\sMD5=(.*)/){ |
|
492 $$iResults{$1} = [$2,$3]; |
|
493 } |
|
494 } |
|
495 close INPUT; |
|
496 |
|
497 } |
|
498 |
|
499 1; |