#!/usr/bin/perl
# $Id: kcvsdiff,v 1.2 1998/01/30 20:11:51 deas Exp $
# 
# Create and/or mail diffs for kupgrade
# 
# Author: Andreas Steffan <deas@rrz.uni-hamburg.de>

use POSIX;
use Getopt::Std;
getopts('d:DshMm:of:', \%opts);

&get_cfg;
&get_status;
if ($opts{h} || (keys %opts)==()) {
  &usage;
  exit 0;
}
if ($opts{o} || (!$opts{M} && !$opts{D})) {
  $SHOW_PATCH=1;
}
if ($opts{m}) {
  $MODULE=$opts{m};
  unless ($KCVS_STATUS{$MODULE}) {
    if ($opts{f}) {
      unless (-e "$opts{f}") {
	print STDERR "No reference file $opts{f}\n";
	exit 1;
      } else {
	$KCVS_STATUS{$MODULE}=(stat $opts{f})[9];
      }
      exit 1;
    }
    &get_date;
    &write_status;
    exit 0;
  }
}
if ($opts{s}) {
  &show_status;
  exit 0;
}
if ($opts{M}) {
  $TO_DISK=1;
  $MAIL=1;
}
if ($opts{D}) {
  $TO_DISK=1;
}
if ($opts{d}) {
  $CVSROOT=$opts{d};
}
if ($MODULE) {
  if ($MAIL && !$RCPT{$MODULE}) {
    print STDERR "No recipients found for module $MODULE\n";
    exit 1;
  }
  &print_patch;
}

sub print_patch { # print patches to mail/STDOUT/disk
  local $ttime=time;
  local @cvs_sdate=localtime ($KCVS_STATUS{$MODULE});
  local @cvs_tdate=localtime ($ttime);
  local $cvs_sdate=localtime($KCVS_STATUS{$MODULE});
  local $cvs_tdate=localtime("$ttime");
# Current workaround, since POSIX::strftime currently dumps core on my box
# To be fixed
  local $diff_tdate=sprintf ("%.2d%.2d%.2d",$cvs_tdate[5],$cvs_tdate[4]+1,$cvs_tdate[3]);
  local $diff_sdate=sprintf ("%.2d%.2d%.2d",$cvs_sdate[5],$cvs_sdate[4]+1,$cvs_sdate[3]);
  local @DIFF,$SUCCESS;
# Check if the dates make sense
  if ($diff_sdate==$diff_tdate) {
    print STDERR "Source date $diff_sdate is equal to Target date $diff_tdate\n";
    exit 1;
  }
  if ($diff_sdate > $diff_tdate) {
    print STDERR "Sorry, downgrading from $diff_sdate to $diff_tdate not supported. :)\n";
    exit 1;
  }
# Assemble CVS command
  local $patch="$MODULE-$diff_sdate-$diff_tdate.diff.gz";
  local $rdiff_opts="-- -u -D \"$cvs_sdate\" -D \"$cvs_tdate\" $MODULE";
  if ($CVSROOT) {
    $cvs_opts="-d$CVSROOT";
  }
  if ($cvs_opts) {
    $cvs_cmd="cvs $cvs_opts rdiff $rdiff_opts";
  } else {
    $cvs_cmd="cvs rdiff $rdiff_opts";
  }
  open (PATCH,"$cvs_cmd |");
  while (<PATCH>) { # Read the patch into memory
    print if $SHOW_PATCH;
    push @DIFF,$_ if ($MAIL || $TO_DISK);
  }
  close PATCH;
  if ($?!=0) {
    print STDERR "Error running $cvs_cmd\n";
    exit 1;
  }
  if ($TO_DISK) { # Write the patch to disk
    open (DIFF,"| gzip -c >$ENV{HOME}/kcvsdiff/patches/$patch");
    print DIFF @DIFF;
    close DIFF;
    if ($?!=0) {
      print STDERR "Error writing patch $ENV{HOME}/kcvsdiff/patches/$patch\n";
      exit 1;
    } else {
      &print_log("$ENV{HOME}/kcvsdiff/patches/$patch written");
      $SUCCESS=1;
    }
  }
  if ($MAIL) { # Mail the patch to the recipients
    open (MAIL,"| gzip | uuencode $patch | mail -s \"kcvsdiff: $patch\" $RCPT{$MODULE}");
    print MAIL @DIFF;
    close MAIL;
    if ($?!=0) {
      print STDERR "Error printing to gzip | uuencode $patch | mail -s \"kcvsdiff: $patch\" $RCPT{$MODULE}\n";
      exit 1;
    } else {
      &print_log("$patch sent to $RCPT{$MODULE}");
      $SUCCESS=1;
    }
  }
  if ($SUCCESS) { # Update status file if patch was written and/or mailed
    $KCVS_STATUS{$MODULE}=$ttime;
    &write_status;
  }
}

sub print_log { # Write logfile entry
  local $date=localtime(time);
  print "$_[0]\n";
  unless (open(LOG,">>$ENV{HOME}/kcvsdiff/kcvsdiff.log")) {
    print STDERR "Cannot write to logfile $ENV{HOME}/kcvsdiff/kvsdiff.log\n";
  }
  print LOG "$date $_[0]\n";
  close LOG;
}

sub show_status { # Show times to diff against
  local $module,$date,@date,$ndate;
  foreach $module (keys %KCVS_STATUS) {
    $date=localtime($KCVS_STATUS{$module});
    @date=localtime($KCVS_STATUS{$module});
    $ndate=sprintf ("%.2d%.2d%.2d",$date[5],$date[4]+1,$date[3]);
    print "$module\t$date [$ndate]\n";
  }
}

sub get_date { # Get date interactively when adding a new module
  local $value,$val,%date;
  print "Enter all values as two digits\n";
  foreach $value (year,month,day,hour,min,sec) {
    while ($val!~/\d{2}/) {
      print "$value : ";
      $val=<STDIN>;
    }
    chomp $val;
    $date{$value}=$val;
    undef $val;
  }
  $KCVS_STATUS{$MODULE}=POSIX::mktime($date{sec},$date{min},$date{hour},$date{day},$date{month}-1,$date{year});
}

sub get_cfg { # Read the configuration file
  unless (-e "$ENV{HOME}/kcvsdiff") {
    &create_sample;
    return 1;
  }
  unless(open (CFG,"$ENV{HOME}/kcvsdiff/kcvsdiff.cfg")) {
    print "No configuration file $ENV{HOME}/kcvsdiff/kcvsdiff.cfg\n";
    return 1;
  }
  while (<CFG>) {
    chomp;
    s/ |\t//;
    unless (/^(\w+):(.+)/) {
      next;
    }
    $RCPT{$1}=$2;
  }
  close CFG;
}

sub create_sample { # Create sample configuration file
    print STDERR "\nMissing directory $ENV{HOME}/kcvsdiff. Creating it\n";
    mkdir "$ENV{HOME}/kcvsdiff",0755;
    mkdir "$ENV{HOME}/kcvsdiff/patches",0755;
    open (SAMPLE,">$ENV{HOME}/kcvsdiff/kcvsdiff.cfg");
    print SAMPLE "# This configuration file contains two fields separated by colons\n";
    print SAMPLE "# The first field is the name of the CVS-module to track\n";
    print SAMPLE "# The second are the recipients email-adresses separated by comma\n";
    print SAMPLE "# Lines have to terminated by CR\n";
    print SAMPLE "\nyour_cvs_module:rcpt1\@foo.bar.org,rcpt2\@org.bar.de\n";
    close SAMPLE;
}

sub write_status { # Write status file after creating the diff
  local $module;
  unless (open (STATUS,">$ENV{HOME}/kcvsdiff/kcvsdiff.status")) {
    print STDERR "Cannot open $ENV{HOME}/kcvsdiff/kcvsdiff.status for writing\n";
    exit 1;
  }
  foreach $module (keys %KCVS_STATUS) {
    print STATUS "$module:$KCVS_STATUS{$module}\n";
  }
  close STATUS;
}

sub get_status { # Get times to diff against
  unless (open (STATUS,"$ENV{HOME}/kcvsdiff/kcvsdiff.status")) {
#    print "No status file $ENV{HOME}/kcvsdiff/kcvsdiff.status\n";
    return;
  }
  while (<STATUS>) {
    unless (/^(\w+):(\d{9})$/) {
      chomp;
      print STDERR "Invalid entry $_ in $ENV{HOME}/kcvsdiff/kcvsdiff.status\n";
    } else {
      $KCVS_STATUS{$1}=$2;
    }
  }
  close STATUS;
}

sub usage { # Print out usage

print <<EOF

kcvsdiff [-MDosh] [-f ref_file] [-m module]

Version: $Revision: 1.2 $\

-M                 Mail gzipped diff to recients specified
                   in $ENV{HOME}/kcvsdiff/kcvsdiff.cfg
                   Implies -D.
-m module          Work on CVS-module module.
                   Status-file entry will be created if not existent.
-s                 Show timestamps for modules
                   in $ENV{HOME}/kcvsdiff/kcvsdiff.status
-o                 Show diff on STDOUT. Default, if neiter -M nor -D
                   is given.
-f ref_file        Use mtime of ref_file for initial module entry
                   in $ENV{HOME}/kcvsdiff/kcvsdiff.status
-D                 Put the patch in $ENV{HOME}/kcvsdiff/patches
-d cvs_root_dir    Use cvs_root_dir. Default is \$CVSROOT.                
-h                 Show this help

EOF
}
