#!/usr/bin/perl

# TP synchronisation de fichiers en perl
# solution basée sur la question 4 et qui ne tient pas compte des méta-données
#
# Question 7
# 
# Antoine Miné
# 26/01/2007


# ceci n'a pas changé depuis mirroir5.pl

use File::Copy;

$doit = 0;

foreach (@ARGV) {
    if ($_ eq "--doit") { $doit = 1; }
    elsif (!defined($dirB)) { $dirB = $_; }
    elsif (!defined($dirA)) { $dirA = $_; }
    else { die "argument $_ en trop"; }
}

die "utilisation: $0 [--doit] repertoire-courant repertoire-archive"
    unless defined($dirA) && defined($dirB);

sub parcours 
{
    my $dir  = shift;

    opendir(DIR,"$root/$dir") or return;
    my @files = readdir(DIR) or return;
    closedir(DIR);

    foreach (@files) {
	next if /^\.\.?$/;
	next if /^\.etat$/;

	$filename = "$dir/$_";
	$fullfilename = "$root/$filename";

	if (-d $fullfilename) { parcours($filename); }
	elsif (-f $fullfilename) {
	    $result = `sha1sum "$fullfilename"`;
	    chomp $result;
	    $result =~ s/ .*$//;
	    $table{$filename} = $result;
	}
    }
}

sub sauvegarde 
{
    open(ETAT,">$root/.etat") or die "echec d'écriture de $root/.etat ($!)";
    for (keys %table) {
	print ETAT "$table{$_} $_\n";
    }
    close(ETAT);
}

sub charge 
{
    open(ETAT,"$root/.etat") or die "echec de lecture de $root/.etat ($!)";
    while (<ETAT>) {
	($signature,$fichier) = /^([^ ]*) (.*)$/;
	$table{$fichier} = $signature;
    }
    close(ETAT);
}

# copy_safe(file,src,dst): copie src/file => dst/file
sub copy_safe
{
    my $file = shift;
    my $src = shift;
    my $dst = shift;
    print "copie $src/$file => $dst/$file\n";
    if ($doit) {
	copy("$src/$file","$dst/$file")
	    or warn "échec de la copie de $src/$file vers $dst/$file ($!)";
    }
}

# unlik_safe(file,src): efface src/file
sub unlik_safe
{
    my $file = shift;
    my $src = shift;
    print "détruit $src/$file\n";
    if ($doit) {
	unlink "$src/$file" or warn "échec de la destruction de $src/$file ($!)";
    }
}


# place dans %newA, l'état courant de $dirA
# et dans %oldA, l'ancien état de $dirA (ou vide si pas de .etat)
$root = $dirA; 
%table = (); 
parcours("."); 
%newA = %table;
%table = ();
if (-f "$root/.etat") { charge(); }
%oldA = %table;

# même chose pour B
$root = $dirB; 
%table = (); 
parcours("."); 
%newB = %table;
%table = ();
if (-f "$root/.etat") { charge(); }
%oldB = %table;


# unifie A et B en un état newC
for $i (keys %newA, keys %newB) {

    # l'étant antérieur de $i doit être identique dans A et B
    die "fichiers .etat non synchronisés (fichier $i)" 
	if ($oldA{$i} ne $oldB{$i});

    # cas où le fichier est identique en A et B
    # gère le cas où le fichier a été modifié ni dans A ni dans B 
    # et celui où il a été modifié de manière identique dans A et dans B
    if ($newA{$i} eq $newB{$i}) { $newC{$i} = $newA{$i}; }

    # cas où le fichier a été modifié seulement dans A ou dans B
    elsif ($newA{$i} eq $oldA{$i}) { $newC{$i} = $newB{$i}; }
    elsif ($newB{$i} eq $oldB{$i}) { $newC{$i} = $newA{$i}; }

    # cas où le fichier a été modifié de manière différente dans A et dans B
    else { die "fichier $i modifié dans $dirA et $dirB"; }
}

# copie A<->B
for $i (keys %newC) {
    next if !defined($newC{$i}); # ceci peut arriver!
    if ($newB{$i} ne $newC{$i}) { copy_safe($i,$dirA,$dirB); }
    if ($newA{$i} ne $newC{$i}) { copy_safe($i,$dirB,$dirA); }
}

# nettoyage A
for $i (keys %newA) {
    unlik_safe($i,$dirA) if !defined($newC{$i});
}

# nettoyage B
for $i (keys %newB) {
    unlik_safe($i,$dirB) if !defined($newC{$i});
}

# mise à jour des .etat
if ($doit) {
    %table = %newC;
    $root = $dirA;
    sauvegarde();
    $root = $dirB;
    sauvegarde();
}

