#!/usr/bin/env perl
# +-======-+ 
#  Copyright (c) 2003-2007 United States Government as represented by 
#  the Admistrator of the National Aeronautics and Space Administration.  
#  All Rights Reserved.
#  
#  THIS OPEN  SOURCE  AGREEMENT  ("AGREEMENT") DEFINES  THE  RIGHTS  OF USE,
#  REPRODUCTION,  DISTRIBUTION,  MODIFICATION AND REDISTRIBUTION OF CERTAIN 
#  COMPUTER SOFTWARE ORIGINALLY RELEASED BY THE UNITED STATES GOVERNMENT AS 
#  REPRESENTED BY THE GOVERNMENT AGENCY LISTED BELOW ("GOVERNMENT AGENCY").  
#  THE UNITED STATES GOVERNMENT, AS REPRESENTED BY GOVERNMENT AGENCY, IS AN 
#  INTENDED  THIRD-PARTY  BENEFICIARY  OF  ALL  SUBSEQUENT DISTRIBUTIONS OR 
#  REDISTRIBUTIONS  OF THE  SUBJECT  SOFTWARE.  ANYONE WHO USES, REPRODUCES, 
#  DISTRIBUTES, MODIFIES  OR REDISTRIBUTES THE SUBJECT SOFTWARE, AS DEFINED 
#  HEREIN, OR ANY PART THEREOF,  IS,  BY THAT ACTION, ACCEPTING IN FULL THE 
#  RESPONSIBILITIES AND OBLIGATIONS CONTAINED IN THIS AGREEMENT.
#  
#  Government Agency: National Aeronautics and Space Administration
#  Government Agency Original Software Designation: GSC-15354-1
#  Government Agency Original Software Title:  GEOS-5 GCM Modeling Software
#  User Registration Requested.  Please Visit http://opensource.gsfc.nasa.gov
#  Government Agency Point of Contact for Original Software:  
#  			Dale Hithon, SRA Assistant, (301) 286-2691
#  
# +-======-+ 
#=======================================================================
# name - cmpdir
# purpose - this utility allows users to quickly and easily see
#           differences between files in two experiment directories.
#
# Note:
# 1. See usage subroutine for usage information
# 2. This script uses the xxdiff utility
#
# !Revision History
# 29Mar2010  Stassi  Initial version.
#=======================================================================
use strict;
use warnings;

# global variables
#-----------------
my ($ext, @p1, @p2, $quiet, $subdir, $verbose, @exclude);
my (%diffsTXT, %diffsBIN, %different, $diffFLGs);
my (%found, @notfound1, @notfound2, %identical);
my ($dirA, $dirB, $dir1, $dir2, @files1, @files2, $first);
my (@subdirs);

# main program
#-------------
{
    init();
    while (1) {
        cmp_files();
        show_results();
    }
}

#=======================================================================
# name - init
# purpose  - get input parameters and initialize global variables
#=======================================================================
sub init {
    use File::Basename;
    use Getopt::Long;
    my ($bwiFLG, $help, $rsflg, $runflg, $subdirname);

    # get runtime flags
    #------------------
    GetOptions("bwi"      => \$bwiFLG,
               "ext=s"    => \$ext,
               "h"        => \$help,
               "help"     => \$help,
               "p1=s"     => \@p1,
               "p2=s"     => \@p2,
               "q"        => \$quiet,
               "rs"       => \$rsflg,
               "run"      => \$runflg,
               "subdir=s" => \$subdir,
               "v"        => \$verbose,
               "X=s"      => \@exclude );
    usage() if $help;

    # shortcuts for checking run or rs directory 
    #-------------------------------------------
    if ($runflg) { $subdir = "run" }
    if ($rsflg)  { $subdir = "rs"; $ext = "bin" unless $ext }

    # runtime parameters
    #-------------------
    $dirA  = shift @ARGV;
    $dirB  = shift @ARGV;

    # check inputs
    #-------------
    usage() unless $dirA and $dirB;
    die ">> Error << Cannot find directory $dirA" unless -d $dirA;
    die ">> Error << Cannot find directory $dirB" unless -d $dirB;

    # remove final slash from directory name
    #---------------------------------------
    $dirA = cleanDirName($dirA);
    $dirB = cleanDirName($dirB);

    # Add directory basenames to pattern arrays
    #------------------------------------------
    push @p1, basename $dirA;
    push @p2, basename $dirB;

    # check for @p1/@p2 correspondence
    #---------------------------------
    die ">> Error << unequal number of patterns (-p1/-p2)"
        unless scalar(@p1) == scalar(@p2);

    # find common subdirectories
    #---------------------------
    foreach (<$dirA/*>) {
        next unless -d $_;
        $subdirname = basename $_;
        push @subdirs, $subdirname if -d "$dirB/$subdirname";
        next;
    }

    # check for requested subdirectory
    #---------------------------------
    if ($subdir) {
        die "not found: $dirA/$subdir\n" unless -d "$dirA/$subdir";
        die "not found: $dirB/$subdir\n" unless -d "$dirB/$subdir";
    }

    # initialize other variables
    #---------------------------
    $first = 1;
    $diffFLGs = "";
    $diffFLGs .= "-bwi" if $bwiFLG;
}

#=======================================================================
# name - cmp_files
# purpose - get list of files from both directories and compare files
#           to find those which are the same in both directories, those
#           which are not, and those which are in only one directory or
#           the other.
#=======================================================================
sub cmp_files {
    use File::Basename;
    my ($file1, $file2, $base1, $base2, $dirname1, $dirname2, $middir);
    my ($maxK, $maxV, $fmt1, $fmt2);
    my ($status, $indexB, $indexT);

    # get directories
    #----------------
    $dir1 = $dirA;
    $dir2 = $dirB;

    $dir1 .= "/$subdir" if $subdir;
    $dir2 .= "/$subdir" if $subdir;

    $dir1 = cleanDirName($dir1);
    $dir2 = cleanDirName($dir2);

    # get file lists
    #---------------
    if ($ext) {
        chomp( @files1 = (`find $dir1 -name \\*.$ext`) );
        chomp( @files2 = (`find $dir2 -name \\*.$ext`) );
    } else {
        @files1 = ( <$dir1/*> );
        @files2 = ( <$dir2/*> );
    }

    # remove directories from file list
    #----------------------------------
    for (0..$#files1) {
        $file1 = shift @files1;
        push @files1, $file1 unless -d $file1;
    }
    for (0..$#files2) {
        $file2 = shift @files2;
        push @files2, $file2 unless -d $file2;
    }
    show_file_counts(1);
    find_common_label(\@files1, \@files2);

    # zero-out lists
    #---------------
    %diffsBIN = ();
    %diffsTXT = ();
    %different = ();
    %identical = ();
    @notfound1 = ();
    @notfound2 = ();
    %found = ();

    # which files are in both expdir1 and expdir2?
    #---------------------------------------------
    foreach $file1 (@files1) {

        $base1 = basename $file1;
        next if Xcluded($base1);

        $dirname1 = dirname $file1;
        $middir = "";
        $middir = $1 if ($dirname1 =~ m[^$dir1/(\S+)]);
        print "(1) checking $file1\n" if $verbose;

        $file2 = namechange("$dir2/$middir/$base1", "1");
        print "(2) checking $file2\n\n" if $verbose;
        if (-e $file2) { $found{$file1} = $file2 }
        else           { push @notfound2, $file1 }
    }

    # send job to dmget files (just in case)
    #---------------------------------------
    dmget(keys %found);
    dmget(values %found);

    # compare files found in both directories
    #----------------------------------------
    $maxK = 0; $maxV = 0;
    foreach (keys   %found) { $maxK = baselen($_) if baselen($_) > $maxK }
    foreach (values %found) { $maxV = baselen($_) if baselen($_) > $maxV }
    $fmt1 = "checking %s\n";
    $fmt2 = "checking %-${maxV}s <=> %-${maxK}s\n";

    foreach $file1 (sort keys %found) {
        $file2 = $found{$file1};
        $base1 = basename $file1;
        $base2 = basename $file2;
        unless ($quiet) {
            if ($base1 eq $base2) { printf $fmt1, $base1         }
            else                  { printf $fmt2, $base1, $base2 }
        }

        ($status = system "diff $diffFLGs $file1 $file2 >& /dev/null") /= 256;
        unless ($status) {
            $identical{$base1} = $base2;
            next;
        }

        # files are different
        #--------------------
        $different{$file1} = $file2;
        if ( binary($file1, $file2) ) { $diffsBIN{++$indexB} = $file1 }
        else                          { $diffsTXT{++$indexT} = $file1 }
    }

    # compare dir2 files to dir1
    #---------------------------
    foreach $file2 (@files2) {

        # check corresponding file in dir1
        #---------------------------------
        $base2 = basename $file2;
        next if Xcluded($base2);

        $dirname2 = dirname $file2;
        $middir = "";
        $middir = $1 if ($dirname2 =~ m[^$dir2/(\S+)]);
        print "(2) checking $file2\n" if $verbose;

        $file1 = namechange("$dir1/$middir/$base2", "2");
        $base1 = basename $file1;
        print "(1) checking $file1\n\n" if $verbose;
        unless (-e $file1) { push @notfound1, $file2; next }
    }
}

#=======================================================================
# name - find_common_label
# purpose - check the file names to see if the files have a common label.
#           if so, then add labels to pattern arrays, if not already present.
#=======================================================================
sub find_common_label {
    my ($arr1ADDR, $arr2ADDR, @arr1, @arr2);
    my ($base, @parts, %L1, %L2);
    my ($max, $labelone, $labeltwo);

    $arr1ADDR = shift @_;
    $arr2ADDR = shift @_;
    @arr1 = @$arr1ADDR;
    @arr2 = @$arr2ADDR;

    # get labels from files in each array
    #------------------------------------
    foreach (@arr1) {
        $base = basename $_;
        @parts = split /\./, $base;
        ++$L1{$parts[0]} if scalar(@parts) > 1;
    }
    foreach (@arr2) {
        $base = basename $_;
        @parts = split /\./, $base;
        ++$L2{$parts[0]} if scalar(@parts) > 1;
    }
    
    # find most common label in each array
    #-------------------------------------
    $max = 0; $labelone = "";
    foreach (keys %L1) {
        if ($L1{$_} > $max) { $labelone = $_; $max = $L1{$_} }
    }
    return unless $max*2 > @arr1;

    $max = 0; $labeltwo = "";
    foreach (keys %L2) {
        if ($L2{$_} > $max) { $labeltwo = $_; $max = $L2{$_} }
    }
    return unless $max*2 > @arr2;

    # add labels to pattern arrays if appropriate
    #--------------------------------------------
    if ($labelone ne $labeltwo) {
        return if in($labelone, @p1);
        return if in($labeltwo, @p2);
        push @p1, $labelone;
        push @p2, $labeltwo;
    }
    return;
}

#=======================================================================
# name - show_results
# purpose - give menu option for user to view results of comparison
#           between two directories
#=======================================================================
sub show_results {
    my ($opt, $dflt);

    $opt  = "";
    $dflt = 1;
    unless (@files1 and @files2) {
        if ($ext) { print "No .$ext files found.\n\n"; exit }
        else      { $dflt = 6 }
    }
    unless (%found) {
        if ($ext)  { print "No common .$ext files were found.\n\n" }
        else       { print "No common files were found.\n\n" }
        exit;
    }
    while (1) {
        unless ($first and $dflt == 6) {
            underline("Make Selection",2);
            print " 1. differences\n"
                . " 2. identical\n"
                . " 3. not found\n\n"
                . " 4. file counts\n"
                . " 5. file lists\n";
            print " 6. choose (other) subdirectory\n" unless $ext;
            print "\n"
                . " 0. quit\n\n"
                . "choose option: [$dflt] ";

            chomp($opt = <STDIN>);
        }
        $opt = $dflt if $opt =~ /^\s*$/;
        exit unless $opt;

        $first = 0;
        if ($opt > 2) { $dflt = 0 }
        else          { $dflt++ }

        if ($opt eq "1") { show_differences(); next }
        if ($opt eq "2") { show_identical();   next }
        if ($opt eq "3") { show_notfound();    next }
        if ($opt eq "4") { show_file_counts(); next }
        if ($opt eq "5") { list_files();       next }
        unless ($ext) {
            if ($opt eq "6") { choose_subdir();  return }
        }
        print "\n$opt: Invalid option; Try again.\n\n";
    }
}

#=======================================================================
# name - show differences
# purpose - print summary list of files which differ; give user option
#           to view differences in specific files using xxdiff utility
#=======================================================================
sub show_differences {
    use File::Basename;
    my ($maxB, $maxT, $fmt0, $fmt1, $fmtB, $fmtT);
    my ($num, $file1, $file2, $base1, $base2);
    my ($dflt, $sel);

    unless (%diffsBIN or %diffsTXT) {
        print "\nNo differences found.\n";
        pause();
        return;
    }

    $maxB = 0; $maxT = 0;
    foreach (values %diffsBIN) { $maxB = baselen($_) if baselen($_) > $maxB }
    foreach (values %diffsTXT) { $maxT = baselen($_) if baselen($_) > $maxT }
    $fmt0 = "%3s. %s";
    $fmt1 = "%3s. %s\n";
    $fmtB = "%3s. %-${maxB}s <=> %-s\n";
    $fmtT = "%3s. %-${maxT}s <=> %-s\n";

    # show binary file differences
    #-----------------------------
    underline("These binary files differ") if %diffsBIN;
    foreach (sort numeric keys %diffsBIN) {
        $file1 = $diffsBIN{$_};
        $base1 = basename $file1;
        $base2 = basename $different{$file1};

        if ($base1 eq $base2) { printf $fmt1, $_, $base1 }
        else                  { printf $fmtB, $_, $base1, $base2 }
    }
    pause() if %diffsBIN;
    return unless %diffsTXT;

    # show text file differences
    #---------------------------
    $num = 0;
    while (1) {
        
        # select which file to show differences
        #--------------------------------------
        underline("These text files differ");
        foreach (sort numeric keys %diffsTXT) {
            $file1 = $diffsTXT{$_};
            $base1 = basename $file1;
            $base2 = basename $different{$file1};

            if ($base1 eq $base2) { printf $fmt1, $_, $base1 }
            else                  { printf $fmtT, $_, $base1, $base2 }
        }
        $dflt = ++$num;
        $dflt = 0 unless $diffsTXT{$dflt};

        print "\n";
        printf $fmt1, "0", "previous menu";
        if (keys %diffsTXT > 1) {
            printf $fmt0, "a", "cycle thru all";
            print " (starting from $dflt)" if $dflt;
        }
        print "\n\n";
        print "Make Selection: [$dflt] ";
        chomp( $sel = <STDIN> ); $sel = $dflt unless $sel =~ /\S+/;

        return if $sel eq "0";

        # show differences for all remaining files starting with current index
        #---------------------------------------------------------------------
        if ($sel eq "a") {
            $num = 1 unless $diffsTXT{$num};
            while ($diffsTXT{$num}) {
                $file1 = $diffsTXT{$num};
                $file2 = $different{$file1};
                $base1 = basename $file1;
                $base2 = basename $file2;
                printf "showing diffs for (%d) %s\n", $num, $base1;
                system "xxdiff $diffFLGs $file1 $file2";
                $num++;
            }
            $num = -1; next;
        }

        # show selected difference
        #-------------------------
        $num = $sel;
        unless ($diffsTXT{$num}) {
            print "Selection not found: $num\n"
                . "Try again.\n";
            $num = --$dflt;
            next;
        }
        $file1 = $diffsTXT{$num};
        $file2 = $different{$file1};
        printf "showing diffs for (%d) %s\n", $num, basename $diffsTXT{$num};
        system "xxdiff $diffFLGs $file1 $file2";
    }
}

#=======================================================================
# name - show_identical
# purpose - print summary list of files which are identical in both
#           directories
#=======================================================================
sub show_identical {
    my ($max, $num, $fmt1, $fmt2);
    my ($file1, $file2);

    $max = 0;
    foreach (keys   %identical) { $max = length($_) if length($_) > $max }
    $fmt1 = "%2d. %s\n";
    $fmt2 = "%2d. %-${max}s <=> %-s\n";

    if (%identical) {
        $num = 0;
        underline("These files are identical in the two directories");
        foreach (sort keys %identical) {
            $file1 = $_;
            $file2 = $identical{$file1};
            if ($file1 eq $file2) { printf $fmt1, ++$num, $file1         }
            else                  { printf $fmt2, ++$num, $file1, $file2 }
        }
    } else {
        print "\nNo identical files were found in the two directories.\n";
    }
    pause();
}

#=======================================================================
# name - show_notfound
# purpose - print summary lists of files which exist in one directory
#           but not in the other
#=======================================================================
sub show_notfound {
    my $num;

    # list files found in dir1 but not in dir2
    #-----------------------------------------
    if (@notfound2) {
        $num = 0;
        underline("FOUND in (1)$dir1 but NOT FOUND in (2)$dir2");
        foreach ( sort @notfound2 ) { printf "%2d. %s\n", ++$num, $_ }
    } else {
        if (@files1) { print "\nAll files in $dir1 are also in $dir2\n"   }
        else         { print "\nNo files found in directory (1): $dir1\n" }
    }
    pause();

    # list files found in dir2 but not in dir1
    #-----------------------------------------
    if (@notfound1) {
        $num = 0;
        underline("FOUND in (2)$dir2 but NOT FOUND in (1)$dir1");
        foreach ( sort @notfound1 ) { printf "%2d. %s\n", ++$num, $_ }
    } else {
        if (@files2) { print "\nAll files in $dir2 are also in $dir1\n"   }
        else         { print "\nNo files found in directory (2): $dir1\n" }
    }
    pause();
}

#=======================================================================
# name - show_file_counts
# purpose - show number of files in each of the two directories being compared
#=======================================================================
sub show_file_counts {
    my ($flag, $max, $fmt);

    $flag = shift @_;

    if (length($dir1) > length($dir2)) { $max = length($dir1) }
    else                               { $max = length($dir2) }
    $fmt = "%-${max}s (%d files)\n";

    underline("Directory file counts");
    printf $fmt, $dir1, scalar(@files1);
    printf $fmt, $dir2, scalar(@files2);
    print "\n";

    pause() unless $flag;
}

#=======================================================================
# name - list_files
# purpose - display list of files in the two directories being compared
#=======================================================================
sub list_files {
    my ($fmt, $num, $base);

    $fmt = "%2d. %s\n";

    # print filenames in dir1
    #------------------------
    if (@files1) {
        underline($dir1);
        $num = 0;
        foreach (sort @files1) {
            printf $fmt, ++$num, mybase($_, "1");
        }
    } else {
        print "\nNo files in directory (1): $dir1\n";
    }
    pause();

    # print filenames in dir2
    #------------------------
    if (@files2) {
        underline($dir2);
        $num = 0;
        foreach (sort @files2) {
            printf $fmt, ++$num, mybase($_, "2");
        }
    } else {
        print "\nNo files in directory (2): $dir2\n";
    }
    pause();
}

#=======================================================================
# name - mybase
# purpose - return filename with specified directory name removed
#
# input parameters
# => $name: full name of file, including path
# => $dir: name of directory path to remove from $name
#          Note: if $dir eq "1", then set $dir to global variable, $dir1
#                if $dir eq "2", then set $dir to global variable, $dir2
#=======================================================================
sub mybase {
    my ($name, $dir, $base);

    $name = shift @_;
    $dir  = shift @_;

    if    ($dir eq "1") { $dir = $dir1 }
    elsif ($dir eq "2") { $dir = $dir2 }

    $base = $1 if $name =~ m[$dir/(.*)];
    $base = $name unless $base;

    return $base;
}

#=======================================================================
# name - choose_subdir
# purpose - choose which subdirectory to compare
#=======================================================================
sub choose_subdir {
    my ($opt, $cnt, $dflt);

    # short-circuit if no common subdirectories
    #------------------------------------------
    unless (@subdirs) {
        print "\nNo common subdirectories found.\n";
        pause();
        return;
    }

    # choose subdirectory to compare
    #-------------------------------
    $dflt = 1;
    while (1) {
        underline("Directories");
        print "$dir1\n"
            . "$dir2\n";

        $cnt = 0;
        underline("Which subdirectory do you want to compare?",2);
        foreach (@subdirs) {
            printf "%2d. %s\n", ++$cnt, $_;
            $dflt = $cnt if $_ eq "run";
        }
        print "\n";
        print " 0. previous menu\n";
        print "\n";
        print "choose: [$dflt] ";

        chomp($opt = <STDIN>); 
        $opt = $dflt unless $opt =~ /\S+/;
        return if $opt eq "0";

        unless ($subdirs[$opt-1]) {
            print "\n$opt: Invalid option; Try again.\n\n";
            next;
        }
        $subdir = $subdirs[$opt-1];
        last;
    }
}


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#                         UTILITY subroutines
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


#=======================================================================
# name - baselen
# purpose - get length of basename of a variable
#=======================================================================
sub baselen {
    use File::Basename;
    my $name = shift @_;
    return length(basename $name);
}    

#=======================================================================
# name - binary
# purpose - determine whether files are binary (i.e. non-viewable)
#=======================================================================
sub binary {
    my ($file1, $file2, $type1, $type2, $binflag);
    $file1 = shift @_;
    $file2 = shift @_;

    $binflag = 1;
    $type1 = `file $file1`;
    $type2 = `file $file2`;

    $binflag = 0 if ($type1=~/ASCII/ or $type1=~/text/ or $type1=~/source/)
        and         ($type2=~/ASCII/ or $type2=~/text/ or $type2=~/source/);
    return $binflag;
}

#=======================================================================
# name - dmget
# purpose - send job to dmget files prior to comparing them
#=======================================================================
sub dmget {
    use File::Basename;
    my (@arr, $str);
    my ($dmgetcmd, $pid);

    @arr = @_;

    # look for dmget command
    #-----------------------
    $dmgetcmd = "/x/x/x/";
    chomp($dmgetcmd = `which dmget`);
    return unless -x $dmgetcmd;

    # get list of files in archive directory
    #---------------------------------------
    $str = "";
    foreach (@arr) { $str .= " $_" if dirname($_) =~ /archive/ }
    return unless $str;

    # fork job to dmget files
    #------------------------
    print "$dmgetcmd $str\n" if $verbose;
    defined($pid = fork) or die ">> Error << while forking: $!";
    unless ($pid) {
        exec "$dmgetcmd $str";
        die ">> Error << $dmgetcmd command not executed: $!";
    }
}

#=======================================================================
# name - in
# purpose - determine whether value is in an array
#=======================================================================
sub in {
    my ($value, @array, $flag);

    $value = shift @_;
    @array = @_;

    $flag = 0;
    foreach (@array) {
        if ($_ eq $value) { $flag = 1; last }
    }
    return $flag;
}

#=======================================================================
# name - namechange
# purpose - substitute patterns into name
#
# input parameters
# => $name: name of file before name change
# => $flag: =1 (default) replace @p1 values with @p2 values
#           =2           replace @p2 values with @p1 values
#=======================================================================
sub namechange {
    use File::Basename;
    my ($name, $flag);
    my ($dir, $base);

    $name = shift @_;
    $flag = shift @_;
    $flag = 1 unless $flag;

    foreach (0..$#p1) {
        last if -e $name;
        $dir = dirname $name;
        $base = basename $name;
        if ($flag eq "1") { $base =~ s/$p1[$_]/$p2[$_]/g }
        else              { $base =~ s/$p2[$_]/$p1[$_]/g }
        $name = "$dir/$base";
    }

    return $name;
}

#=======================================================================
# name - numeric
# purpose - used with perl sort command to do a numeric sort
#=======================================================================
sub numeric {
    return  1 if $a > $b;
    return -1 if $a < $b;
}

#=======================================================================
# name - pause
# purpose - pause processing until user input is detected
#=======================================================================
sub pause {
    my $dummy;
    print "\nHit <CR> to continue ... ";
    $dummy = <STDIN>;
}

#=======================================================================
# name - queryYN
# purpose - get and return response to y/n question
#
# input parameters
# => $prompt: string to prompt for user response
# => $addr: address for variable $YN which contains a default response
#           either "y" or "n"; will be set to "n" unless it equals "y"
#
# output
# => sent back through address, $addr
#=======================================================================
sub queryYN {
    my ($prompt, $addr, $YN, $ans);

    while (1) {

        # input parameters
        #-----------------
        $prompt = shift @_;
        $addr   = shift @_; $YN = $$addr;

        # default response is "n" unless user specified "y"
        #-------------------------------------------------
        $YN = "n" unless $YN;

        # concatenate y/n choices and default to prompt
        #----------------------------------------------
        $prompt .= " (y/n) [$YN]? ";

        # print prompt and get response
        #------------------------------
        print $prompt;
        chomp($ans = lc <STDIN>); $ans = $YN unless $ans;
        $ans = "n" if $ans eq "no";
        $ans = "y" if $ans eq "yes";

        last if $ans eq "y" or $ans eq "n";
        print "Unrecognizable input.  Try again\n";
    }
    $$addr = $ans;
}

#=======================================================================
# name - cleanDirName
# purpose - remove the final slash from a directory name and find
#           the absolute path
#=======================================================================
sub cleanDirName {
    use Cwd ("abs_path");
    my $str = shift @_;
    $str = $1 if $str =~ m[^(.*[^/])/+$];
    return abs_path($str);
}

#=======================================================================
# name - underline
# purpose - prints a string to stdout and underlines it
#
# input parameters
# => string: the string to underline
# => flag: (optional); defaults to =1
#           =1: underline only with '-'
#           =2: underline and overline with '='
#=======================================================================
sub underline {
    my ($string, $flag);
    my (%pattern, $cnt);

    $string = shift @_;
    $flag = shift @_;

    $pattern{1} = "-";
    $pattern{2} = "=";

    $flag = 1 unless $flag;
    $flag = 1 unless $flag == 2;

    $cnt = length($string);
    print "\n";
    print $pattern{$flag}x$cnt."\n" if $flag == 2;
    print $string."\n";
    print $pattern{$flag}x$cnt."\n";
}

#=======================================================================
# name - Xcluded
# purpose - identify files that are to be excluded
#=======================================================================
sub Xcluded {
    my ($name, $Xclude);

    $name = shift @_;

    $Xclude = 0;
    foreach (@exclude) {
        if ($name =~ /$_/) { $Xclude = 1; last }
    }
    return $Xclude;
}

#=======================================================================
# name - usage
# purpose - print script usage information
#=======================================================================
sub usage {    
    use File::Basename;
    my $script = basename $0;
    print << "EOF";
Usage: $script dir1 dir2 [options]
where
  dir1 = first directory being compared
  dir2 = second directory being compared

options
  -bwi               ignore blanks, white space, and case when doing file diffs
  -ext extension     compare all files with this extension (recursive)
  -h(elp)            print usage information
  -p1 pattern1       pattern1 in dir1 filenames corresponds to pattern2 in dir2 filenames
  -p2 pattern2       (see -p1 note)
  -q                 quiet mode
  -rs                shortcut for "-subdir rs -ext bin"
  -run               shortcut for "-subdir run"
  -subdir name       start comparison in specified subdirectory
  -v                 verbose mode
  -X  string         filenames which include this string will be excluded
                     from the comparison
EOF
exit;
}
