#/usr/bin/perl
#
# Usage: lndir old-dir new-dir
#
# Compare two directories, and link all identical files.

# Check args && fetch names.

((($olddir,$newdir) = @ARGV) == 2 && -d $olddir && -d $newdir)
    || die "Usage: $0 old-dir new-dir\n";

# Get all files in the new dir.

open(FIND,"find $newdir -type f -print|");

$|=1;

FILE: while ($new = <FIND>) {
    chop $new;
    ($file = $new) =~ s#^$newdir/##o
	|| die "find said $new?\n";
    $old = "$olddir/$file";

    # Get stat info for both files.

    ($ndev,$nino,$nmode,$nnlink,$nuid,$ngid,$nrdev,$nsize,
      $natime,$nmtime,$nctime,$nblksize,$nblocks)
	  = stat($new);

    unless ($nino) {
	print "$new: $!\n";
	next FILE;
    }

    unless (-f _) {
	warn "$new is not a plain file\n";
	next FILE;
    }

    ($odev,$oino,$omode,$onlink,$ouid,$ogid,$ordev,$osize,
      $oatime,$omtime,$octime,$oblksize,$oblocks)
	  = stat($old);

    unless ($oino) {
	next FILE;
    }

    unless (-f _) {
	warn "$old is not a plain file\n";
	next FILE;
    }

    # Quick check on size and mode.

    if ($nsize != $osize) {
	print "$file differs\n";
	next FILE;
    }

    if ($nmode != $omode) {
	print "$file mode differs\n";
	next FILE;
    }

    # Already linked?  (Perhaps symbolically?)
    # Compare dev/inode numbers.

    if ($ndev == $odev && $nino == $oino) {
	print "$file already linked\n";
	next FILE;
    }

    # Now compare the two files.

    unless (open(NEW,"$new")) {
	print "$new: $!\n";
	next FILE;
    }
    unless (open(OLD,"$old")) {
	print "$old: $!\n";
	next FILE;
    }
    $blksize = $nblksize || 8192;
    while (read(OLD,$obuf,$blksize)) {
	read(NEW,$nbuf,$blksize);
	if ($obuf ne $nbuf) {
	    print "$file differs\n";
	    next FILE;
	}
    }

    # Okay, let's link.

    if (unlink($new) && link($old, $new)) {
	print "$file linked to $old\n";
	next FILE;
    }

    print "$file: $!\n";
}