MacOSX-File

 view release on metacpan or  search on metacpan

bin/psync  view on Meta::CPAN

    }
}

$opt_v and do_log("fixing directory attributes ...");
# sort must be this order for depth-first traversal
for my $k (sort {$b cmp $a} keys %Action){
    $Action{$k} >  0 or next;
    my ($size, $mtime)		      = unpack("N2", $Signature{$k});
    my ($mode, $uid,  $gid,  $atime)  = unpack("N4", $Attribs{$k});
    S_ISDIR($mode) or next;  # -d
    my $spath = $Root{$k} . $k; $spath =~ s,^/+,/,o;
    my $dpath = $Dst . $k;

    unless ($opt_n){
	copyattrib($spath, $dpath, $mode, $uid, $gid, $atime, $mtime);
	$opt_v and do_log(sprintf "0%04o,%s,%s $dpath", ($mode & 07777),
			  (getpwuid($uid))[0],(getgrgid($gid))[0] );
    }
}

if ($opt_r >= 2){
    # these are to make DB operation fast enough
    my $hashinfo = DB_File::HASHINFO->new;
    $hashinfo->{nelem} = scalar keys %Action;
    $hashinfo->{bsize} = 1024; # MAXPATHLEN
    $hashinfo->{cachesize} = 4 * 1024 * 1024;
    tie (my %db, 'DB_File', $Psync_DB,	O_CREAT|O_RDWR, 0640, $hashinfo)
	or die "$Psync_DB : $!";
    $opt_v and do_log("Using $Dst/$Psync_DB to store extra attributes.");
    my $count;
    while ( my ($k, $v) = each %Action){
	if ($v >= 0){
	    $db{$k} = $Attribs{$k};
	    $count++ % 10000 == 0 and do_log("$count items stored.");
	}
    }
    untie %db;
    move $Psync_DB, "$Dst/$Psync_DB" or die "Can't move $Psync_DB";
}

sub copyattrib{
    my ($spath, $dpath, $mode, $uid, $gid, $atime, $mtime) = @_;
    my $finfo = getfinfo($spath);
    unless ($opt_r > 1){
	chmod $mode & 07777, $dpath;
	chown $uid,   $gid,  $dpath;
    }
    $finfo and $finfo->set($dpath);
    utime $atime, $mtime,  $dpath;
}

exit;
sub do_log{
    print shift, "\n";
}

sub sig2txt{
    return sprintf("0x%08x,0x%08x",unpack("N2",shift));
}

sub addsig{
    my ($path, $mode,$uid,$gid,$size,$atime,$mtime, $action) = @_;
    my $sig  = pack("N2", (S_ISREG($mode) ? $size : 0), $mtime);
    tied %Attribs or $Attribs{$path} = pack("N4", $mode, $uid, $gid, $atime);
    if ($opt_v > 3 and $action > 0){
	do_log qq(was: ) . sig2txt($Signature{$path});
	do_log qq(now: ) . sig2txt($sig);
    }
    if ($Signature{$path} eq $sig){
	$opt_f or $action = 0;	   # same file
    }else{
	$Signature{$path} =  $sig; # different
    }
    $Action{$path} = $action;
    $opt_v > 2 and
	do_log(join("," => $Action{$path},
		    sprintf("0x%08x,0x%08x,0x%08x",
			    unpack("N2",$Signature{$path})),
		    $path));
}

# File::Find is too general purpose thus slow.
# we implement our own traversal routine

sub scantree {
    my ($root, $path, $action) = @_;
    if ($opt_v){
	$ScanCount % 8192 == 0 and  printf "\n%10d:", $ScanCount;
	$ScanCount % 128  == 0 and  print  ".";
	$ScanCount++;
    }
    if ($path =~ $IgnorePat){
	addsig($path, 0, 0, 0, 0, 0, 0, $Del_Ignored);
	return;
    }
    $action > 0 and $Root{$path} = $root;
    my $fpath = $root . $path;
    my ($dev, $mode, $nlink, $uid, $gid, $size, $atime, $mtime) =
	(lstat($fpath))[0,2,3,4,5,7,8,9] or warn "can't stat $fpath";

    addsig($path, $mode, $uid, $gid, $size, $atime, $mtime, $action);

    if (-d _){
	$dev != $Topdev and return;
	opendir my $d, $fpath or warn "$fpath:$!";
	# see ._* is avoided
	my @f = grep !/^\.(?:\.?$|_)/o, readdir $d;
	closedir $d;
	for my $f (@f){
	    my $spath = "$path/$f";
	    if ($IgnoreFiles{$f}){
		addsig($spath, $mode, $uid, $gid, $size, $mtime, $atime,
		       $Del_IgFiles) unless $f eq $Psync_DB;
	    }else{
		scantree($root, $spath, $action);
	    }
	}
    }
}

sub help{
    print <<"EOT";
psync	[-c][-d][-n][-q|-v] source_items ... target_directory
psync -r[-c][-d][-n][-q|-v] source_directory target_directory
EOT
exit;
}
1;
__END__
=head1 NAME

psync -- update copy

=head1 SYNOPSIS

 psync   [-c][-d][-n][-q|-v] source_items ... target_directory
 psync -r[-c][-d][-n][-q|-v] source_directory target_directory

=head1 TIGER

As of Mac OS X v10.4 (Tiger) L<rsync(1)> does support resorce fork
with -E option. You should also consider using it.

=head1 DESCRIPTION

psync does an update copy. It compares source directory and target
directory at first, then erases items that are nonexistent on source
directory if specified and finally copies everything on source directory.
Items with the same modification date and (data fork) size remain
untouched, saving time on operation.

Currently psync supports options below

=over 4

=item -r

Remote backup/restore mode. Ownership and permissions are
stored/retrieved via C<.psync.db>

If the source directory contains a file C<.psync.db>, psync
turns into remote restore mode. It uses .psync.db on source
directory to restore ownership and permissions.

If not, psync turns into remote backup mode. After the backup
it stores ownership and permissions to C<.psync.db>

As the name suggests, this option is imperative when the backup
directory is on remote volume such as AFP, NFS, and Samba.

=item -dI<n>



( run in 0.506 second using v1.01-cache-2.11-cpan-71847e10f99 )