#!/usr/bin/perl ############################################################################### # # # Name : diff_lib # # Author : Chris Koeritz # # Rights : Copyright (C) 1996-$now by Author # # # # Purpose: # # # # A collection of services used for creating a report of differences # # between two directories. # # # ############################################################################### # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License as published by the Free # # Software Foundation; either version 2 of the License or (at your option) # # any later version. See: "http://www.gruntose.com/Info/GNU/GPL.txt" for a # # version of the License. Please send any updates to "fred@gruntose.com". # ############################################################################### # note: this script and the scripts that use it require the Text::Diff # support from CPAN. this can be installed by using cpan and telling it # install Text::Diff require "ctime.pl"; require "filename_helper.pl"; require "importenv.pl"; use Text::Diff; sub diff_dirs { local(@arguments) = @_; # print "args: @arguments\n"; # the temporary file below is used during the difference checking, but is # deleted again after differ finishes. $random_num = (rand() * 1000000) % 1000000; ### $temp_file = $TMP . "/diff_tmp_" . $random_num . ".txt"; ############################################################################ # constants used for header creation. $long_line = "-------------------------------------------------------------------------------\n"; $break_line = "###############################################################################\n"; $separator = "\n\n"; $differing_header = "Differences between %1 and %2:\n"; $no_differences = "The files %1 and %2 are the same.\n"; ############################################################################ # state machine constants and initialization. $STARTING = 2; # initial state. $SAW_DIFF = 3; # recently saw a difference. $SAW_SAME = 4; # recently saw no differences. $state = $STARTING; ############################################################################ $header_printed = 0; # did we print the header for this directory yet? ############################################################################ if ($#arguments < 0) { # complain if they don't pass a directory name. &instructions; return; } # get the comparison directory from the arguments. local($compare_directory) = &sanitize_name($arguments[0]); if ($compare_directory eq "") { # it's a bad directory name, only composed of slashes. &instructions; return; } # the current directory is usually used as the starting point. local($source_directory) = "."; if ($arguments[1] ne "") { # they specified a current directory. $source_directory = $arguments[1]; } $source_directory = &sanitize_name($source_directory); # print "src=$source_directory, dest=$compare_directory\n"; # keep a local copy of the names for print_header. #hmmm: fix bad design. local($temp_src) = $source_directory; local($temp_dest) = $compare_directory; # iterate over all the files in the source directory. opendir CURDIR, $source_directory || die("couldn't open $source_directory for reading.\n"); foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } $filename = $source_directory."/".$filename; # make sure we compare against the base part of the name. @name_components = split(/\//, $filename); $basename = $name_components[$#name_components]; # print "doing diff of $filename against $compare_directory/$basename\n"; &do_diff($filename, $compare_directory."/".$basename); } closedir CURDIR; if ($state == $SAW_SAME) { print $long_line; } # clean up after the diffing process. ### unlink($temp_file); # report files that exist in target directory, but not here. &back_compare($source_directory, $compare_directory); # only print the closure line if we printed some other stuff. if ($header_printed) { print "\n"; print $break_line; } return; # scram. } ############################################################################ sub print_header { # this function prints out the header prior to printing out any real # data. if there are no diffs, the header should never get printed. print "$break_line\n"; print "Source is \"$temp_src\".\n"; print "Destination is \"$temp_dest\".\n"; local($printable_date) = &ctime(time); print "compared on $printable_date\n"; $header_printed = 1; } ############################################################################ sub instructions { print " differ: This program needs a directory name. The directory name is used to specify a target directory where there are files which are mostly similar to the files in this directory. The files in the current directory are compared to those in the specified target directory and the differences are sent to the standard output. "; } ############################################################################ sub cpdiff_instructions { print " cpdiff: This program needs two directory names. The first is the source and the second is a target directory where the files are mostly similar to the files in the source directory. The files in source are compared to those in the target directory and if a file differs, then it's copied from the source to the target. Also, if the file does not exist in the target directory, it gets copied. "; } ############################################################################ sub synchronize_instructions { print " synch_build: This program needs one directory name to be passed on the command line. This directory is where the installation's executable files live. Any files that are in the installation bin directory will be compared against the files in the build repository (specified by an environment variable REPOSITORY_DIR). If files differ, they will be copied from the repository into the installation directory. "; } ############################################################################ # replaces the variable marks in the text with the two substitutions. sub replace_text { local($text, $subst_1, $subst_2) = @_; local($text_copy) = $text; $text_copy =~ s/%1/$subst_1/; $text_copy =~ s/%2/$subst_2/; return $text_copy; } sub change_to_saw_diffs { print $separator, $long_line; } sub change_to_saw_same { } ############################################################################ # checks the differences between the two files and creates appropriate # output. sub do_diff { local($first, $second) = @_; # turn stupid pc slashes into normal ones. $first =~ s/\\/\//g; $second =~ s/\\/\//g; @first_components = split(/\//, $first); $base_filename1 = $first_components[$#first_components]; @second_components = split(/\//, $second); $base_filename2 = $second_components[$#second_components]; # skip if it's a directory. if (-d $first) { return; } # skip the file if we don't think it's important. if (! &important_filename($base_filename1)) { return; } if (! -e $second) { # make sure we don't bother if the file's not in the target directory. if (!$header_printed) { &print_header; } &change_to_saw_diffs; print "The file \"$second\" does not exist.\n"; print $long_line; return; } $temp_file = $TMP . "/dodiff_tmp_" . $random_num . ".txt"; local($diff_output) = diff $first, $second, { STYLE => "OldStyle" }; if (!length($diff_output)) { # there are no differences. &change_to_saw_same; # print &replace_text($no_differences, "src/".$base_filename1, # "dest/".$base_filename2); } else { # there were differences. if (!$header_printed) { &print_header; } &change_to_saw_diffs; print &replace_text($differing_header, "src/".$base_filename1, "dest/".$base_filename2); print $long_line; if (-B $first || -B $second) { print "Binary files $first and $second differ.\n"; } else { print $diff_output; } } unlink $temp_file; } ############################################################################ # this function copies files as needed by comparing whether a file has # changed or not. third parm is a flag added to cp, like -p to preserve # the timestamps. sub do_cpdiff { local($first, $second, $third) = @_; @first_components = split(/\//, $first); $base_filename1 = $first_components[$#first_components]; @second_components = split(/\//, $second); $base_filename2 = $second_components[$#second_components]; # turn stupid pc slashes into normal ones. $second =~ s/\\/\//g; # skip if it's a directory. if (-d $first) { return; } # skip the file if we don't think it's important. if (! &important_filename($base_filename1)) { return; } if (! -e $second) { # the file doesn't exist yet, so copy it. # print "copying $first -> $second\n"; system("cp -v $third \"$first\" \"$second\""); return; } local($diff_output) = diff $first, $second, { STYLE => "OldStyle" }; if (length($diff_output)) { # there were differences in the two files, so copy the source. # print "copying $first -> $second\n"; system("cp -v $third \"$first\" \"$second\""); unlink $temp_file; return; } unlink $temp_file; #print "did nothing; $first & $second are identical\n"; } ############################################################################ # back_compare checks to make sure that all of the files in the target # directory are represented in the source directory. sub back_compare { local($source, $target) = @_; local(@missing_list); # print "backcomp: source=$source target=$target\n"; opendir CURDIR, $target || die("couldn't open $target for reading.\n"); foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } $filename = $target."/".$filename; # chop up the filename. @dirs = split(/\//, $filename); # get the basename from the last array element. $basename = $dirs[$#dirs]; # skip the file if it seems like junk. if (! &important_filename($basename)) { next; } # print "backcomp: file a=$source/$basename b=$filename\n"; if ( (! -e "$source/$basename") && (! -d $filename) ) { push(@missing_list, $basename); } } closedir CURDIR; if ($#missing_list < 0) { return; } if (!$header_printed) { &print_header; } print "$separator ------------------------------------------------------------------------------- The following files existed in \"$target\", but are missing from \"$source\": ------------------------------------------------------------------------------- "; foreach $filename (@missing_list) { print "$filename\n"; } } ############################################################################ # this function copies files from the source directory to the target directory # if there is a file of the same name whose contents differ, or if there is # no file of that name in the target directory. sub copy_diff_dirs { local(@arguments) = @_; # print "args: @arguments\n"; # the temporary file below is used during the difference checking, but is # deleted again after differ finishes. $random_num = (rand() * 1000000) % 1000000; $temp_file = $TMP . "/cpdiff_tmp_" . $random_num . ".txt"; ############################################################################ if ($#arguments < 1) { # complain if they don't pass two directory names. &cpdiff_instructions; return; } # the source directory is the first parameter. local($source_directory) = &sanitize_name($arguments[0]); if ($source_directory eq "") { # it's a bad directory name, only composed of slashes. &cpdiff_instructions; return; } # get the comparison directory from the arguments. local($compare_directory) = &sanitize_name($arguments[1]); if ($compare_directory eq "") { # it's a bad directory name here. &cpdiff_instructions; return; } # print "src=$source_directory, dest=$compare_directory\n"; # iterate over all the files in the source directory. opendir CURDIR, $source_directory || die("couldn't open $source_directory for reading.\n"); foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } $filename = $source_directory."/".$filename; # make sure we compare against the base part of the name. @name_components = split(/\//, $filename); $basename = $name_components[$#name_components]; # print "diffing $filename against $compare_directory/$basename\n"; &do_cpdiff($filename, $compare_directory."/".$basename, "-p"); } closedir CURDIR; unlink($temp_file); # drop the temp file we were using. return; # scram. } ############################################################################ # this version of copy_diff_dirs (see above) causes the dates of the copied # files to be changed to "now". #hmmm: extract the common bits used in copy_diff_dirs into useful functions. sub copy_diff_dirs_using_now { local(@arguments) = @_; # print "args: @arguments\n"; # the temporary file below is used during the difference checking, but is # deleted again after differ finishes. $random_num = (rand() * 1000000) % 1000000; $temp_file = $TMP . "/cpdiff_tmp_" . $random_num . ".txt"; ############################################################################ if ($#arguments < 1) { # complain if they don't pass two directory names. &cpdiff_instructions; return; } # the source directory is the first parameter. local($source_directory) = &sanitize_name($arguments[0]); if ($source_directory eq "") { # it's a bad directory name, only composed of slashes. &cpdiff_instructions; return; } # get the comparison directory from the arguments. local($compare_directory) = &sanitize_name($arguments[1]); if ($compare_directory eq "") { # it's a bad directory name here. &cpdiff_instructions; return; } # print "src=$source_directory, dest=$compare_directory\n"; # iterate over all the files in the source directory. opendir CURDIR, $source_directory || die("couldn't open $source_directory for reading.\n"); foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } $filename = $source_directory."/".$filename; # make sure we compare against the base part of the name. @name_components = split(/\//, $filename); $basename = $name_components[$#name_components]; # print "diffing $filename against $compare_directory/$basename\n"; &do_cpdiff($filename, $compare_directory."/".$basename); } closedir CURDIR; unlink($temp_file); # drop the temp file we were using. return; # scram. } ############################################################################ # makes sure all of the exes and dynamic libs are up to date in the # installed executable directory. sub synchronize_against_build { local(@arguments) = @_; # print "args: @arguments\n"; # the temporary file below is used during the difference checking, but is # deleted again after differ finishes. $temp_file = "synchb_tmp.txt"; ############################################################################ if ($#arguments < 0) { # complain if they don't pass a directory name. &synchronize_instructions; return; } # clean up the directory they passed. local($install_directory) = &sanitize_name($arguments[0]); if ($install_directory eq "") { # it's a bad directory name, only composed of slashes. &synchronize_instructions; return; } # print "install=$install_directory\n"; # print "repos=$REPOSITORY_DIR\n"; # iterate over all the files in the source directory. opendir CURDIR, $install_directory || die("couldn't open $install_directory for reading.\n"); $compare_directory = "$REPOSITORY_DIR/dll"; foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } if (! ($filename =~ /\.dll$/)) { next; } $filename = $install_directory."/".$filename; # make sure we compare against the base part of the name. @name_components = split(/\//, $filename); $basename = $name_components[$#name_components]; if (! -e $compare_directory."/".$basename) { next; } # print "diffing $filename against $compare_directory/$basename\n"; &do_cpdiff($compare_directory."/".$basename, $filename); } closedir CURDIR; # repeat for the exe directory. opendir CURDIR, $install_directory || die("couldn't open $install_directory for reading.\n"); $compare_directory = "$REPOSITORY_DIR/exe"; foreach $filename (readdir CURDIR) { if ( ($filename eq ".") || ($filename eq "..") ) { next; } if (! ($filename =~ /\.exe$/)) { next; } $filename = $install_directory."/".$filename; # make sure we compare against the base part of the name. @name_components = split(/\//, $filename); $basename = $name_components[$#name_components]; if (! -e $compare_directory."/".$basename) { next; } # print "diffing $filename against $compare_directory/$basename\n"; &do_cpdiff($compare_directory."/".$basename, $filename); } closedir CURDIR; unlink($temp_file); # drop the temp file we were using. return; # scram. } ############################################################################ 1;