#!@-PERL-@ -w
# -*- perl -*-
#
# This perl script is used to make a patch for your GnuCash
# development work. All patches should be submitted to the
# mailing list gnucash-devel@gnucash.org. For more info
# consult README.patches
#
# WARNING: By default, this script will checkout an entire
# up to date copy of the source tree in ../tmp/gnucash/.
#
# In order to prevent patches which reverse recent changes
# made in CVS, make sure to "cvs update" in both
# directories before running make-gnucash-patch.
#
# This script requires the programs 'makepatch', 'gzip',
# a 'diff' work-a-like, and 'uuencode'.
#
# Author: Dave Peticolas <dave@krondo.com>

use strict;

use File::Basename;
use Getopt::Long;

$::ask_description = 1;
$::should_uuencode = 1;
$::diffcmd = "diff -up";

die "This utility has not been updated to use SVN.  Sorry, just use diff(1).";

my $rcfile = $ENV{"HOME"} . "/.gnucash-patch.rc";

if (-f $rcfile) {
  require $rcfile;
}

###########################################################
# This section must be configured for your own setup.     #
###########################################################

# The directory with the original gnucash sources
my $old = undef;

chomp(my $cwd = `pwd`);

my ($new, $gnc_home) = fileparse($cwd);

###########################################################
# This section should not need to be modified.            #
###########################################################

# Allow the user to override the defaults with evnt vars.

if($ENV{'GNC_MAKEPATCH_OLD_DIR'}) {
  $old = $ENV{'GNC_MAKEPATCH_OLD_DIR'};
}

if($ENV{'GNC_MAKEPATCH_NEW_DIR'}) {
  $new = $ENV{'GNC_MAKEPATCH_NEW_DIR'};
}

if($ENV{'GNC_MAKEPATCH_HOME_DIR'}) {
  $gnc_home = $ENV{'GNC_MAKEPATCH_HOME_DIR'};
}

###########################################################
# Make sure makepatch exists before going on              #
###########################################################

open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, "> /dev/null") || die "Can't redirect stdout";
open(STDERR, "> /dev/null") || die "Can't redirect stderr";
my $test = system('makepatch', "-help");
close(STDOUT);
close(STDERR);
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
close(OLDOUT);
close(OLDERR);

if($test == -1) {
  print "No makepatch installed.  Exiting\n";
  exit(10);
}


###########################################################
# Now, check if anything specified on command line        #
###########################################################

my $result;
my $help;
my $zip = 1;     # defaults to on
my $diffname = undef;
my $ignorename = undef;
my @filename = ();
my $manifest = undef;

$result = GetOptions("old=s" => \$old, "new=s" => \$new, 
            "prefix=s" => \$gnc_home, "help" => \$help,
            "file=s" => \@filename, "ask!" => \$::ask_description,
            "diff=s" => \$diffname, "uuencode!" => \$::should_uuencode,
            "diffcmd!" => \$::diffcmd, "zip!" => \$zip, 
            "ignore=s" => \$ignorename, "manifest=s" => \$manifest);

if ($help or $result == 0) {
  printf "Help information\n\n";
  printf "make-gnucash-patch accepts the following arguments:\n";
  printf "--old \"olddir\"     Directory of \"pristine\" copy\n";
  printf "--new \"newdir\"     Directory with modified copy\n";
  printf "--prefex \"homedir\" Full path of parent directory\n";
  printf "--diff \"diffname\"  Output name for diff file\n";
  printf "--diffcmd \"cmd\"    Command to compare two files\n";
  printf "--file \"filename\"  Make patch for filename ONLY\n";
  printf "--manifest \"file\"  Use file as a manifest file\n";
  printf "--ignore \"igname\"  File containing file matches to ignore\n";
  printf "--(no)uuencode     Enable or disable uuencoded output\n";
  printf "                   (Defaults to enabled)\n";
  printf "--(no)zip          Enable or disable gzipped output\n";
  printf "                   (Defaults to enabled)\n";
  printf "--(no)ask          Enable or disable prompting for description\n";
  printf "                   (Defaults to enabled)\n";
  printf "and of course:\n";
  printf "--help             Displays this text\n";
  printf "\nAll options can be abbreviated to their shortest unique\n";
  printf "  form: --o, --ne, --p, --d, --diffc, --f, --m, --i,\n";
  printf "        --u/--nou, --z/--noz, --a/--noa, and --h\n";
  printf "\n";
  exit 1;
}

# if explicit filename given, build required MANIFEST file

@filename = split(/,/,join(',',@filename));

if (@filename) {
  open (FN, ">$gnc_home/tmp.MANIFEST") or die "Couldn't create MANIFEST file";
  printf (FN "# Temporary manifest file for make-gnucash-patch -f option\n");
  printf (FN "\n");
  foreach my $part (@filename) {
    printf (FN "%s\n", $part);
  }
  close (FN);
}

# Switch to the home directory
print "Changing directory to $gnc_home\n";
chdir $gnc_home or die "Can't cd!\n";

if (not defined($old)) {
  if (not -f "$new/CVS/Root") {
    print "Source not checked out of CVS and no \$old set.  Quitting...\n";
    exit(1);
  }
  if (not -d "tmp") {
    mkdir "tmp", 0755;
  }
  chdir "tmp";
  system("cvs -d `cat ../$new/CVS/Root` co gnucash");
  chdir "..";
  $old = "tmp/gnucash";
}

chdir $gnc_home . "/" . $new or die "Can't cd!\n";
# Start out with our basic makepatch arguments
my @args = ('-verbose', '-exclude-vc');

# Push the diff command value
push(@args, '-diff',  "$::diffcmd");

if (not $::ask_description) {
  push(@args, '-description', '');
}

# If -f options given, use generated manifest file
# otherwise, add exclusions and proceed as normal
if (@filename) {
  push(@args, '-manifest', "$gnc_home/tmp.MANIFEST");
}
elsif (defined($manifest)) {
  if (not $manifest =~ m#/#) {
    $manifest = "$cwd/$manifest";
  }
  push(@args, '-manifest', "$manifest");
}
else {
  # Add in the exclude patterns from the __DATA__ section
  push_exclusions(\@args);
}

sub push_exclusions {
  my $args = shift;
  foreach my $pat (<DATA>) {
    chomp($pat);
    push(@{$args}, '-exclude', $pat) if $pat;
  }
  if (defined ($ignorename)) {
    $ignorename = "../" . $ignorename;
    if (-e $ignorename) {
      open (IG, $ignorename) or die "Couldn't open $ignorename";
      foreach my $igf (<IG>) {
        chomp ($igf);
        push(@{$args}, '-exclude', $igf) if $igf;
      }
      close (IG);
    }
  }
  my @cvsignores = `find . -name '.cvsignore'`;
  foreach my $one_ignore (@cvsignores) {
    my ($name, $path) = fileparse($one_ignore);
    open (IG, $one_ignore);
    foreach my $fl (<IG>) {
      chomp $fl;
      $path =~ s/^\.\///;
      push(@{$args}, '-exclude', $path . $fl) if $fl;
    }
    close (IG);
  }
}
# Add the from and to directories for makepatch
push(@args, $old, $new);
print "Arguments are: " . join("; ", @args) . "\n";

chdir $gnc_home or die "Can't cd!\n";

# Erase the old files
#unlink('gnc.diff', 'gnucash.diff.gz', 'gnucash.diff.gz.uue');

if (not -d "diffs") {
  mkdir "diffs", 0755;
}

my $outfilename;

if (not defined($diffname)) {
  my $date = `date '+%Y%m%d-%H%M%S'`;
  chomp($date);
  my $who = `whoami`;
  chomp($who);

  $outfilename = "gnucash-$date-$who.diff";
}
else {
  $outfilename = $diffname;
}

# Invoke makepatch with standard out redirected to 'gnucash.diff'
open(OLDOUT, ">&STDOUT");
open(STDOUT, "> diffs/$outfilename") || die "Can't redirect stdout";
system('makepatch', @args);
close(STDOUT);
open(STDOUT, ">&OLDOUT");
close(OLDOUT);
print "makepatch done\n";

# Compress the patch if required
if ($zip) {
  if (-f "diffs/$outfilename") {
    system("gzip", "-9vf", "diffs/$outfilename");
  }
}

# UU encode the patch if required

# if $zip is true, then
# 'gnucash.diff.gz.uue' is the file you send.
if ($zip and -f "diffs/$outfilename.gz" and $::should_uuencode) {
  system("uuencode diffs/$outfilename.gz $outfilename.gz > diffs/$outfilename.gz.uue");
  print "diffs/$outfilename.gz.uue\n";
}
else {
  if (not $zip and -f "diffs/$outfilename" and $::should_uuencode) {
    system("uuencode diffs/$outfilename $outfilename > diffs/$outfilename.uue");
    print "diffs/$outfilename.uue\n";
  }
}


exit(0);

__DATA__

#*#
*.P
*.pp 
*.a
*.bak
*.bin
*.diff
*.diffs
*.gmo
*.la
*.lai
*.lo
*.loT
*.log
*.mo
*.moc
*.o
*.orig
*.ignmgp
*.patch
*.rej
*.tar.gz
*.wrap
*.xac.*.xac
*~
.#*
doc/gnc-prices.1
doc/gnucash.1