First version master
authorJoey Schulze <joey@infodrom.org>
Sat, 28 May 2016 14:24:05 +0000 (16:24 +0200)
committerJoey Schulze <joey@infodrom.org>
Sat, 28 May 2016 23:33:47 +0000 (01:33 +0200)
src/mirrordir [new file with mode: 0755]

diff --git a/src/mirrordir b/src/mirrordir
new file mode 100755 (executable)
index 0000000..802a3df
--- /dev/null
@@ -0,0 +1,156 @@
+#! /usr/bin/perl
+
+#   Copyright (c) 2016  Joey Schulze <joey@infodrom.org>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+
+use strict;
+use warnings;
+
+use File::Copy;
+use File::Path;
+use File::DirCompare;
+use Getopt::Long;
+
+my $opt_dry_run;
+my $opt_verbose;
+my $opt_source;
+my $opt_destination;
+
+my @mirrordirs;
+
+sub help
+{
+    print <<EOT;
+mirrordir options sourcdir destdir
+
+Options
+--help|-h         This help
+--verbose|-v      Verbose output
+--dry-run         Don\'t actually mirror
+
+EOT
+    exit;
+}
+
+sub assert_directory
+{
+    my $dir = shift;
+    return 1 if -d $dir;
+
+    mkpath $dir;
+}
+
+sub copy_file
+{
+    my $src = shift;
+    my $dst = shift;
+
+    return unless -f $src;
+
+    my $pos = rindex $dst, '/';
+    assert_directory substr $dst, 0, $pos if $pos > 1;
+
+    if (!copy $src, $dst) {
+       printf STDERR "copy %s %s failed\n", $src, $dst;
+       return;
+    }
+
+    my @stat_src = stat $src;
+    my @stat_dst = stat $dst;
+
+    if ($stat_src[7] != $stat_dst[7]) {
+       printf STDERR "copy %s %s failed (size mismatch)\n", $src, $dst;
+       return;
+    }
+
+    utime $stat_src[9], $stat_src[9], $dst;
+}
+
+sub compare_process_item
+{
+    my $a = shift;
+    my $b = shift;
+
+    if (! $b) {
+       my $dst = $opt_destination . substr($a, length $opt_source);
+
+       if (-d $a) {
+           printf "mirror %s %s\n", $a, $dst if $opt_verbose;
+           push @mirrordirs, [$a, $dst] unless $opt_dry_run;
+       } else {
+           printf "copy %s %s\n", $a, $dst if $opt_verbose;
+           copy_file $a, $dst unless $opt_dry_run;
+       }
+    } elsif (! $a) {
+       if (-d $b) {
+           printf "rmdir %s\n", $b if $opt_verbose;
+           rmtree $b unless $opt_dry_run;
+       } else {
+           printf "delete %s\n", $b if $opt_verbose;
+           unlink $b unless $opt_dry_run;
+       }
+    } else {
+       if (-f $a && -f $b) {
+           printf "copy %s %s\n", $a, $b if $opt_verbose;
+           copy_file $a, $b unless $opt_dry_run;
+       } elsif (-d $b) {
+           printf "rmdir %s\n", $b if $opt_verbose;
+           rmtree $b unless $opt_dry_run;
+
+           printf "copy %s %s\n", $a, $b if $opt_verbose;
+           copy_file $a, $b unless $opt_dry_run;
+       } elsif (-d $a) {
+           printf "delete %s\n", $b if $opt_verbose;
+           unlink $b unless $opt_dry_run;
+
+           my $pos = rindex $b, '/';
+           my $relpath = substr($b, $pos);
+           printf "mirror %s %s\n", $opt_source.$relpath, $opt_destination.$relpath if $opt_verbose;
+           push @mirrordirs, [$opt_source.$relpath, $opt_destination.$relpath] unless $opt_dry_run;
+       }
+    }
+}
+
+sub mirror_directory
+{
+    File::DirCompare->compare($opt_source, $opt_destination, \&compare_process_item);
+}
+
+sub mirror_directories
+{
+    mirror_directory;
+
+    while (my $item = shift @mirrordirs) {
+       $opt_source = $item->[0];
+       $opt_destination = $item->[1];
+       mirror_directory;
+    }
+}
+
+GetOptions (
+    'help|h' => \&help,
+    'verbose|v' => \$opt_verbose,
+    'dry-run|dryrun' => \$opt_dry_run,
+);
+
+die "Not enough parameters\n" unless scalar @ARGV == 2;
+
+$| = 1;
+
+$opt_source = $ARGV[0];
+$opt_destination = $ARGV[1];
+
+mirror_directories;