Skip to content

Commit a31aaec

Browse files
committed
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
1 parent a298a1e commit a31aaec

File tree

1 file changed

+96
-11
lines changed

1 file changed

+96
-11
lines changed

src/test/perl/RecursiveCopy.pm

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
# RecursiveCopy, a simple recursive copy implementation
1+
2+
=pod
3+
4+
=head1 NAME
5+
6+
RecursiveCopy - simple recursive copy implementation
7+
8+
=head1 SYNOPSIS
9+
10+
use RecursiveCopy;
11+
12+
RecursiveCopy::copypath($from, $to, filterfn => sub { return 1; });
13+
RecursiveCopy::copypath($from, $to);
14+
15+
=cut
16+
217
package RecursiveCopy;
318

419
use strict;
@@ -7,35 +22,105 @@ use warnings;
722
use File::Basename;
823
use File::Copy;
924

25+
=pod
26+
27+
=head1 DESCRIPTION
28+
29+
=head2 copypath($from, $to, %params)
30+
31+
Recursively copy all files and directories from $from to $to.
32+
33+
Only regular files and subdirectories are copied. Trying to copy other types
34+
of directory entries raises an exception.
35+
36+
Raises an exception if a file would be overwritten, the source directory can't
37+
be read, or any I/O operation fails. Always returns true.
38+
39+
If the B<filterfn> parameter is given, it must be a subroutine reference.
40+
This subroutine will be called for each entry in the source directory with its
41+
relative path as only parameter; if the subroutine returns true the entry is
42+
copied, otherwise the file is skipped.
43+
44+
On failure the target directory may be in some incomplete state; no cleanup is
45+
attempted.
46+
47+
=head1 EXAMPLES
48+
49+
RecursiveCopy::copypath('/some/path', '/empty/dir',
50+
filterfn => sub {
51+
# omit pg_log and contents
52+
my $src = shift;
53+
return $src ne 'pg_log';
54+
}
55+
);
56+
57+
=cut
58+
1059
sub copypath
1160
{
12-
my $srcpath = shift;
13-
my $destpath = shift;
61+
my ($base_src_dir, $base_dest_dir, %params) = @_;
62+
my $filterfn;
1463

15-
die "Cannot operate on symlinks" if -l $srcpath or -l $destpath;
64+
if (defined $params{filterfn})
65+
{
66+
die "if specified, filterfn must be a subroutine reference"
67+
unless defined(ref $params{filterfn})
68+
and (ref $params{filterfn} eq 'CODE');
1669

17-
# This source path is a file, simply copy it to destination with the
18-
# same name.
19-
die "Destination path $destpath exists as file" if -f $destpath;
70+
$filterfn = $params{filterfn};
71+
}
72+
else
73+
{
74+
$filterfn = sub { return 1; };
75+
}
76+
77+
# Start recursive copy from current directory
78+
return _copypath_recurse($base_src_dir, $base_dest_dir, "", $filterfn);
79+
}
80+
81+
# Recursive private guts of copypath
82+
sub _copypath_recurse
83+
{
84+
my ($base_src_dir, $base_dest_dir, $curr_path, $filterfn) = @_;
85+
my $srcpath = "$base_src_dir/$curr_path";
86+
my $destpath = "$base_dest_dir/$curr_path";
87+
88+
# invoke the filter and skip all further operation if it returns false
89+
return 1 unless &$filterfn($curr_path);
90+
91+
# Check for symlink -- needed only on source dir
92+
die "Cannot operate on symlinks" if -l $srcpath;
93+
94+
# Can't handle symlinks or other weird things
95+
die "Source path \"$srcpath\" is not a regular file or directory"
96+
unless -f $srcpath or -d $srcpath;
97+
98+
# Abort if destination path already exists. Should we allow directories
99+
# to exist already?
100+
die "Destination path \"$destpath\" already exists" if -e $destpath;
101+
102+
# If this source path is a file, simply copy it to destination with the
103+
# same name and we're done.
20104
if (-f $srcpath)
21105
{
22106
copy($srcpath, $destpath)
23107
or die "copy $srcpath -> $destpath failed: $!";
24108
return 1;
25109
}
26110

27-
die "Destination needs to be a directory" unless -d $srcpath;
111+
# Otherwise this is directory: create it on dest and recurse onto it.
28112
mkdir($destpath) or die "mkdir($destpath) failed: $!";
29113

30-
# Scan existing source directory and recursively copy everything.
31114
opendir(my $directory, $srcpath) or die "could not opendir($srcpath): $!";
32115
while (my $entry = readdir($directory))
33116
{
34-
next if ($entry eq '.' || $entry eq '..');
35-
RecursiveCopy::copypath("$srcpath/$entry", "$destpath/$entry")
117+
next if ($entry eq '.' or $entry eq '..');
118+
_copypath_recurse($base_src_dir, $base_dest_dir,
119+
$curr_path eq '' ? $entry : "$curr_path/$entry", $filterfn)
36120
or die "copypath $srcpath/$entry -> $destpath/$entry failed";
37121
}
38122
closedir($directory);
123+
39124
return 1;
40125
}
41126

0 commit comments

Comments
 (0)