#!perl -w

# This script compares similar named control files (makefile, *.mak *.mk *.pl) in two directories. 
#
#
# The script expects the utility diff to be in the path.
#
# The syntax of each line in the configuration file is the following (comments start with #)
# <left_directory> <right_directory>
#
# Attention: file and directory names MUST NOT contain spaces or other white space
use strict;

use File::Glob ':glob';
use File::Spec;

# define global variables
my $cnfFile=$ARGV[0];
my $logFile="cmp_report.log";
my $firstComment = 1;

# find the config file
@ARGV == 1 || die "Usage:\tcmp_maks.pl config_file\n";

# check for diff.exe
system("diff -v > cmp_report.log 2>&1") && die "ERROR: diff must be in the path \n";

# open the config file
#
open (IN,"<$cnfFile")||die "Can't open $cnfFile !\n";

# parse the config file
#
  my $line;

  while(defined($line = <IN>))
  {
    # reset variable for next loop
    my $leftDir;
    my $rightDir;
    my @files;
    my @excludedFiles;
    my @explicitFiles;

    # ignore comments (starting with #)
    if($line =~ /^\s*\#/) {
      # print out special comments starting with ##
      if ($' =~ /^\s*\#/) {
        if ($firstComment == 1) {
          print "\n";
          $firstComment = 0;
        }
        print "[COMMENT]  $'";
      }
      else {
        $firstComment = 1;
      }
      next;
    }

    # ignore empty lines
    if($line =~ /^\s+$/) {
      next;
    }

    # remove trailing \n
    chomp($line);
    
    # put everything SEPARATED BY WHITESPACE in an array
    @files = split(/\s+/,$line);

    # find the directories (and remove them from the array)
    $leftDir = $files[0]; shift(@files);
    $rightDir = $files[0]; shift(@files);

    # check whether the directories are wildcards
#    if ($leftDir =~ s/(.*?)(\/\*)/$1/ && $rightDir =~ s/(.*?)(\/\*)/$1/ ) {
    if ($leftDir =~ s:(.*?)([/\\]\*):$1: && $rightDir =~ s:(.*?)([/\\]\*):$1: ) {
      my @dirs;
      my @leftDirs;
      my @rightDirs;

      # check whether the parent directories exist
      (-e $leftDir) || die "Directory $leftDir does not exist !\n";
      (-e $rightDir) || die "Directory $rightDir does not exist !\n";

      # find all sub directories left
      @dirs = bsd_glob("$leftDir/*");
      @dirs = sort @dirs;
      foreach (@dirs) {
        my ($drv,$dir);
        ($drv,$dir,$_) = File::Spec->splitpath($_);
        if (-d "$leftDir/$_") {
          push (@leftDirs, "$_");
        }
      }

      # reset variables
      splice (@dirs);

      # find all sub directories right
      @dirs = bsd_glob("$rightDir/*");
      @dirs = sort @dirs;
      foreach (@dirs) {
        my ($drv,$dir);
        ($drv,$dir,$_) = File::Spec->splitpath($_);
        if (-d "$rightDir/$_") {
          push (@rightDirs, "$_");
        }
      }

      # create a hash that uses the filename as the key and an integer as value
      # to find those directories that are found in both directories or only in one
      # of them
      my %count;
      my $element;

      foreach $element (@leftDirs) { $count{$element}++; }
      foreach $element (@rightDirs) { $count{$element}+=2; }
  
      # delete used arrays for later reuse
      splice (@dirs);
      splice (@leftDirs);
      splice (@rightDirs);

      foreach $element (keys %count) { 
          if ($count{$element} == 1) {
            push @leftDirs, $element;
          }
          elsif ($count{$element} == 2) {
            push @rightDirs, $element;
          }
          elsif  ($count{$element} == 3) {
            push @dirs, $element;
          }
      }
      print "[LEFT   ]  $leftDir/*\n";
      print "[RIGHT  ]  $rightDir/*\n";
      foreach $element (sort(@leftDirs)) { 
        print "[WARNING - directory missing right]     $element\n";
      }
      foreach $element (sort(@rightDirs)) { 
        print "[WARNING - directory missing left]     $element\n";
      }
      print "\n";
      foreach $element (sort(@dirs)) {
        # ignore some commonly used version files
        splice (@excludedFiles);
#        push (@excludedFiles,"ver.obj");
#        push (@excludedFiles,"$element\_version.obj");
        cmp_dirs ("$leftDir/$element", "$rightDir/$element", \@excludedFiles, \@explicitFiles);
      }

    }
    else {
      # check whether the directories exist
      (-e $leftDir) || die "Directory $leftDir does not exist !\n";
      (-e $rightDir) || die "Directory $rightDir does not exist !\n";

      if (defined($files[0])) {
        # some files are are specified or excluded explicitely
        foreach (@files) {
          if (/^-/) {
            push(@excludedFiles,$');
          }
          elsif(/^\+/) {
            (-e "$leftDir/$'") || die "File $rightDir/$' does not exist !\n";
            (-e "$rightDir/$'") || die "File $rightDir/$' does not exist !\n";
            push(@explicitFiles,$');
          }
        }
      }
      cmp_dirs ($leftDir, $rightDir, \@excludedFiles, \@explicitFiles);
    }
 
  }


# close the config file
#
close IN;

# delete the log file
#
#unlink($logFile);

###################
#
sub cmp_dirs {

    my $leftDir = $_[0];
    my $rightDir = $_[1];
    my @excludedFiles = @{$_[2]};
    my @explicitFiles = @{$_[3]};

    my @files;
    my @leftFiles;
    my @rightFiles;
    my $element;

    if (defined($explicitFiles[0])) {
      @files = @explicitFiles;
    }
    else {
      @leftFiles = bsd_glob("$leftDir/*.mk");
      push (@leftFiles, bsd_glob("$leftDir/*.mak"));
      push (@leftFiles, bsd_glob("$leftDir/*makefile"));
      push (@leftFiles, bsd_glob("$leftDir/*.pl"));

      @leftFiles = sort @leftFiles;
      foreach (@leftFiles) {
        my ($drv,$dir);
        ($drv,$dir,$_) = File::Spec->splitpath($_);
      }
  
      @rightFiles = bsd_glob("$rightDir/*.mk");
      push (@rightFiles, bsd_glob("$rightDir/*.mak"));
      push (@rightFiles, bsd_glob("$rightDir/*makefile"));
      push (@rightFiles, bsd_glob("$rightDir/*.pl"));
      @rightFiles = sort @rightFiles;
      foreach (@rightFiles) {
        my ($drv,$dir);
        ($drv,$dir,$_) = File::Spec->splitpath($_);
      }
  
      # create a hash that uses the filename as the key and an integer as value
      # to find those files that are found in both directories or only in one
      # of them
      my %count;

      foreach $element (@leftFiles) { $count{$element}++; }
      foreach $element (@rightFiles) { $count{$element}+=2; }
  
      # delete used arrays for later reuse
      splice (@files);
      splice (@leftFiles);
      splice (@rightFiles);

      # remove all exclude files
      foreach $element (@excludedFiles) { 
        if (exists($count{$element})) {
#          print "Skipping file: $element \n";
          delete($count{$element});
        }
      }

      foreach $element (keys %count) { 
          if ($count{$element} == 1) {
            push @leftFiles, $element;
          }
          elsif ($count{$element} == 2) {
            push @rightFiles, $element;
          }
          elsif  ($count{$element} == 3) {
            push @files, $element;
          }
      }
    }


    print "[LEFT ]  $leftDir/\n";
    print "[RIGHT]  $rightDir/\n";

    if(defined($leftFiles[0]) || defined($rightFiles[0]) ) {
      my %tmp;

      foreach $element (@leftFiles)  { $tmp{$element} = "  [WARNING - file missing right]     $element\n" };
      foreach $element (@rightFiles) { $tmp{$element} = "  [WARNING - file missing left ]     $element\n"; }

      foreach $element (sort(keys %tmp)) { 
        print $tmp{$element};
      }
    }


    foreach (sort(@files)) {
      if (system("cmp $leftDir/$_ $rightDir/$_ >>$logFile 2>&1")) {
       print "  [ERROR   - files differ]     $_\n";
       print "  [Diff'ing files]  $leftDir/$_  $rightDir/$_\n";
 
       my $leftFile = "$leftDir/$_";
       $leftFile =~ s:/:\\:g ;
       my $rightFile = "$rightDir/$_";
       $rightFile =~ s:/:\\:g ;

       open(DIFFOUT, '-|', "diff -I ^#.*\$ -t -w -y --suppress-common-lines --width=120 $leftFile $rightFile");
       #indent output
       while (defined($line = <DIFFOUT>)) {
         print "      $line";
       }

       close(DIFFOUT);
       print " \n";

      }
    }
   print "\n";
}
