|
Packit |
26bf30 |
# $Id: /mirror/lab/perl/File-Find-Rule/lib/File/Find/Rule.pm 2102 2006-06-01T15:39:03.942922Z richardc $
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
package File::Find::Object::Rule;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use strict;
|
|
Packit |
26bf30 |
use warnings;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use 5.008;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use vars qw/$VERSION $AUTOLOAD/;
|
|
Packit |
26bf30 |
use File::Spec;
|
|
Packit |
26bf30 |
use Text::Glob 'glob_to_regex';
|
|
Packit |
26bf30 |
use Number::Compare;
|
|
Packit |
26bf30 |
use Carp qw/croak/;
|
|
Packit |
26bf30 |
use File::Find::Object; # we're only wrapping for now
|
|
Packit |
26bf30 |
use File::Basename;
|
|
Packit |
26bf30 |
use Cwd; # 5.00503s File::Find goes screwy with max_depth == 0
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$VERSION = '0.0306';
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use Class::XSAccessor
|
|
Packit |
26bf30 |
accessors => {
|
|
Packit |
26bf30 |
"extras" => "extras",
|
|
Packit |
26bf30 |
"finder" => "finder",
|
|
Packit |
26bf30 |
"_match_cb" => "_match_cb",
|
|
Packit |
26bf30 |
"rules" => "rules",
|
|
Packit |
26bf30 |
"_relative" => "_relative",
|
|
Packit |
26bf30 |
"_subs" => "_subs",
|
|
Packit |
26bf30 |
"_maxdepth" => "_maxdepth",
|
|
Packit |
26bf30 |
"_mindepth" => "_mindepth",
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# we'd just inherit from Exporter, but I want the colon
|
|
Packit |
26bf30 |
sub import {
|
|
Packit |
26bf30 |
my $pkg = shift;
|
|
Packit |
26bf30 |
my $to = caller;
|
|
Packit |
26bf30 |
for my $sym ( qw( find rule ) ) {
|
|
Packit |
26bf30 |
no strict 'refs';
|
|
Packit |
26bf30 |
*{"$to\::$sym"} = \&{$sym};
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
for (grep /^:/, @_) {
|
|
Packit |
26bf30 |
my ($extension) = /^:(.*)/;
|
|
Packit |
26bf30 |
eval "require File::Find::Object::Rule::$extension";
|
|
Packit |
26bf30 |
croak "couldn't bootstrap File::Find::Object::Rule::$extension: $@" if $@;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=encoding utf8
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 NAME
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
File::Find::Object::Rule - Alternative interface to File::Find::Object
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 SYNOPSIS
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use File::Find::Object::Rule;
|
|
Packit |
26bf30 |
# find all the subdirectories of a given directory
|
|
Packit |
26bf30 |
my @subdirs = File::Find::Object::Rule->directory->in( $directory );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# find all the .pm files in @INC
|
|
Packit |
26bf30 |
my @files = File::Find::Object::Rule->file()
|
|
Packit |
26bf30 |
->name( '*.pm' )
|
|
Packit |
26bf30 |
->in( @INC );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# as above, but without method chaining
|
|
Packit |
26bf30 |
my $rule = File::Find::Object::Rule->new;
|
|
Packit |
26bf30 |
$rule->file;
|
|
Packit |
26bf30 |
$rule->name( '*.pm' );
|
|
Packit |
26bf30 |
my @files = $rule->in( @INC );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 DESCRIPTION
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
File::Find::Object::Rule is a friendlier interface to L<File::Find::Object> .
|
|
Packit |
26bf30 |
It allows you to build rules which specify the desired files and directories.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
B<WARNING> : This module is a fork of version 0.30 of L<File::Find::Rule>
|
|
Packit |
26bf30 |
(which has been unmaintained for several years as of February, 2009), and may
|
|
Packit |
26bf30 |
still have some bugs due to its reliance on File::Find'isms. As such it is
|
|
Packit |
26bf30 |
considered Alpha software. Please report any problems with
|
|
Packit |
26bf30 |
L<File::Find::Object::Rule> to its RT CPAN Queue.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# the procedural shim
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
*rule = \&fin;;
|
|
Packit |
26bf30 |
sub find {
|
|
Packit |
26bf30 |
my $object = __PACKAGE__->new();
|
|
Packit |
26bf30 |
my $not = 0;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
while (@_) {
|
|
Packit |
26bf30 |
my $method = shift;
|
|
Packit |
26bf30 |
my @args;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
if ($method =~ s/^\!//) {
|
|
Packit |
26bf30 |
# jinkies, we're really negating this
|
|
Packit |
26bf30 |
unshift @_, $method;
|
|
Packit |
26bf30 |
$not = 1;
|
|
Packit |
26bf30 |
next;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
unless (defined prototype $method) {
|
|
Packit |
26bf30 |
my $args = shift;
|
|
Packit |
26bf30 |
@args = ref $args eq 'ARRAY' ? @$args : $args;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
if ($not) {
|
|
Packit |
26bf30 |
$not = 0;
|
|
Packit |
26bf30 |
@args = ref($object)->new->$method(@args);
|
|
Packit |
26bf30 |
$method = "not";
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my @return = $object->$method(@args);
|
|
Packit |
26bf30 |
return @return if $method eq 'in';
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
$object;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 METHODS
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=over
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<new>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
A constructor. You need not invoke C<new> manually unless you wish
|
|
Packit |
26bf30 |
to, as each of the rule-making methods will auto-create a suitable
|
|
Packit |
26bf30 |
object if called as class methods.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub new {
|
|
Packit |
26bf30 |
# We need this to maintain compatibility with File-Find-Object.
|
|
Packit |
26bf30 |
# However, Randal Schwartz recommends against this practice in general:
|
|
Packit |
26bf30 |
# http://www.stonehenge.com/merlyn/UnixReview/col52.html
|
|
Packit |
26bf30 |
my $referent = shift;
|
|
Packit |
26bf30 |
my $class = ref $referent || $referent;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return
|
|
Packit |
26bf30 |
bless {
|
|
Packit |
26bf30 |
rules => [], # [0]
|
|
Packit |
26bf30 |
_subs => [], # [1]
|
|
Packit |
26bf30 |
iterator => [],
|
|
Packit |
26bf30 |
extras => {},
|
|
Packit |
26bf30 |
_maxdepth => undef,
|
|
Packit |
26bf30 |
_mindepth => undef,
|
|
Packit |
26bf30 |
_relative => 0,
|
|
Packit |
26bf30 |
}, $class;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub _force_object {
|
|
Packit |
26bf30 |
my $object = shift;
|
|
Packit |
26bf30 |
if (! ref($object))
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
$object = $object->new();
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
return $object;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=back
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 finder
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
The L<File::Find::Object> finder instance itself.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 my @rules = @{$ffor->rules()};
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
The rules to match against. For internal use only.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 Matching Rules
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=over
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<name( @patterns )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Specifies names that should match. May be globs or regular
|
|
Packit |
26bf30 |
expressions.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$set->name( '*.mp3', '*.ogg' ); # mp3s or oggs
|
|
Packit |
26bf30 |
$set->name( qr/\.(mp3|ogg)$/ ); # the same as a regex
|
|
Packit |
26bf30 |
$set->name( 'foo.bar' ); # just things named foo.bar
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub _flatten {
|
|
Packit |
26bf30 |
my @flat;
|
|
Packit |
26bf30 |
while (@_) {
|
|
Packit |
26bf30 |
my $item = shift;
|
|
Packit |
26bf30 |
ref $item eq 'ARRAY' ? push @_, @{ $item } : push @flat, $item;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
return @flat;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub _add_rule {
|
|
Packit |
26bf30 |
my $self = shift;
|
|
Packit |
26bf30 |
my $new_rule = shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
push @{$self->rules()}, $new_rule;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub name {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @names = map { ref $_ eq "Regexp" ? $_ : glob_to_regex $_ } _flatten( @_ );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule(
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
rule => 'name',
|
|
Packit |
26bf30 |
code => join( ' || ', map { "m($_)" } @names ),
|
|
Packit |
26bf30 |
args => \@_,
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item -X tests
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Synonyms are provided for each of the -X tests. See L<perlfunc/-X> for
|
|
Packit |
26bf30 |
details. None of these methods take arguments.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Test | Method Test | Method
|
|
Packit |
26bf30 |
------|------------- ------|----------------
|
|
Packit |
26bf30 |
-r | readable -R | r_readable
|
|
Packit |
26bf30 |
-w | writeable -W | r_writeable
|
|
Packit |
26bf30 |
-w | writable -W | r_writable
|
|
Packit |
26bf30 |
-x | executable -X | r_executable
|
|
Packit |
26bf30 |
-o | owned -O | r_owned
|
|
Packit |
26bf30 |
| |
|
|
Packit |
26bf30 |
-e | exists -f | file
|
|
Packit |
26bf30 |
-z | empty -d | directory
|
|
Packit |
26bf30 |
-s | nonempty -l | symlink
|
|
Packit |
26bf30 |
| -p | fifo
|
|
Packit |
26bf30 |
-u | setuid -S | socket
|
|
Packit |
26bf30 |
-g | setgid -b | block
|
|
Packit |
26bf30 |
-k | sticky -c | character
|
|
Packit |
26bf30 |
| -t | tty
|
|
Packit |
26bf30 |
-M | modified |
|
|
Packit |
26bf30 |
-A | accessed -T | ascii
|
|
Packit |
26bf30 |
-C | changed -B | binary
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Though some tests are fairly meaningless as binary flags (C<modified>,
|
|
Packit |
26bf30 |
C<accessed>, C<changed>), they have been included for completeness.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# find nonempty files
|
|
Packit |
26bf30 |
$rule->file,
|
|
Packit |
26bf30 |
->nonempty;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use vars qw( %X_tests );
|
|
Packit |
26bf30 |
%X_tests = (
|
|
Packit |
26bf30 |
-r => readable => -R => r_readable =>
|
|
Packit |
26bf30 |
-w => writeable => -W => r_writeable =>
|
|
Packit |
26bf30 |
-w => writable => -W => r_writable =>
|
|
Packit |
26bf30 |
-x => executable => -X => r_executable =>
|
|
Packit |
26bf30 |
-o => owned => -O => r_owned =>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
-e => exists => -f => file =>
|
|
Packit |
26bf30 |
-z => empty => -d => directory =>
|
|
Packit |
26bf30 |
-s => nonempty => -l => symlink =>
|
|
Packit |
26bf30 |
=> -p => fifo =>
|
|
Packit |
26bf30 |
-u => setuid => -S => socket =>
|
|
Packit |
26bf30 |
-g => setgid => -b => block =>
|
|
Packit |
26bf30 |
-k => sticky => -c => character =>
|
|
Packit |
26bf30 |
=> -t => tty =>
|
|
Packit |
26bf30 |
-M => modified =>
|
|
Packit |
26bf30 |
-A => accessed => -T => ascii =>
|
|
Packit |
26bf30 |
-C => changed => -B => binary =>
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
for my $test (keys %X_tests) {
|
|
Packit |
26bf30 |
my $sub = eval 'sub () {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
$self->_add_rule({
|
|
Packit |
26bf30 |
code => "' . $test . ' \$path",
|
|
Packit |
26bf30 |
rule => "'.$X_tests{$test}.'",
|
|
Packit |
26bf30 |
});
|
|
Packit |
26bf30 |
$self;
|
|
Packit |
26bf30 |
} ';
|
|
Packit |
26bf30 |
no strict 'refs';
|
|
Packit |
26bf30 |
*{ $X_tests{$test} } = $sub;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item stat tests
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
The following C<stat> based methods are provided: C<dev>, C<ino>,
|
|
Packit |
26bf30 |
C<mode>, C<nlink>, C<uid>, C<gid>, C<rdev>, C<size>, C<atime>,
|
|
Packit |
26bf30 |
C<mtime>, C<ctime>, C<blksize>, and C<blocks>. See L<perlfunc/stat>
|
|
Packit |
26bf30 |
for details.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Each of these can take a number of targets, which will follow
|
|
Packit |
26bf30 |
L<Number::Compare> semantics.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$rule->size( 7 ); # exactly 7
|
|
Packit |
26bf30 |
$rule->size( ">7Ki" ); # larger than 7 * 1024 * 1024 bytes
|
|
Packit |
26bf30 |
$rule->size( ">=7" )
|
|
Packit |
26bf30 |
->size( "<=90" ); # between 7 and 90, inclusive
|
|
Packit |
26bf30 |
$rule->size( 7, 9, 42 ); # 7, 9 or 42
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use vars qw( @stat_tests );
|
|
Packit |
26bf30 |
@stat_tests = qw( dev ino mode nlink uid gid rdev
|
|
Packit |
26bf30 |
size atime mtime ctime blksize blocks );
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
my $i = 0;
|
|
Packit |
26bf30 |
for my $test (@stat_tests) {
|
|
Packit |
26bf30 |
my $index = $i++; # to close over
|
|
Packit |
26bf30 |
my $sub = sub {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my @tests = map { Number::Compare->parse_to_perl($_) } @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule({
|
|
Packit |
26bf30 |
rule => $test,
|
|
Packit |
26bf30 |
args => \@_,
|
|
Packit |
26bf30 |
code => 'do { my $val = (stat $path)['.$index.'] || 0;'.
|
|
Packit |
26bf30 |
join ('||', map { "(\$val $_)" } @tests ).' }',
|
|
Packit |
26bf30 |
});
|
|
Packit |
26bf30 |
$self;
|
|
Packit |
26bf30 |
};
|
|
Packit |
26bf30 |
no strict 'refs';
|
|
Packit |
26bf30 |
*$test = $sub;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<any( @rules )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<or( @rules )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Allows shortcircuiting boolean evaluation as an alternative to the
|
|
Packit |
26bf30 |
default and-like nature of combined rules. C<any> and C<or> are
|
|
Packit |
26bf30 |
interchangeable.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# find avis, movs, things over 200M and empty files
|
|
Packit |
26bf30 |
$rule->any( File::Find::Object::Rule->name( '*.avi', '*.mov' ),
|
|
Packit |
26bf30 |
File::Find::Object::Rule->size( '>200M' ),
|
|
Packit |
26bf30 |
File::Find::Object::Rule->file->empty,
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub any {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @rulesets = @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule({
|
|
Packit |
26bf30 |
rule => 'any',
|
|
Packit |
26bf30 |
code => '(' . join( ' || ', map {
|
|
Packit |
26bf30 |
"( " . $_->_compile($self->_subs()) . " )"
|
|
Packit |
26bf30 |
} @rulesets ) . ")",
|
|
Packit |
26bf30 |
args => \@rulesets,
|
|
Packit |
26bf30 |
});
|
|
Packit |
26bf30 |
$self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
*or = \&any;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<none( @rules )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<not( @rules )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Negates a rule. (The inverse of C<any>.) C<none> and C<not> are
|
|
Packit |
26bf30 |
interchangeable.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# files that aren't 8.3 safe
|
|
Packit |
26bf30 |
$rule->file
|
|
Packit |
26bf30 |
->not( $rule->new->name( qr/^[^.]{1,8}(\.[^.]{0,3})?$/ ) );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub not {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @rulesets = @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule({
|
|
Packit |
26bf30 |
rule => 'not',
|
|
Packit |
26bf30 |
args => \@rulesets,
|
|
Packit |
26bf30 |
code => '(' . join ( ' && ', map {
|
|
Packit |
26bf30 |
"!(". $_->_compile($self->_subs()) . ")"
|
|
Packit |
26bf30 |
} @_ ) . ")",
|
|
Packit |
26bf30 |
});
|
|
Packit |
26bf30 |
$self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
*none = \¬
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<prune>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Traverse no further. This rule always matches.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub prune () {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule(
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
rule => 'prune',
|
|
Packit |
26bf30 |
code => 'do { $self->finder->prune(); 1 }'
|
|
Packit |
26bf30 |
},
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<discard>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Don't keep this file. This rule always matches.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub discard () {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule({
|
|
Packit |
26bf30 |
rule => 'discard',
|
|
Packit |
26bf30 |
code => '$discarded = 1',
|
|
Packit |
26bf30 |
});
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<exec( \&subroutine( $shortname, $path, $fullname ) )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Allows user-defined rules. Your subroutine will be invoked with parameters of
|
|
Packit |
26bf30 |
the name, the path you're in, and the full relative filename.
|
|
Packit |
26bf30 |
In addition, C<$_> is set to the current short name, but its use is
|
|
Packit |
26bf30 |
discouraged since as opposed to File::Find::Rule, File::Find::Object::Rule
|
|
Packit |
26bf30 |
does not cd to the containing directory.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Return a true value if your rule matched.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# get things with long names
|
|
Packit |
26bf30 |
$rules->exec( sub { length > 20 } );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub exec {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my $code = shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_add_rule(
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
rule => 'exec',
|
|
Packit |
26bf30 |
code => $code,
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item ->grep( @specifiers );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Opens a file and tests it each line at a time.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
For each line it evaluates each of the specifiers, stopping at the
|
|
Packit |
26bf30 |
first successful match. A specifier may be a regular expression or a
|
|
Packit |
26bf30 |
subroutine. The subroutine will be invoked with the same parameters
|
|
Packit |
26bf30 |
as an ->exec subroutine.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
It is possible to provide a set of negative specifiers by enclosing
|
|
Packit |
26bf30 |
them in anonymous arrays. Should a negative specifier match the
|
|
Packit |
26bf30 |
iteration is aborted and the clause is failed. For example:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$rule->grep( qr/^#!.*\bperl/, [ sub { 1 } ] );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Is a passing clause if the first line of a file looks like a perl
|
|
Packit |
26bf30 |
shebang line.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub grep {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @pattern = map {
|
|
Packit |
26bf30 |
ref $_
|
|
Packit |
26bf30 |
? ref $_ eq 'ARRAY'
|
|
Packit |
26bf30 |
? map { [ ( ref $_ ? $_ : qr/$_/ ) => 0 ] } @$_
|
|
Packit |
26bf30 |
: [ $_ => 1 ]
|
|
Packit |
26bf30 |
: [ qr/$_/ => 1 ]
|
|
Packit |
26bf30 |
} @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->exec( sub {
|
|
Packit |
26bf30 |
local *FILE;
|
|
Packit |
26bf30 |
open FILE, $self->finder->item() or return;
|
|
Packit |
26bf30 |
local ($_, $.);
|
|
Packit |
26bf30 |
while (<FILE>) {
|
|
Packit |
26bf30 |
for my $p (@pattern) {
|
|
Packit |
26bf30 |
my ($rule, $ret) = @$p;
|
|
Packit |
26bf30 |
return $ret
|
|
Packit |
26bf30 |
if ref $rule eq 'Regexp'
|
|
Packit |
26bf30 |
? /$rule/
|
|
Packit |
26bf30 |
: $rule->(@_);
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
return;
|
|
Packit |
26bf30 |
} );
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<maxdepth( $level )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Descend at most C<$level> (a non-negative integer) levels of directories
|
|
Packit |
26bf30 |
below the starting point.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
May be invoked many times per rule, but only the most recent value is
|
|
Packit |
26bf30 |
used.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<mindepth( $level )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Do not apply any tests at levels less than C<$level> (a non-negative
|
|
Packit |
26bf30 |
integer).
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<extras( \%extras )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Specifies extra values to pass through to C<File::File::find> as part
|
|
Packit |
26bf30 |
of the options hash.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
For example this allows you to specify following of symlinks like so:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $rule = File::Find::Object::Rule->extras({ follow => 1 });
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
May be invoked many times per rule, but only the most recent value is
|
|
Packit |
26bf30 |
used.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub maxdepth {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
$self->_maxdepth(shift);
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub mindepth {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
$self->_mindepth(shift);
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<relative>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Trim the leading portion of any path found
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub relative () {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
$self->_relative(1);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<not_*>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Negated version of the rule. An effective shortand related to ! in
|
|
Packit |
26bf30 |
the procedural interface.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$foo->not_name('*.pl');
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$foo->not( $foo->new->name('*.pl' ) );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub DESTROY {}
|
|
Packit |
26bf30 |
sub AUTOLOAD {
|
|
Packit |
26bf30 |
$AUTOLOAD =~ /::not_([^:]*)$/
|
|
Packit |
26bf30 |
or croak "Can't locate method $AUTOLOAD";
|
|
Packit |
26bf30 |
my $method = $1;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $sub = sub {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
$self->not( $self->new->$method(@_) );
|
|
Packit |
26bf30 |
};
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
no strict 'refs';
|
|
Packit |
26bf30 |
*$AUTOLOAD = $sub;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
&$sub;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=back
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 Query Methods
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=over
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<in( @directories )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Evaluates the rule, returns a list of paths to matching files and
|
|
Packit |
26bf30 |
directories.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub _call_find {
|
|
Packit |
26bf30 |
my $self = shift;
|
|
Packit |
26bf30 |
my $paths = shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $finder = File::Find::Object->new( $self->extras(), @$paths);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->finder($finder);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub _compile {
|
|
Packit |
26bf30 |
my $self = shift;
|
|
Packit |
26bf30 |
my $subs = shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return '1' unless @{ $self->rules() };
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $code = join " && ", map {
|
|
Packit |
26bf30 |
if (ref $_->{code}) {
|
|
Packit |
26bf30 |
push @$subs, $_->{code};
|
|
Packit |
26bf30 |
"\$subs->[$#{$subs}]->(\@args) # $_->{rule}\n";
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
else {
|
|
Packit |
26bf30 |
"( $_->{code} ) # $_->{rule}\n";
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
} @{ $self->rules() };
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $code;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub in {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @paths = @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->start(@paths);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my @results;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
while (defined(my $match = $self->match()))
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
push @results, $match;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return @results;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<start( @directories )>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Starts a find across the specified directories. Matching items may
|
|
Packit |
26bf30 |
then be queried using L</match>. This allows you to use a rule as an
|
|
Packit |
26bf30 |
iterator.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $rule = File::Find::Object::Rule->file->name("*.jpeg")->start( "/web" );
|
|
Packit |
26bf30 |
while ( my $image = $rule->match ) {
|
|
Packit |
26bf30 |
...
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub start {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
my @paths = @_;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $fragment = $self->_compile($self->_subs());
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $subs = $self->_subs();
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
warn "relative mode handed multiple paths - that's a bit silly\n"
|
|
Packit |
26bf30 |
if $self->_relative() && @paths > 1;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $code = 'sub {
|
|
Packit |
26bf30 |
my $path_obj = shift;
|
|
Packit |
26bf30 |
my $path = shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
if (!defined($path_obj))
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
return;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$path =~ s#^(?:\./+)+##;
|
|
Packit |
26bf30 |
my $path_dir = dirname($path);
|
|
Packit |
26bf30 |
my $path_base = fileparse($path);
|
|
Packit |
26bf30 |
my @args = ($path_base, $path_dir, $path);
|
|
Packit |
26bf30 |
local $_ = $path_base;
|
|
Packit |
26bf30 |
my $maxdepth = $self->_maxdepth;
|
|
Packit |
26bf30 |
my $mindepth = $self->_mindepth;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $comps = $path_obj->full_components();
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $depth = scalar(@$comps);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
defined $maxdepth && $depth >= $maxdepth
|
|
Packit |
26bf30 |
and $self->finder->prune();
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
defined $mindepth && $depth < $mindepth
|
|
Packit |
26bf30 |
and return;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
#print "Testing \'$_\'\n";
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $discarded;
|
|
Packit |
26bf30 |
return unless ' . $fragment . ';
|
|
Packit |
26bf30 |
return if $discarded;
|
|
Packit |
26bf30 |
return $path;
|
|
Packit |
26bf30 |
}';
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
#use Data::Dumper;
|
|
Packit |
26bf30 |
#print Dumper \@subs;
|
|
Packit |
26bf30 |
#warn "Compiled sub: '$code'\n";
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $callback = eval "$code" or die "compile error '$code' $@";
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
$self->_match_cb($callback);
|
|
Packit |
26bf30 |
$self->_call_find(\@paths);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return $self;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item C<match>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Returns the next file which matches, false if there are no more.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
sub match {
|
|
Packit |
26bf30 |
my $self = _force_object shift;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $finder = $self->finder();
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $match_cb = $self->_match_cb();
|
|
Packit |
26bf30 |
my $preproc_cb = $self->extras()->{'preprocess'};
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
while(defined(my $next_obj = $finder->next_obj()))
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
if (defined($preproc_cb) && $next_obj->is_dir())
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
$finder->set_traverse_to(
|
|
Packit |
26bf30 |
$preproc_cb->(
|
|
Packit |
26bf30 |
$self,
|
|
Packit |
26bf30 |
[ @{$finder->get_current_node_files_list()} ]
|
|
Packit |
26bf30 |
)
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
if (defined(my $path = $match_cb->($next_obj, $next_obj->path())))
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
if ($self->_relative)
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
my $comps = $next_obj->full_components();
|
|
Packit |
26bf30 |
if (@$comps)
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
return
|
|
Packit |
26bf30 |
($next_obj->is_dir()
|
|
Packit |
26bf30 |
? File::Spec->catdir(@$comps)
|
|
Packit |
26bf30 |
: File::Spec->catfile(@$comps)
|
|
Packit |
26bf30 |
)
|
|
Packit |
26bf30 |
;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
else
|
|
Packit |
26bf30 |
{
|
|
Packit |
26bf30 |
return $path;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
return;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
1;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
__END__
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=back
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 Extensions
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Extension modules are available from CPAN in the File::Find::Object::Rule
|
|
Packit |
26bf30 |
namespace. In order to use these extensions either use them directly:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use File::Find::Object::Rule::ImageSize;
|
|
Packit |
26bf30 |
use File::Find::Object::Rule::MMagic;
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# now your rules can use the clauses supplied by the ImageSize and
|
|
Packit |
26bf30 |
# MMagic extension
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
or, specify that File::Find::Object::Rule should load them for you:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
use File::Find::Object::Rule qw( :ImageSize :MMagic );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
For notes on implementing your own extensions, consult
|
|
Packit |
26bf30 |
L<File::Find::Object::Rule::Extending>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 Further examples
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=over
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item Finding perl scripts
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $finder = File::Find::Object::Rule->or
|
|
Packit |
26bf30 |
(
|
|
Packit |
26bf30 |
File::Find::Object::Rule->name( '*.pl' ),
|
|
Packit |
26bf30 |
File::Find::Object::Rule->exec(
|
|
Packit |
26bf30 |
sub {
|
|
Packit |
26bf30 |
if (open my $fh, $_) {
|
|
Packit |
26bf30 |
my $shebang = <$fh>;
|
|
Packit |
26bf30 |
close $fh;
|
|
Packit |
26bf30 |
return $shebang =~ /^#!.*\bperl/;
|
|
Packit |
26bf30 |
}
|
|
Packit |
26bf30 |
return 0;
|
|
Packit |
26bf30 |
} ),
|
|
Packit |
26bf30 |
);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Based upon this message http://use.perl.org/comments.pl?sid=7052&cid=10842
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=item ignore CVS directories
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
my $rule = File::Find::Object::Rule->new;
|
|
Packit |
26bf30 |
$rule->or($rule->new
|
|
Packit |
26bf30 |
->directory
|
|
Packit |
26bf30 |
->name('CVS')
|
|
Packit |
26bf30 |
->prune
|
|
Packit |
26bf30 |
->discard,
|
|
Packit |
26bf30 |
$rule->new);
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Note here the use of a null rule. Null rules match anything they see,
|
|
Packit |
26bf30 |
so the effect is to match (and discard) directories called 'CVS' or to
|
|
Packit |
26bf30 |
match anything.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=back
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 TWO FOR THE PRICE OF ONE
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
File::Find::Object::Rule also gives you a procedural interface. This is
|
|
Packit |
26bf30 |
documented in L<File::Find::Object::Rule::Procedural>
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 EXPORTS
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 find
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 rule
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 Tests
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 accessed
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-A>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 ascii
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-T>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 atime
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 binary
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-b>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 blksize
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 block
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-b>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 blocks
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 changed
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-C>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 character
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-c>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 ctime
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 dev
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 directory
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-d>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 empty
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-z>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 executable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-x>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 exists
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-e>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 fifo
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-p>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 file
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-f>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 gid
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 ino
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 mode
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 modified
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-M>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 mtime
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 nlink
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 r_executable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-X>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 r_owned
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-O>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 nonempty
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
A predicate that determines if the file is empty. Uses C<-s>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 owned
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-o>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 r_readable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-R>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 r_writeable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 r_writable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-W>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 rdev
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 readable
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-r>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 setgid
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-g>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 setuid
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-u>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 size
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See stat tests.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 socket
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-S>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 sticky
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-k>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 symlink
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-l>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 uid
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
See "stat tests".
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 tty
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-t>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head2 writable()
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Corresponds to C<-w>.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 BUGS
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
The code relies on qr// compiled regexes, therefore this module
|
|
Packit |
26bf30 |
requires perl version 5.005_03 or newer.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Currently it isn't possible to remove a clause from a rule object. If
|
|
Packit |
26bf30 |
this becomes a significant issue it will be addressed.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 AUTHOR
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Richard Clamp <richardc@unixbeard.net> with input gained from this
|
|
Packit |
26bf30 |
use.perl discussion: http://use.perl.org/~richardc/journal/6467
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Additional proofreading and input provided by Kake, Greg McCarroll,
|
|
Packit |
26bf30 |
and Andy Lester andy@petdance.com.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Ported to use L<File::Find::Object> as File::Find::Object::Rule by
|
|
Packit |
26bf30 |
Shlomi Fish.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 COPYRIGHT
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Copyright (C) 2002, 2003, 2004, 2006 Richard Clamp. All Rights Reserved.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
This module is free software; you can redistribute it and/or modify it
|
|
Packit |
26bf30 |
under the same terms as Perl itself.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 SEE ALSO
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
L<File::Find::Object>, L<Text::Glob>, L<Number::Compare>, find(1)
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
If you want to know about the procedural interface, see
|
|
Packit |
26bf30 |
L<File::Find::Object::Rule::Procedural>, and if you have an idea for a neat
|
|
Packit |
26bf30 |
extension, see L<File::Find::Object::Rule::Extending> .
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
L<Path::Class::Rule> ’s SEE ALSO contains a review of many directory traversal
|
|
Packit |
26bf30 |
modules on CPAN, including L<File::Find::Object::Rule> and L<File::Find::Rule>
|
|
Packit |
26bf30 |
(on which this module is based).
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=head1 KNOWN BUGS
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
The tests don't run successfully when directly inside an old Subversion
|
|
Packit |
26bf30 |
checkout, due to the presence of C<.svn> directories. C<./Build disttest> or
|
|
Packit |
26bf30 |
C<./Build distruntest> run fine.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=begin Developers
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Implementation notes:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
[0] Currently we use an array of anonymous subs, and call those
|
|
Packit |
26bf30 |
repeatedly from match. It'll probably be way more effecient to
|
|
Packit |
26bf30 |
instead eval-string compile a dedicated matching sub, and call that to
|
|
Packit |
26bf30 |
avoid the repeated sub dispatch.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
[1] Though [0] isn't as true as it once was, I'm not sure that the
|
|
Packit |
26bf30 |
subs stack is exposed in quite the right way. Maybe it'd be better as
|
|
Packit |
26bf30 |
a private global hash. Something like $subs{$self} = []; and in
|
|
Packit |
26bf30 |
C<DESTROY>, delete $subs{$self}.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
That'd make compiling subrules really much easier (no need to pass
|
|
Packit |
26bf30 |
@subs in for context), and things that work via a mix of callbacks and
|
|
Packit |
26bf30 |
code fragments are possible (you'd probably want this for the stat
|
|
Packit |
26bf30 |
tests).
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Need to check this currently working version in before I play with
|
|
Packit |
26bf30 |
that though.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
[*] There's probably a win to be made with the current model in making
|
|
Packit |
26bf30 |
stat calls use C<_>. For
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
find( file => size => "> 20M" => size => "< 400M" );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
up to 3 stats will happen for each candidate. Adding a priming _
|
|
Packit |
26bf30 |
would be a bit blind if the first operation was C< name => 'foo' >,
|
|
Packit |
26bf30 |
since that can be tested by a single regex. Simply checking what the
|
|
Packit |
26bf30 |
next type of operation doesn't work since any arbritary exec sub may
|
|
Packit |
26bf30 |
or may not stat. Potentially worse, they could stat something else
|
|
Packit |
26bf30 |
like so:
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
# extract from the worlds stupidest make(1)
|
|
Packit |
26bf30 |
find( exec => sub { my $f = $_; $f =~ s/\.c$/.o/ && !-e $f } );
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
Maybe the best way is to treat C<_> as invalid after calling an exec,
|
|
Packit |
26bf30 |
and doc that C<_> will only be meaningful after stat and -X tests if
|
|
Packit |
26bf30 |
they're wanted in exec blocks.
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=end Developers
|
|
Packit |
26bf30 |
|
|
Packit |
26bf30 |
=cut
|