#!/usr/bin/perl -w

# copy - A wrapper around `cp' that can read a list of files to copy, from stdin or an external file.
# Copyright (c) 2009 Mark A. Taff <mark@marktaff.com>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

use Getopt::Long;
use File::Basename;


# About info
$appName   = "copy";
$appDesc   = "A wrapper around `cp' that can read a list of files to copy, from stdin or an external file.";
$author    = "Mark A. Taff";
$email     = "<mark\@marktaff.com>";
$copyright = "Copyright (c) 2009";
$license   = "Licensed under the GNU GPL, v3, or any later version.";

# Command line args
my $verbose;
my $help;
my $version;
my $src;

# Print out usage information
sub help()
{
    $help = <<END;
$appName
$appDesc

Usage: $appName [options]

Options:
      --files-from=[SRC]   The file to read the files to be copied from, where SRC
                           is the file to read from.  If SRC is - files will be read
                           from stdin.
      --verbose            Operate in verbose mode
  -h, --help               Print this help information
  -V, --version            Print out version, author, and copyright information

    If the destination directory doesn't exist, it will be automatically created. All
    other options are simply passed along to `cp', so see `cp --help' for more information.

    To copy a list of files from STDIN to /home/mark/export:
        `copy --files-from=- /home/mark/export`

    To copy a list of files from /tmp/export_files to /home/mark/export:
        `copy --files-from=/tmp/export_files /home/mark/export`

END
    print $help;
} # End sub help()




# Print out version information
sub version()
{
    $version = <<END;
$appName - $appDesc
  $copyright $author $email
  $license
END
    print $version;
} # End sub version()




sub buildDest(@)
{
    my $dest = shift;
    my $src = shift;
    my $basename = "";

    # Create dest directory, if needed
    system ("mkdir", "-p", $dest);

    # Build new destination filename
    $basename = `basename "$src"`;
    chomp $basename;
    if ($dest =~ /\/\Z/gi) {$dest .= $basename;}
    else {$dest .= "/" . $basename;}

    return $dest;
} # End sub buildDest()




# Copy a single file
sub copy (@)
{
    my @args = @_;
    unshift (@args, "cp");

    # Determine  destination arg, and explicitly use full path
    for (my $i=2; $i < @args; ++$i)
    # NOTE: since we prepended "cp" and the source file to @args earlier,
    #       know that they are at index 0 & 1, respectively, so we can start at
    #       index position 2.
    {
        if    (($args[$i] =~ m/\A-t/gi) ||
               ($args[$i] =~ m/\A--target/gi))
               {$args[$i] = buildDest $args[$i], $args[1];}

        elsif (($args[$i] =~ /\A\//gi) &&
               ($args[$i] ne $args[1]))
               {$args[$i] = buildDest $args[$i], $args[1];}
    };

    system(@args) == 0
        or warn "system @args failed: $?";
    if ($verbose)
        {print "Copied $args[1] to $args[2]\n";}
} # End sub copy()



# Split @ARGV into args for `copy' and args for `cp'
@ARGV_COPY = @ARGV;
foreach (@ARGV_COPY)
{
    if ($_ eq "--verbose")                   {push (@copyArgs, $_); next;}
    if (($_ eq "--help") || ($_ eq "-h"))    {push (@copyArgs, $_); next;}
    if (($_ eq "--version") || ($_ eq "-v")) {push (@copyArgs, $_); next;}
    if (/--files-from=/gi)                   {push (@copyArgs, $_); next;}

    push (@cpArgs, $_);
};

# Now reset @ARGV so it only conatins args for `copy'
@ARGV = @copyArgs;


# Read command line options
GetOptions ( "verbose"        => \$verbose,
             "help|h"         => \$help,
             "version|v"      => \$version,
             "files-from=s"   => \$src,
            ) or die "Unable to parse command line arguments.";


# Process command line args
if ($help)    {help(); exit 0;}
if ($version) {version(); exit 0;}



if (!($src))
{
    # Useless use of copy.  Just pass everything to cp
    copy( @copyArgs );
    exit 0;
}

if ($src eq "-")
{
    # Read from stdin
    while (<STDIN>)
    {
        my @args = @cpArgs;
        chomp;
        unshift (@args, $_);
        copy( @args );
    }
}
else
{
    # Read from a file
    open INPUT, "<$src"
        or die "Unable to open $src: $?";
    while (<INPUT>)
    {
        my @args = @cpArgs;
        chomp;
        unshift (@args, $_);
        copy( @args );
    }
    close INPUT;
}

exit 0;
