From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Wed, 9 Mar 2016 21:00:31 +0000 (-0300) Subject: Add filter capability to RecursiveCopy::copypath X-Git-Tag: REL9_6_BETA1~543 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a31aaec40643e0074f30b1683953cb0b3ea27036;p=postgresql Add filter capability to RecursiveCopy::copypath This allows skipping copying certain files and subdirectories in tests. This is useful in some circumstances such as copying a data directory; future tests want this feature. Also POD-ify the module. Authors: Craig Ringer, Pallavi Sontakke Reviewed-By: Álvaro Herrera --- diff --git a/src/test/perl/RecursiveCopy.pm b/src/test/perl/RecursiveCopy.pm index 9362aa8959..c4da1bbd83 100644 --- a/src/test/perl/RecursiveCopy.pm +++ b/src/test/perl/RecursiveCopy.pm @@ -1,4 +1,19 @@ -# RecursiveCopy, a simple recursive copy implementation + +=pod + +=head1 NAME + +RecursiveCopy - simple recursive copy implementation + +=head1 SYNOPSIS + +use RecursiveCopy; + +RecursiveCopy::copypath($from, $to, filterfn => sub { return 1; }); +RecursiveCopy::copypath($from, $to); + +=cut + package RecursiveCopy; use strict; @@ -7,16 +22,85 @@ use warnings; use File::Basename; use File::Copy; +=pod + +=head1 DESCRIPTION + +=head2 copypath($from, $to, %params) + +Recursively copy all files and directories from $from to $to. + +Only regular files and subdirectories are copied. Trying to copy other types +of directory entries raises an exception. + +Raises an exception if a file would be overwritten, the source directory can't +be read, or any I/O operation fails. Always returns true. + +If the B<filterfn> parameter is given, it must be a subroutine reference. +This subroutine will be called for each entry in the source directory with its +relative path as only parameter; if the subroutine returns true the entry is +copied, otherwise the file is skipped. + +On failure the target directory may be in some incomplete state; no cleanup is +attempted. + +=head1 EXAMPLES + + RecursiveCopy::copypath('/some/path', '/empty/dir', + filterfn => sub { + # omit pg_log and contents + my $src = shift; + return $src ne 'pg_log'; + } + ); + +=cut + sub copypath { - my $srcpath = shift; - my $destpath = shift; + my ($base_src_dir, $base_dest_dir, %params) = @_; + my $filterfn; - die "Cannot operate on symlinks" if -l $srcpath or -l $destpath; + if (defined $params{filterfn}) + { + die "if specified, filterfn must be a subroutine reference" + unless defined(ref $params{filterfn}) + and (ref $params{filterfn} eq 'CODE'); - # This source path is a file, simply copy it to destination with the - # same name. - die "Destination path $destpath exists as file" if -f $destpath; + $filterfn = $params{filterfn}; + } + else + { + $filterfn = sub { return 1; }; + } + + # Start recursive copy from current directory + return _copypath_recurse($base_src_dir, $base_dest_dir, "", $filterfn); +} + +# Recursive private guts of copypath +sub _copypath_recurse +{ + my ($base_src_dir, $base_dest_dir, $curr_path, $filterfn) = @_; + my $srcpath = "$base_src_dir/$curr_path"; + my $destpath = "$base_dest_dir/$curr_path"; + + # invoke the filter and skip all further operation if it returns false + return 1 unless &$filterfn($curr_path); + + # Check for symlink -- needed only on source dir + die "Cannot operate on symlinks" if -l $srcpath; + + # Can't handle symlinks or other weird things + die "Source path \"$srcpath\" is not a regular file or directory" + unless -f $srcpath or -d $srcpath; + + # Abort if destination path already exists. Should we allow directories + # to exist already? + die "Destination path \"$destpath\" already exists" if -e $destpath; + + # If this source path is a file, simply copy it to destination with the + # same name and we're done. if (-f $srcpath) { copy($srcpath, $destpath) @@ -24,18 +108,19 @@ sub copypath return 1; } - die "Destination needs to be a directory" unless -d $srcpath; + # Otherwise this is directory: create it on dest and recurse onto it. mkdir($destpath) or die "mkdir($destpath) failed: $!"; - # Scan existing source directory and recursively copy everything. opendir(my $directory, $srcpath) or die "could not opendir($srcpath): $!"; while (my $entry = readdir($directory)) { - next if ($entry eq '.' || $entry eq '..'); - RecursiveCopy::copypath("$srcpath/$entry", "$destpath/$entry") + next if ($entry eq '.' or $entry eq '..'); + _copypath_recurse($base_src_dir, $base_dest_dir, + $curr_path eq '' ? $entry : "$curr_path/$entry", $filterfn) or die "copypath $srcpath/$entry -> $destpath/$entry failed"; } closedir($directory); + return 1; }