#!/usr/bin/perl
#SYNOPSIS
#   rsyncbkup user
#   rsyncbkup srcdir destdir [linkdir]..
#
#DESCRIPTION
# This does a backup using rsync.  It copies files from srcdir to dstdir.  If
# one  or  more linkdirs are included, they are searched for versions of each
# file, and if found, a link is done from  there  rather  than  a  copy  from
# srcdir.  The linkdirs must be on the same filesystem as destdir.
#
# A major problem with rsync is getting the linkdir pathnames  correct.   The
# usual  problem  is  that  they must be must be relative to the destdir, and
# that can be tricky.  The man page implies (but  doesn't  state)  that  full
# pathnames  are  acceptable,  but  I've never seen one actually work.  Also,
# there are problems with pathnames that contain spaces; these often  produce
# strange error messages and total failure to find the linkdirs.
#
# Our "rsync user" syntax, with only the one arg, generates a case that seems
# to  work  reliably (as long as the userid doesn't contain a space), so it's
# the easiest way to use this script.  But it only works for  backing  up  an
# entire  user's  file  tree.   A  directory  CCYYMMDD/user is created, where
# CCYMMDD is the ISO 8-character date.  If there are other  year  directories
# with the same user id, they will all be used as linkdirs.

$| = 1;
$exitstat = 0;
($P = $0) =~ s".*/"";
$V = $ENV{"V_$P"} || $ENV{"D_$P"} || 1;	# Verbose level.
$rsed = '';

$a = int(@ARGV);
if ($a < 1) {
	print STDERR "Usage: rsyncbkup srcdir destdir [linkdir]..\n" if $V>0;
	exit 1;
} elsif ($a == 1) {	# Single arg is user login id
	$U = shift @ARGV;
	print "User: $U\n" if $V>1;
#	open(PASSWD, '/etc/passwd');
#	while (<PASSWD>) {		# Doesn't work on OSX
#		($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split(/:/);
#		if ($login eq $U) {
#			$S = $home;
#			close PASSWD;
#		}
#	}
	($S = `echo ~$U`) =~ s"[\r\s/]*$"/";
	die "$0: Can't find home directory for $U.\n" unless defined $S && -d $S;
	unless (-d $S) {
		print "$S is not a directory.\n" if $V>0;
		exit $?;
	}
	print "Backing up user directory '$S'\n" if $V>0;
	print "S: $S\n" if $V>0;
	($ss,$mm,$hh,$DD,$MM,$CY) = gmtime($now = time);
	$CY += 1900;
	$MM += 1;
	$cymd = sprintf('%04d%02d%02d',$CY,$MM,$DD); 
	@dirs = glob("*/$U/");		# Do we have backup dirs for this user?
	$D = "$cymd/$U/";
	print "D: $D\n" if $V>0;
	while ($l = pop @dirs) {	# Use them in reverse order
		next if $l eq $S || $l eq $D;
		$LD = " --link-dest=../../$l";
		print "LD:$LD\n" if $V>0;
		$L .= $LD;
		print "L:$L\n" if $V>1;
	}
} else {				# Multiple args are srcdir, destdir, linkdirs
	($S= shift @ARGV) =~ s"/*$"/";	# Make sure src dir has final '/'
	($D= shift @ARGV);		# The dst dir doesn't need the final '/'
	for $l (@ARGV) {
		next if $l eq $S || $l eq $D;
		$L .= " --link-dest=$l";
		print "L: $L\n" if $V>0;
	}
}
system "mkdir -p $D" unless -d $D;

$rsef = $S . ".rsExclude";
print "rsef: $rsef\n" if $V>0;
if (-f $rsef) {
	$rsed = "--exclude-from=$rsef";
	print "rsed: $rsed\n" if $V>0;
} else {
	print "rsef=\"$rsef\" is not a file.\n" if $V>0;
}

$cmd = "rsync -Hav $rsed $L $S $D";
print "cmd: $cmd\n" if $V>0;
system $cmd;
