MacOSX-File
view release on metacpan or search on metacpan
# $Id: pcpmac,v 0.70 2005/08/09 15:47:00 dankogai Exp $
#
use strict;
use Getopt::Std;
use File::stat;
use File::Basename;
my %Opt;
getopts("fiprv", \%Opt);
$Opt{i} and delete $Opt{f}; # for safety
my $IAM = basename($0);
my %Hardlink; # key = inode, val = name of first file copied
my $Dst = pop @ARGV; @ARGV or help();
my $Dstst = stat($Dst); # not lstat; symlink to directory allowed
unless (-d _){
@ARGV == 1 or help();
do_copy($ARGV[0], $Dst);
}else{
$Dst =~ s,/+$,,o;
for my $src (@ARGV){
$src =~ s,/+$,,o;
do_copy($src, $Dst . '/' . basename($src));
}
}
exit;
use MacOSX::File::Copy;
use MacOSX::File::Info;
sub do_copy{
my ($src, $dst) = @_;
my $srcst = lstat($src) or warn "$src: Can't lstat!" and return;
$srcst->ino == $Dstst->ino and $srcst->dev == $Dstst->dev
and warn "$src and $dst are identical. skipped" and return;
$Opt{v} and print STDERR "$src\n";
if (my $dstst = lstat($dst) and -l _ or -f _){
$Opt{i} and prompt($dst) or return;
$Opt{f} and unlink $dst;
}
$srcst = lstat($src);
if (-l _){ # just copy the linkage
symlink(readlink($src), $dst) or warn "$src -> $dst : $!";
}elsif(-f _){
if ($srcst->nlink > 1){ # MacOSX has hard links!
if (my $link = $Hardlink{$srcst->ino}){
link($link, $dst) or warn "$link -> $dst:$!";
}else{
$Hardlink{$srcst->ino} = $dst;
}
}
copy($src, $dst) # simple file-to-file copy
or warn "$dst:$! ($MacOSX::File::OSErr)";
# set attributes
if ($Opt{p}){
chown $srcst->uid, $srcst->gid, $dst;
chmod $srcst->mode & 07777, $dst or warn "$dst : $!";
}else{
my $now = time();
utime $now, $now, $dst or warn "$dst: $!";
}
}elsif(-d _){ # tough part;
unless ($Opt{r}){
warn "$IAM: $src is a directory. skipped";
return;
}
mkdir $dst,0777 or die "$dst:$!";
opendir my $d, $src or die "$src:$!";
# see ._* is avoided
my @f = grep !/^\.(?:\.?$|_)/o, readdir $d;
closedir $d;
my $finfo = getfinfo($src)
or die "$src:Error $MacOSX::File::OSErr";
for my $f (@f){
my ($srcf, $dstf) = ("$src/$f", "$dst/$f");
# no cross-device traversal within source directory
# so you can casually go like 'pcpmac -R / /Volumes/backup'
lstat($srcf)->dev != $srcst->dev and next;
-e $dstf and die "$dstf: already exists!";
# else lets' do it recursively
do_copy($srcf, $dstf);
}
# copy finfo info after all traversal is done
$finfo->set($dst) or warn "$dst : $MacOSX::File::OSErr";
# set attributes
if ($Opt{p}){
chown $srcst->uid, $srcst->gid, $dst;
chmod $srcst->mode & 07777, $dst or warn "$dst : $!";
}else{
# do nothing
}
}else{
# do nothing for devices, sockets and fifos;
# devices are handled by devfs on Macs
}
}
sub prompt{
my $path = shift;
$| = 1;
print "Overwrite $path? [y/N]:";
my $answer = <STDIN>; chomp $answer;
return lc($answer) eq 'y';
}
sub help{
# warn caller;
print STDERR <<"EOT";
usage: $IAM [-r] [-f|-i] [-p][-v] src target
$IAM [-r] [-f|-i] [-p][-v] src1 ... srcN directory
EOT
exit;
}
1;
__END__
=head1 NAME
pcpmac -- CpMac(1) or cp(1), implemented as perl script
=head1 SYNOPSIS
pcpmac [-r] [-f|-i] [-p][-v] source_file target_file
pcpmac [-r] [-f|-i] [-p][-v] source_file ... target_directory
=head1 TIGER
As of Mac OS X v10.4 (Tiger), the ordinary L<cp(1)> does support resource fork.
=head1 DESCRIPTION
pcpmac, as its name implies, copies files with finder info and
resource fork.
In the first synopsis form, pcpmac copies the contents of the
source_file to the target_file. In the second synopsis form, the contents
of each named source_file is copied to the destination target_directory.
The names of the files themselves are not changed. If cpmac detects an
attempt to copy a file to itself, the copy will fail.
The following options are available:
=item -r
If source_file designates a directory, cp copies the directory and
the entire subtree connected at that point.
( run in 1.068 second using v1.01-cache-2.11-cpan-71847e10f99 )