package UpdateTopics;


sub init_config { 
   my $config = shift;

use strict;

}# END of init_config

use File::Find;
use File::Copy;
use File::Basename;
use Text::Diff;

# Try to upgrade an installation's TWikiTopics using the rcs info in it.


sub UpdateTopics 
{ 
   my $config = shift;

    $config->{UpdateTopics}->{CurrentDataDir} = shift or die "UpdateTopics not provided with existing data directory!\n";

    $config->{UpdateTopics}->{NewReleaseDataDir} = shift or die "UpdateTopics not provided with new data directory!\n";

    $config->{UpdateTopics}->{DestinationDataDir} = (shift or "$config->{UpdateTopics}->{BaseDir}/newData");

    my $whoCares = `which rcsdiff`;   # we should use File::Which to do this, except that would mean
                                      # getting yet another .pm into lib, which seems like hard work?
    ($? >> 8 == 0) or die "Uh-oh - couldn't see an rcs executable on your path!  I really need one of those!\n";

    $whoCares = `which patch`;

    ($? >> 8 == 0) or die "Uh-oh - couldn't see a patch executable on your path!  I really need one of those!\n";

    $config->{UpdateTopics}->{BaseDir} = `pwd`;
    chomp ($config->{UpdateTopics}->{BaseDir});

    $config->{UpdateTopics}->{TempDir} = "$config->{UpdateTopics}->{BaseDir}/tmp";

    while (-d $config->{UpdateTopics}->{TempDir} ) { $config->{UpdateTopics}->{TempDir} .= 'p' }   # we want our own previously non-existing directory!

    mkdir $config->{UpdateTopics}->{TempDir} or die "Uhoh - couldn't make a temporary directory called $config->{UpdateTopics}->{TempDir}: $!\n";

#Set if you want to see the debug output
#$config->{UpdateTopics}->{debug} = "yes";

    if ($config->{UpdateTopics}->{debug}) {print "$config->{UpdateTopics}->{CurrentDataDir}, $config->{UpdateTopics}->{NewReleaseDataDir}\n"; }

    if ((! -d $config->{UpdateTopics}->{CurrentDataDir} ) || (! -d $config->{UpdateTopics}->{NewReleaseDataDir})) {
	print "\nUsage: UpdateTopics <CurrentDataDir> <NewReleaseDataDir> [<DestinationDataDir]>\n";
	exit;
    }

    print "\n";
    print "\t...new upgraded data will be put in $config->{UpdateTopics}->{DestinationDataDir}\n";
    print "\t   there will be no changes made to either the source data directory or $config->{UpdateTopics}->{NewReleaseDataDir}.\n\n"; 
    print "\t This progam will attempt to use the rcs versioning information to upgrade the\n";
    print "\t   contents of your distributed topics in $config->{UpdateTopics}->{CurrentDataDir} to the content in $config->{UpdateTopics}->{NewReleaseDataDir}.\n\n";
    print "Output:\n";
    print "\tFor each file that has no versioning information in your existing twiki a _v_ will be printed\n";
    print "\tFor each file that has no changes from the previous release a _c_ will be printed\n";
    print "\tFor each file that has no changes made in your existing release, a _u_ will be printed\n";
    print "\tFor each file where no commonality could be found, your existing one is used, and a _C_ will be printed\n";
    print "\tFor each file that has changes and a patch is generated a _p_ will be printed\n";
    print "\tFor each file that is new in the NewReleaseDataDir a _+_ will be printed\n";
    print "\t When the script has attempted to patch the $config->{UpdateTopics}->{NewReleaseDataDir}, 
\t *.rej files will contain the failed merges\n";
    print "\t although many of these rejected chages will be discardable, 
\t please check them to see if your configuration is still ok\n\n";

    sussoutDefaultWebTopics( $config ) ;

    mkdir $config->{UpdateTopics}->{DestinationDataDir};

#redirect stderr into a file (rcs dumps out heaps of info)

    $config->{UpdateTopics}->{RcsLogFile} = $config->{UpdateTopics}->{BaseDir}."/rcs.log";

    unlink($config->{UpdateTopics}->{RcsLogFile});  # let's have just the messages from this session!

    open(PATCH, "> $config->{UpdateTopics}->{DestinationDataDir}/patchTopics");
    
    print "\n\n ...checking existing files from $config->{UpdateTopics}->{CurrentDataDir}\n";
#TODO: need to find a way to detect non-Web directories so we don't make a mess of them..
# (should i just ignore Dirs without any ,v files?) - i can't upgrade tehm anyway..
#upgrade templates..?

    my %findOptions;

    $findOptions{'wanted'} = \&getRLog;
    $findOptions{'follow_fast'} = 1;   # surely the user hasn't put loops of links etc in their data!?

    find(\%findOptions, $config->{UpdateTopics}->{CurrentDataDir});

    close(PATCH);

#do a find through $config->{UpdateTopics}->{NewReleaseDataDir} and copy all missing files & dirs
    print "\n\n ... checking for new files in $config->{UpdateTopics}->{NewReleaseDataDir}";
    find(\&copyNewTopics, $config->{UpdateTopics}->{NewReleaseDataDir});
    
#run `patch patchTopics` in $config->{UpdateTopics}->{DestinationDataDir}
    print "\nPatching topics (manually check the rejected patch (.rej) files)";
    chdir($config->{UpdateTopics}->{DestinationDataDir});
    `patch -p0 < patchTopics > patch.log`;
#TODO: examing the .rej files to remove the ones that have already been applied
    find(\&listRejects, ".");
#TODO: run `ci` in $config->{UpdateTopics}->{DestinationDataDir}
    
    print "\n\n";
    
    rmdir($config->{UpdateTopics}->{TempDir});
    chdir($config->{UpdateTopics}->{BaseDir});  # seems the kind thing to do :-)
}
    
# ============================================
sub listRejects
{ 
   my $config = shift;

    my ( $filename ) = @_;
    
    $filename = $File::Find::name;

    if ($filename =~ /.rej$/ ) {
        print "\nPatch rejected: $filename";
    }
}

# ============================================
sub copyNewTopics
{ 
   my $config = shift;

    my ( $filename ) = $File::Find::name;

    my $destinationFilename = $filename;
    $destinationFilename =~ s/$config->{UpdateTopics}->{NewReleaseDataDir}/$config->{UpdateTopics}->{DestinationDataDir}/g;

# Sven had these commeted out, so I've left them here commented out.
#    return if $filename =~ /,v$/;
#    return if $filename =~ /.lock$/;
#    return if $filename =~ /~$/;

    if ( -d $filename) {
        print "\nprocessing directory $filename";
	if ( !-d $destinationFilename ) {
	    print " (creating $destinationFilename)";
	    mkdir($destinationFilename);
	}
	print "\n";
        return;
    }
    
    if (! -e $destinationFilename ) { 
        print "\nadding $filename (new in this release)" if ($config->{UpdateTopics}->{debug});
        print "+" if (!$config->{UpdateTopics}->{debug});
        copy( $filename, $destinationFilename);
    }
    
}

# ============================================
sub getRLog
{ 
   my $config = shift;

    my ( $filename ) = $File::Find::name;

# (see above)
#    my ( $filename ) = @_;
#    $filename = $config->{UpdateTopics}->{BaseDir}."/".$File::Find::name if (! $filename );

    my ( $newFilename ) = $filename;
    if (!$filename =~ /^$config->{UpdateTopics}->{CurrentDataDir}/)
    {
	die "getRLog found $filename that appears not to be in $config->{UpdateTopics}->{CurrentDataDir} tree! That's not supposed to happen: sorry!\n";
    }
	
    $newFilename =~ s/$config->{UpdateTopics}->{CurrentDataDir}/$config->{UpdateTopics}->{NewReleaseDataDir}/g;
    print "\n$filename -> $newFilename : "  if ( $config->{UpdateTopics}->{debug});

    my $destinationFilename = $filename;
    $destinationFilename =~ s/$config->{UpdateTopics}->{CurrentDataDir}/$config->{UpdateTopics}->{DestinationDataDir}/g;

    if ($filename =~ /,v$/ or $filename =~ /.lock$/ or $filename =~ /~$/) {
	print "skipping\n" if $config->{UpdateTopics}->{debug};
	return;
    }

    if ( -d $filename ) {
	print "\nprocessing directory (creating $destinationFilename)\n";
        mkdir($destinationFilename);
        return;
    }
    
#    if ( isFromDefaultWeb( $config, $filename) )
#    {
#        $newFilename =~ s|^(.*)/[^/]*/([^/]*)|$1/_default/$2|g;
#        print "\n$filename appears to have been generated from from _default - merging with $newFilename from the new distribution" if ($config->{UpdateTopics}->{debug});
#    }
    
    if (! -e $filename.",v" )
    {
        if ( $filename =~ /.txt$/ ) {
	    # here we defer making an RCS file for this file to someone else :-)
	    # (probably the process that checks all the new wiki files back in)
            print "\nWarning: $filename does not have any rcs information" if ($config->{UpdateTopics}->{debug});
            print "v" if (! $config->{UpdateTopics}->{debug});
        }
        copy( $filename, $destinationFilename);
        return;
    }

    # make it easy for debugging to turn on or off the business about
    # checking in the existing files before rcsdiffing them
    # this is necessary, because you have to make changes in two places to make
    # this switch, and if you forget the second one you're gunna delete lots of files
    # you wanted to keep!

    my $doCiCo = 1;
#   my $doCiCo = 0;

    # Now - the main business: if we're looking at a file that has a new version in
    # the new distribution then we have to try merging etc...

    if ( -e $newFilename ) { 
        # file that may need upgrading

	my $workingFilename;

	if (!$doCiCo)
	{
	    $workingFilename = $filename;
	}
	else
	{
	    $workingFilename = "$config->{UpdateTopics}->{TempDir}/". basename($filename);
	    
	    print "Working file: $workingFilename\n" if ($config->{UpdateTopics}->{debug});
	    
	    copy ( $filename, $workingFilename)
		or die "Couldn't make copy of $filename at $workingFilename: $!\n";

	    copy ( "$filename,v", "$workingFilename,v")
		or die "Couldn't make copy of $filename,v at $workingFilename,v: $!\n";
	    
	    # This procedure copied from UI::Manage.pm
	    # could be perhaps performed in less steps, but who cares...
	    # break lock
	    system("rcs -q -u -M $workingFilename 2>>$config->{UpdateTopics}->{RcsLogFile}");
	    # relock
	    system("rcs -q -l $workingFilename 2>>$config->{UpdateTopics}->{RcsLogFile}");
	    # check outstanding changes in (note that -t- option should never be used, but it's there for completeness,
	    #  and since it was in Manage.pm)
	    system("ci -u -mUpdateTopics -t-missing_v $workingFilename 2>>RcsLogFile");
	}

        my $highestCommonRevision = findHighestCommonRevision( $config,  $workingFilename, $newFilename);

	# is it the final version of $filename? 
	# (in which case:
#TODO: what about manually updated files?

        if ( $highestCommonRevision =~ /\d*\.\d*/ ) 
	{
            my $diff = doDiffToHead( $config,  $workingFilename, $highestCommonRevision );

	    $diff = removeVersionChangeDiff( $config, $diff);
            patchFile( $config,  $filename, $destinationFilename, $diff );

            print "\npatching $newFilename from $filename ($highestCommonRevision)" if ($config->{UpdateTopics}->{debug});
            print "\n$newFilename: p\n" if (!$config->{UpdateTopics}->{debug});
            copy( $newFilename, $destinationFilename);
            copy( $newFilename.",v", $destinationFilename.",v");
        } elsif ($highestCommonRevision eq "head" ) {
	    # This uses the existing file rather than the new one, in case they manually
	    # changed the exisiting one without using RCS.
            print "\nhighest common revision is final revision in oldTopic (using new Version)" if ($config->{UpdateTopics}->{debug});
            print "u" if (!$config->{UpdateTopics}->{debug});
            copy( $newFilename, $destinationFilename);
            copy( $newFilename.",v", $destinationFilename.",v");
        } else {
            #no common versions - this might be a user created file, 
            #or a manual attempt at creating a topic off twiki.org?raw=on
#TODO: do something nicer about this.. I think i need to do lots of diffs 
            #to see if there is any commonality
            print "\nWarning: copying $filename (no common versions)" if ($config->{UpdateTopics}->{debug});
            print "\nNo common versions for $filename: C\n" if (!$config->{UpdateTopics}->{debug});
            copy( $filename, $destinationFilename);
            copy( $filename.",v", $destinationFilename.",v");
        }

	if ( $doCiCo )
	{
	    unlink ($workingFilename, "$workingFilename,v") or
		warn "Couldn't remove temporary files $workingFilename, $workingFilename,v: $! Could be trouble ahead...\n";
	}

    } else {
        #new file created by users
#TODO: this will include topics copied using ManagingWebs (createWeb)
        print "\ncopying $filename (new user's file)" if ($config->{UpdateTopics}->{debug});
        print "c" if (!$config->{UpdateTopics}->{debug});
        copy( $filename, $destinationFilename);
        copy( $filename.",v", $destinationFilename.",v");
    }
}

# ==============================================
sub isFromDefaultWeb
{ 
   my $config = shift;

    my ($filename) = @_;

    my $topic = basename($filename);
    return $topic if grep(/^$topic$/, @{$config->{UpdateTopics}->{DefaultWebTopics}});
}

sub sussoutDefaultWebTopics
{ 
   my $config = shift;

    opendir(DEFAULTWEB, './data/_default') or die "Yikes - couldn't open ./data/_default: $! ... not safe to proceed!\n";
    @{$config->{UpdateTopics}->{DefaultWebTopics}} = grep(/.txt$/, readdir(DEFAULTWEB));
    if ($config->{UpdateTopics}->{debug}) 
    {
	print "_default topics in new distro: @{$config->{UpdateTopics}->{DefaultWebTopics}}\n";
    }
}

# ==============================================
sub doDiffToHead
{ 
   my $config = shift;

    my ( $filename, $highestCommonRevision ) = @_;
   
#    print "$highestCommonRevision to ".getHeadRevisionNumber( $config, $filename)."\n";
#    print "\n----------------\n".getRevision( $config, $filename, $highestCommonRevision);
#     print "\n----------------\n".getRevision( $config, $filename, getHeadRevisionNumber( $config, $filename)) ;
#    return diff ( getRevision( $config, $filename, $highestCommonRevision), getRevision($filename, getHeadRevisionNumber( $config, $filename)) );

    my $cmd = "rcsdiff -r".$highestCommonRevision." -r".getHeadRevisionNumber( $config, $filename)." $filename";
    print "\n----------------\n".$cmd  if ($config->{UpdateTopics}->{debug});
    my $diffs =  `$cmd 2>>$config->{UpdateTopics}->{RcsLogFile}`;
    return $diffs;
}

# ==============================================
sub patchFile
{ 
   my $config = shift;

    my ( $oldFilename, $destinationFilename, $diff ) = @_;


    # Here's where we do a total hack to get WIKIWEBLIST right.  
    # we have to check every stinkin line of diff just to intercept this one :-(

    my @diff;

    @diff = split(/\n/, $diff);

    my $i;

    for($i = 0; $i < @diff ; $i++)
    {
	if ($diff[$i] =~ m/<\s*\* Set WIKIWEBLIST = /)
	{
	    # come to mama
	    # this had better be the value in the distribution!...
	    $diff[$i] = '< 		* Set WIKIWEBLIST = [[%MAINWEB%.%HOMETOPIC%][%MAINWEB%]] %SEP% [[%TWIKIWEB%.%HOMETOPIC%][%TWIKIWEB%]] %SEP% [[Sandbox.%HOMETOPIC%][Sandbox]]
';
	    last;
	}
    }
    
    $diff = join("\n", @diff);

    # this looks odd: it's just telling patch to apply the patch to $destinationFilename
    # perhaps only one file name line would do, but better safe than sorry!    
    # (diffs always seem to have two lines)
    print(PATCH "--- $destinationFilename\n");
    print(PATCH "--- $destinationFilename\n");

    print(PATCH "$diff\n");

#    print(PATCH, "");
    
    #patch ($newFilename, $diff);
# and then do an rcs ci (check-in)
}

# ==============================================
sub getHeadRevisionNumber
{ 
   my $config = shift;

    my ( $filename ) = @_;
    
    my ( $cmd ) = "rlog ".$filename.",v";

    my $line;

    my @response = `$cmd 2>>$config->{UpdateTopics}->{RcsLogFile}`;
    foreach $line (@response) {
        next unless $line =~ /^head: (\d*\.\d*)/;
        return $1;
    }
    return "";    
}

# ==============================================
#returns, as a string, the highest revision number common to both files
#Note: we return nothing if the highestcommon verison is also the last version of $filename
#TODO: are teh rcs versions always 1.xxx ? if not, how do we know?
sub findHighestCommonRevision 
{ 
   my $config = shift;

    my ( $filename, $newFilename) = @_;
    
    my $rev = 1;
    my $commonRev = "";

    my $oldContent = "qwer";
    my $newContent = "qwer";
    while ( ( $oldContent ne "" ) & ($newContent ne "") ) {
        print "\ncomparing $filename and $newFilename revision 1.$rev " if ($config->{UpdateTopics}->{debug});
        $oldContent = getRevision( $config,  $filename, "1.".$rev);
        $newContent = getRevision( $config,  $newFilename, "1.".$rev);
        if ( ( $oldContent ne "" ) & ($newContent ne "") ) {
            my $diffs = diff( \$oldContent, \$newContent, {STYLE => "Unified"} );
#            print "\n-----------------------|".$diffs."|-------------------\n";
#            print "\n-------------------[".$oldContent."]----|".$diffs."|-------[".$newContent."]--------------\n";
            if ( $diffs eq "" ) {
                #same!!
                $commonRev = "1.".$rev;
            }
        }
        $rev = $rev + 1;
    }

    print "\nlastCommon = $commonRev (head = ".getHeadRevisionNumber( $config,  $filename).")" if ($config->{UpdateTopics}->{debug});
    
    if ( $commonRev eq getHeadRevisionNumber( $config,  $filename) ) {
        return "head";
    }
    
    return $commonRev;
}

# ==============================================
#returns an empty string if the version does not exist
sub getRevision
{ 
   my $config = shift;

    my ( $filename, $rev ) = @_;

# use rlog to test if the revision exists..
    my ( $cmd ) = "rlog -r".$rev." ".$filename;

#print $cmd."\n";
    my @response = `$cmd 2>>$config->{UpdateTopics}->{RcsLogFile}`;

    my $revision;
    my $line;
    foreach $line (@response) {
        next unless $line =~ /^revision (\d*\.\d*)/;
        $revision = $1;
    }

    my $content = "";

    if ( $revision and ($revision eq $rev) ) {
        $cmd = "co -p".$rev." ".$filename;
        $content = `$cmd 2>>$config->{UpdateTopics}->{RcsLogFile}`;
    }

    return $content;
}

# $diff is assumed to contain the diff between two similar TWiki topics
# TWiki topics should, as a rule, differ in the first line with respect to
# their version number.   This routine gets rid of that component of the diff.
# It could be more rigorous (like testing if the 1c1 change relates to %META).
# The diff is assumed not to contain the preamble.

sub removeVersionChangeDiff
{ 
   my $config = shift;

    my ($diff) = @_;

    my @diff = split( /\n/, $diff);

    if ($diff[0] eq '1c1')
    {
	splice(@diff, 0, 4);
    }

    $diff = join "\n", @diff;

#    print "rVCD returning: \n$diff\n";

    return $diff;
}

use strict;

use File::Find;
use File::Copy;
use Text::Diff;

# Try to upgrade an installation's TWikiTopics using the rcs info in it.


sub UpdateTopics 
{ 
   my $config = shift;

    $config->{UpdateTopics}->{CurrentDataDir} = shift or die "UpdateTopics not provided with existing data directory!\n";

    $config->{UpdateTopics}->{NewReleaseDataDir} = shift or die "UpdateTopics not provided with new data directory!\n";

    $config->{UpdateTopics}->{DestinationDataDir} = (shift or "$config->{UpdateTopics}->{BaseDir}/newData");

    my $whoCares = `which rcs`;   # we should use File::Which to do this, except that would mean
                                  # getting yet another .pm into lib, which seems like hard work?
    ($? >> 8 == 0) or die "Uh-oh - couldn't see an rcs executable on your path!  I really need one of those!\n";

    $whoCares = `which patch`;

    ($? >> 8 == 0) or die "Uh-oh - couldn't see a patch executable on your path!  I really need one of those!\n";

    $config->{UpdateTopics}->{BaseDir} = `pwd`;
    chomp ($config->{UpdateTopics}->{BaseDir});

#Set if you want to see the debug output
#$config->{UpdateTopics}->{debug} = "yes";

    if ($config->{UpdateTopics}->{debug}) {print "$config->{UpdateTopics}->{CurrentDataDir}, $config->{UpdateTopics}->{NewReleaseDataDir}\n"; }

    if ((! -d $config->{UpdateTopics}->{CurrentDataDir} ) || (! -d $config->{UpdateTopics}->{NewReleaseDataDir})) {
	print "\nUsage: UpdateTopics <sourceDataDir> <NewReleaseDataDir>\n";
	exit;
    }

    print "\n\t...new upgraded data will be put in ./data\n";
    print "\tthere will be no changes made to either the source data directory or $config->{UpdateTopics}->{NewReleaseDataDir}.\n\n"; 
    print "\t This progam will attempt to use the rcs versioning information to upgrade the\n";
    print "\t contents of your distributed topics in sourceDataDir to the content in $config->{UpdateTopics}->{NewReleaseDataDir}.\n\n";
    print "Output:\n";
    print "\tFor each file that has no versioning information a _v_ will be printed\n";
    print "\tFor each file that has no changes from the previous release a _c_ will be printed\n";
    print "\tFor each file that has changes and a patch is generated a _p_ will be printed\n";
    print "\tFor each file that is new in the NewReleaseDataDir a _+_ will be printed\n";
    print "\t When the script has attempted to patch the $config->{UpdateTopics}->{NewReleaseDataDir}, 
\t *.rej files will contain the failed merges\n";
    print "\t although many of these rejected chages will be discarable, 
\t please check them to see if your configuration is still ok\n\n";

    sussoutDefaultWebTopics( $config ) ;

    mkdir $config->{UpdateTopics}->{DestinationDataDir};

#redirect stderr into a file (rcs dumps out heaps of info)
    my $rcsLogFile = ">".$config->{UpdateTopics}->{BaseDir}."/rcs.log";
    open(STDERR, $rcsLogFile);
    
    open(PATCH, "> $config->{UpdateTopics}->{DestinationDataDir}/patchTopics");
    
    print "\n\n ...checking existing files from $config->{UpdateTopics}->{CurrentDataDir}\n";
#TODO: need to find a way to detect non-Web directories so we don't make a mess of them..
# (should i just ignore Dirs without any ,v files?) - i can't upgrade tehm anyway..
#upgrade templates..?
    
    find(\&getRLog, $config->{UpdateTopics}->{CurrentDataDir});

    # deal with any symlinked in data directories in existing data that we found...

    my ($OriginalCurrentDataDir, $OriginalDestinationDataDir, $OriginalNewReleaseDataDir) = 
	($config->{UpdateTopics}->{CurrentDataDir}, $config->{UpdateTopics}->{DestinationDataDir}, $config->{UpdateTopics}->{NewReleaseDataDir});

    my $linkedDir;
    
    for $linkedDir (keys %{$config->{UpdateTopics}->{LinkedDirPathsInWiki}})
    {
	$config->{UpdateTopics}->{CurrentDataDir} = $linkedDir;

	$config->{UpdateTopics}->{DestinationDataDir} .= $config->{UpdateTopics}->{LinkedDirPathsInWiki}{$linkedDir};
	$config->{UpdateTopics}->{NewReleaseDataDir} .= $config->{UpdateTopics}->{LinkedDirPathsInWiki}{$linkedDir};

	find(\&getRLog, $config->{UpdateTopics}->{CurrentDataDir});
	
	($config->{UpdateTopics}->{CurrentDataDir}, $config->{UpdateTopics}->{DestinationDataDir}, $config->{UpdateTopics}->{NewReleaseDataDir}) = 
	    ($OriginalCurrentDataDir, $OriginalDestinationDataDir, $OriginalNewReleaseDataDir);
	
    }

    close(PATCH);

#do a find through $config->{UpdateTopics}->{NewReleaseDataDir} and copy all missing files & dirs
    print "\n\n ... checking for new files in $config->{UpdateTopics}->{NewReleaseDataDir}";
    find(\&copyNewTopics, $config->{UpdateTopics}->{NewReleaseDataDir});
    
#run `patch patchTopics` in $config->{UpdateTopics}->{DestinationDataDir}
    print "\nPatching topics (manually check the rejected patch (.rej) files)";
    chdir($config->{UpdateTopics}->{DestinationDataDir});
    `patch -p2 < patchTopics > patch.log`;
#TODO: examing the .rej files to remove the ones that have already been applied
    find(\&listRejects, ".");
#TODO: run `ci` in $config->{UpdateTopics}->{DestinationDataDir}
    
    print "\n\n";
    
}
    
# ============================================
sub listRejects
{ 
   my $config = shift;

    my ( $filename ) = @_;
    
    $filename = $File::Find::name;

    if ($filename =~ /.rej$/ ) {
        print "\nPatch rejected: $filename";
    }
}

# ============================================
sub copyNewTopics
{ 
   my $config = shift;

    my ( $filename ) = $File::Find::name;

    my $destinationFilename = $filename;
    $destinationFilename =~ s/$config->{UpdateTopics}->{NewReleaseDataDir}/$config->{UpdateTopics}->{DestinationDataDir}/g;

# Sven had these commeted out, so I've left them here commented out.
#    return if $filename =~ /,v$/;
#    return if $filename =~ /.lock$/;
#    return if $filename =~ /~$/;

    if ( -d $filename) {
        print "\nprocessing directory $filename";
	if ( !-d $destinationFilename ) {
	    print " (creating $destinationFilename)";
	    mkdir($destinationFilename);
	}
	print "\n";
        return;
    }
    
    if (! -e $destinationFilename ) { 
        print "\nadding $filename (new in this release)" if ($config->{UpdateTopics}->{debug});
        print "\n$destinationFilename: +\n" if (!$config->{UpdateTopics}->{debug});
        copy( $filename, $destinationFilename);
    }
    
}

# ============================================
sub getRLog
{ 
   my $config = shift;

    my ( $filename ) = $File::Find::name;

# (see above)
#    my ( $filename ) = @_;
#    $filename = $config->{UpdateTopics}->{BaseDir}."/".$File::Find::name if (! $filename );

    my ( $newFilename ) = $filename;
    if (!filename =~ /^$config->{UpdateTopics}->{CurrentDataDir}/)
    {
	die "getRLog found $filename that appears not to be in $config->{UpdateTopics}->{CurrentDataDir} tree! That's not supposed to happen: sorry!\n";
    }

    $newFilename =~ s/$config->{UpdateTopics}->{CurrentDataDir}/$config->{UpdateTopics}->{NewReleaseDataDir}/g;
    print "\n$filename -> $newFilename : "  if ( $config->{UpdateTopics}->{debug});

    my $destinationFilename = $filename;
    $destinationFilename =~ s/$config->{UpdateTopics}->{CurrentDataDir}/$config->{UpdateTopics}->{DestinationDataDir}/g;

    if ($filename =~ /,v$/ or $filename =~ /.lock$/ or $filename =~ /~$/) {
	print "skipping" if $config->{UpdateTopics}->{debug};
	return;
    }

    if ( -l $filename )
    {
	my $linkTarget = readlink $filename;

	$linkTarget or {warn "Oooo!  $filename is a symlink I can't read ($!) : ignoring\n"; return}
	
	if ( $linkTarget !~ m|^/| )
	{   # Cant cope with relative links: too lazy to reconstruct original file path, think about
            # implications at the moment!   Should/could be done...
	    warn "Found relative link in old data: $filename ... *ignorning*\n";
	}

	if ( !-d $linkTarget ) 
	{   # it will be safe to just proceed and treat this one like a normal file, though they just get a normal file
            # not a link in the merged data.
	    warn "Found a symlink to a file under existing data: $filename.  An actual file (not a link) will be created in the new data...\n";
	}
	else
	{   
	    my $newPathInWiki = $filename;

	    $newPathInWiki =~ s/$config->{UpdateTopics}->{CurrentDataDir}//;   # take the wiki root out of filename

	    # Put this linked-in data on the list to be dealt with later
	    # (because File::Find is not guaranteed to be reentrant (old perl versions))

	    mkdir($destinationFilename);   # ** OR CREATE A NEW SYMLINK, IF YOU KNEW WHERE TO PUT THE TARGET.

	    $config->{UpdateTopics}->{LinkedDirPathsInWiki}{$linkTarget} = $newPathInWiki;
	    print "Symlinked data under $filename put on list for a bit later...\n" if ($config->{UpdateTopics}->{debug});
	    print "l" if (!$config->{UpdateTopics}->{debug});
	    return;
	}
    }

    if ( -d $filename ) {
	print "\nprocessing directory (creating $destinationFilename)\n";
        mkdir($destinationFilename);
        return;
    }
    
    if ( isFromDefaultWeb( $config, $filename) )
    {
        $newFilename =~ s|^(.*)/[^/]*/([^/]*)|$1/_default/$2|g;
        print "\n$filename appears to have been generated from from _default - merging with $newFilename from the new distribution!" if ($config->{UpdateTopics}->{debug});
    }
    
    if (! -e $filename.",v" ){
#TODO: maybe copy this one too (this will inclure the .htpasswd file!!)   
        if ( $filename =~ /.txt$/ ) {
#TODO: in interactive mode ask if they want to create this topic's rcs file..        
            print "\nError: $filename does not have any rcs information" if ($config->{UpdateTopics}->{debug});
            print "v" if (! $config->{UpdateTopics}->{debug});
        }
        copy( $filename, $destinationFilename);
        return;
    }

    if ( -e $newFilename ) { 
        #file that may need upgrading
        my $highestCommonRevision = findHighestCommonRevision( $config,  $filename, $newFilename);
#print "-r".$highestCommonRevision."\n";
#is it the final version of $filename (in which case 
#TODO: what about manually updated files?
        if ( $highestCommonRevision =~ /\d*\.\d*/ ) {
            my $diff = doDiffToHead( $config,  $filename, $highestCommonRevision );
#print "\n========\n".$diff."\n========\n";            
            patchFile( $config,  $filename, $destinationFilename, $diff );
            print "\npatching $newFilename from $filename ($highestCommonRevision)" if ($config->{UpdateTopics}->{debug});
            print "\n$newFilename: p\n" if (!$config->{UpdateTopics}->{debug});
            copy( $newFilename, $destinationFilename);
            copy( $newFilename.",v", $destinationFilename.",v");
        } elsif ($highestCommonRevision eq "head" ) {
            print "\nhighest revision also final revision in oldTopic (using new Version)" if ($config->{UpdateTopics}->{debug});
            print "c" if (!$config->{UpdateTopics}->{debug});
            copy( $newFilename, $destinationFilename);
            copy( $newFilename.",v", $destinationFilename.",v");
        } else {
            #no common versions - this might be a user created file, 
            #or a manual attempt at creating a topic off twiki.org?raw=on
#TODO: do something nicer about this.. I think i need to do lots of diffs 
            #to see if there is any commonality
            print "\nWarning: copying $filename (no common versions)" if ($config->{UpdateTopics}->{debug});
            print "c" if (!$config->{UpdateTopics}->{debug});
            copy( $filename, $destinationFilename);
            copy( $filename.",v", $destinationFilename.",v");
        }
    } else {
        #new file created by users
#TODO: this will include topics copied using ManagingWebs (createWeb)
        print "\ncopying $filename (new user's file)" if ($config->{UpdateTopics}->{debug});
        print "c" if (!$config->{UpdateTopics}->{debug});
        copy( $filename, $destinationFilename);
        copy( $filename.",v", $destinationFilename.",v");
    }
}

# ==============================================
sub isFromDefaultWeb
{ 
   my $config = shift;

    my ($filename) = @_;
    
    $filename =~ /^(.*)\/[^\/]*\/([^\/]*)/;
    my $topic = $2;    
    return $topic if grep(/^$filename$/, @{$config->{UpdateTopics}->{DefaultWebTopics}});
}

sub sussoutDefaultWebTopics
{ 
   my $config = shift;

    opendir(DEFAULTWEB, 'data/_default"') or die "Yikes - couldn't open ./data/_default: $! ... not safe to proceed!\n";
    @{$config->{UpdateTopics}->{DefaultWebTopics}} = grep(/.txt$/, readdir(DEAFULTWEB));
    if ($config->{UpdateTopics}->{debug}) 
    {
	print "_default topics in new distro: @{$config->{UpdateTopics}->{DefaultWebTopics}}\n";
    }
}

# ==============================================
sub doDiffToHead
{ 
   my $config = shift;

    my ( $filename, $highestCommonRevision ) = @_;
   
#    print "$highestCommonRevision to ".getHeadRevisionNumber( $config, $filename)."\n";
#    print "\n----------------\n".getRevision( $config, $filename, $highestCommonRevision);
#     print "\n----------------\n".getRevision( $config, $filename, getHeadRevisionNumber( $config, $filename)) ;
#    return diff ( getRevision( $config, $filename, $highestCommonRevision), getRevision($filename, getHeadRevisionNumber( $config, $filename)) );

    my $cmd = "rcsdiff -r".$highestCommonRevision." -r".getHeadRevisionNumber( $config, $filename)." $filename";
    print "\n----------------\n".$cmd  if ($config->{UpdateTopics}->{debug});
    return `$cmd`;
}

# ==============================================
sub patchFile
{ 
   my $config = shift;

    my ( $oldFilename, $destinationFilename, $diff ) = @_;

    #make the paths relative again
    $oldFilename =~ s/$config->{UpdateTopics}->{BaseDir}//g;
    $destinationFilename =~ s/$config->{UpdateTopics}->{BaseDir}//g;
    
    print(PATCH "--- $oldFilename\n");
    print(PATCH "--- $destinationFilename\n");
    print(PATCH "$diff\n");
#    print(PATCH, "");
    
    #patch ($newFilename, $diff);
# and then do an rcs ci (check-in)
}

# ==============================================
sub getHeadRevisionNumber
{ 
   my $config = shift;

    my ( $filename ) = @_;
    
    my ( $cmd ) = "rlog ".$filename.",v";

    my $line;

    my @response = `$cmd`;
    foreach $line (@response) {
        next unless $line =~ /^head: (\d*\.\d*)/;
        return $1;
    }
    return;    
}

# ==============================================
#returns, as a string, the highest revision number common to both files
#Note: we return nothing if the highestcommon verison is also the last version of $filename
#TODO: are teh rcs versions always 1.xxx ? if not, how do we know?
sub findHighestCommonRevision 
{ 
   my $config = shift;

    my ( $filename, $newFilename) = @_;
    
    my $rev = 1;
    my $commonRev;

    my $oldContent = "qwer";
    my $newContent = "qwer";
    while ( ( $oldContent ne "" ) & ($newContent ne "") ) {
        print "\ncomparing $filename and $newFilename revision 1.$rev " if ($config->{UpdateTopics}->{debug});
        $oldContent = getRevision( $config,  $filename, "1.".$rev);
        $newContent = getRevision( $config,  $newFilename, "1.".$rev);
        if ( ( $oldContent ne "" ) & ($newContent ne "") ) {
            my $diffs = diff( \$oldContent, \$newContent, {STYLE => "Unified"} );
#            print "\n-----------------------|".$diffs."|-------------------\n";
#            print "\n-------------------[".$oldContent."]----|".$diffs."|-------[".$newContent."]--------------\n";
            if ( $diffs eq "" ) {
                #same!!
                $commonRev = "1.".$rev;
            }
        }
        $rev = $rev + 1;
    }

    print "\nlastCommon = $commonRev (head = ".getHeadRevisionNumber( $config,  $filename).")" if ($config->{UpdateTopics}->{debug});
    
    if ( $commonRev eq getHeadRevisionNumber( $config,  $filename) ) {
        return "head";
    }
    
    return $commonRev;
}

# ==============================================
#returns an empty string if the version does not exist
sub getRevision
{ 
   my $config = shift;

    my ( $filename, $rev ) = @_;

# use rlog to test if the revision exists..
    my ( $cmd ) = "rlog -r".$rev." ".$filename;

#print $cmd."\n";
    my @response = `$cmd`;
    my $revision;
    my $line;
    foreach $line (@response) {
        next unless $line =~ /^revision (\d*\.\d*)/;
        $revision = $1;
    }

    my $content;
    if ( $revision eq $rev ) {
        $cmd = "co -p".$rev." ".$filename;
        $content = `$cmd`;
    }

    return $content;
}
    
1;

1;

1;

1;
