#!/usr/bin/perl

$rcpcmd = 'rcp';
$scpcmd = 'scp';
$pwdcmd = '/bin/pwd';

@rmcmd  = ('rm');
@rmrcmd = ('rm', '-r');

$tagcmd = 'tag';

# scph - remote copy files to other (home) directories, keeping relative paths
# Steve Kinzler, steve@kinzler.com, Apr 00/Jun 03
# https://kinzler.com/me/home.html#homedir

require 5.000;
use Getopt::Std;	$Getopt::Std::STANDARD_HELP_VERSION = 1;

$dcpcmd = ($0 =~ m:scph[^/]*$:) ? $scpcmd : $rcpcmd;

$usage = "usage: $0 [ -S | -s ] [ -d dir ] [ -nqxXprt ]
       file ... [ host | == host ... ]
	-S	use rcp, default \$RCPHRCP or $dcpcmd
	-s	use scp, default \$RCPHRCP or $dcpcmd
	-d	use the given reference directory for paths, default \$HOME
	-n	no execute, just print commands
	-q	quiet mode, don't print commands
	-x	remove local files after copying them okay
	-X	remove local directories and files after copying them okay,
		  requires -r
	-p, -r	passed to rcp/scp
	-t	don't expand tag file specifiers with `$tagcmd`
The given files are copied to the given hosts to the same location
relative to the user's home directory, or relative to the path given
with a -d option.  All files must be hard under the reference directory.
If a file argument does not exist and is an alphanumeric (plus '-')
string and the -t option is not given and a tag expansion command is
configured, then the argument will be taken as a tag for expansion into
a file with the command.\n";

getopts('hSsd:nqxXprt') or die $usage;

die $usage if $opt_h || $#ARGV < 1 || $opt_X && ! $opt_r;

$i = -1;
foreach (@ARGV) { $i++; last if $_ eq '==' }
@hosts = splice(@ARGV, $i);
shift @hosts if $hosts[0] eq '==';

die $usage unless @ARGV && @hosts;

###############################################################################

chop($cwd = `$pwdcmd`);
die "$0: $pwdcmd error ($!)\n" if $? || $cwd eq '';

$cmd = ($opt_s)		 ? $scpcmd	   :
       ($opt_S)		 ? $rcpcmd	   :
       ($ENV{'RCPHRCP'}) ? $ENV{'RCPHRCP'} : $dcpcmd;

$dir  = $opt_d || $ENV{'HOME'}
			  or die "$0: cannot find HOME\n";
-d $dir			  or die "$0: invalid reference directory ($dir)\n";
$dira = &absdir("$dir/.") or exit 1;

$exit = 0; %err = (); $badhost = 0;

foreach (@ARGV) {
	chop($_ = `$tagcmd $_` || "$_\n"), s/^~$ENV{'USER'}\b/$ENV{'HOME'}/
		if ! $opt_t && ! -r && $tagcmd && /^[-\w]+$/;
	warn("$0: skipping file $_, no such readable file or directory\n"),
		$exit = 1, next unless -r;

	$_dir = &absdir($_);
	warn("$0: file ($_ in $_dir) not hard under the reference directory " .
	     "($dira)\n"), next unless $_dir =~ m:^\Q$dira\E(/|$):;

	if ($opt_d) { $_dir =~ s:^\Q$dira\E:$dir: }
	else	    { $_dir =~ s:^\Q$dira\E/?::   }

	push(@{$files{$_dir}}, $_);
}

while (@hosts) {
	$host = shift @hosts;
	warn("$0: skipping host $host, invalid\n"),
		$exit = $badhost = 1, next unless $host =~ /^[-\w.]+$/
					       && $host ne $ENV{'HOST'};

	foreach (sort keys %files) {
		&run($cmd, ($opt_p) ? '-p' : (), ($opt_r) ? '-r' : (),
		     @{$files{$_}}, "$host:$_");

		next if @hosts || $err{$_} || $badhost;

		&run(($opt_x) ? @rmcmd : ($opt_X) ? @rmrcmd : next,
		     @{$files{$_}});
	}
}

exit 1 if $exit;

###############################################################################

sub run {
	print(($opt_n) ? ': ' : '', join(' ', @_), "\n") unless $opt_q;
	eval { system(@_) }				 unless $opt_n;
	$exit = $err{$_} = $? if $?;
}

sub absdir {
	local($_) = @_;

	s:/+:/:g; s:^/$:/.:; s:/$::;
	$_ = (($cwd =~ m:^/+$:) ? '' : $cwd) . "/$_" unless m:^/:;
	s:[^/]*$::;

	s:':'\\'':g;
	chop($_ = `cd '$_'; $pwdcmd`);
	warn("$0: $pwdcmd error ($!)\n"), return '' if $? || $_ eq '';
	$_;
}
