package Net::FTP::Find;

use strict;
use warnings;

our $VERSION = '0.040';

use Carp;
use File::Spec;

use Net::FTP;
use base qw( Net::FTP );

use Net::FTP::Find::Mixin qw( Net::FTP::Find );

use Exporter;
use base qw( Exporter );
our @EXPORT_OK = qw( find finddepth );

1;
__END__

=head1 NAME

Net::FTP::Find - Traverse a directory tree through Net::FTP (or Net::FTPSSL)

=head1 SYNOPSIS

  use Net::FTP::Find;

  my $ftp = Net::FTP::Find->new('localhost');
  $ftp->login('user', 'pass');

  $ftp->find(sub { ... }, '/');

  $ftp->finddepth(sub { ... }, '/');

or

  use Net::FTP;
  use Net::FTP::Find::Mixin;

  my $ftp = Net::FTP->new('localhost');
  $ftp->login('user', 'pass');

  $ftp->find(sub { ... }, '/');

  $ftp->finddepth(sub { ... }, '/');

or

  use Net::FTPSSL;
  use Net::FTP::Find::Mixin qw(Net::FTPSSL);

  my $ftp = Net::FTPSSL->new('localhost');
  $ftp->login('user', 'pass');

  $ftp->find(sub { ... }, '/');

  $ftp->finddepth(sub { ... }, '/');

=head1 DESCRIPTION

These are functions for searching through directory trees doing work on each
file found similar to the File::Find. Net::FTP::Find provides two functions,
"find" and "finddepth".  They work similarly but have subtle differences.

=head1 FUNCTIONS

=over 3

=item B<find>

  $ftp->find(\&wanted,  @directories);
  $ftp->find(\%options, @directories);

=item B<finddepth>

  $ftp->finddepth(\&wanted,  @directories);
  $ftp->finddepth(\%options, @directories);

=back

=head2 %options

The first argument to C<find()> is either a code reference to your
C<&wanted> function, or a hash reference describing the operations
to be performed for each file.  The
code reference is described in L<The wanted function> below.

Here are the possible keys for the hash:

=over 3

=item C<wanted>

The value should be a code reference.  This code reference is
described in L<The wanted function> below. The C<&wanted> subroutine is
mandatory.

=item C<bydepth>

Reports the name of a directory only AFTER all its entries
have been reported.  Entry point C<finddepth()> is a shortcut for
specifying C<< { bydepth => 1 } >> in the first argument of C<find()>.

=item C<no_chdir>

Does not C<cwd()> to each directory as it recurses. The C<wanted()>
function will need to be aware of this, of course. In this case,
C<$_> will be the same as C<$Net::FTP::Find::name>.

=item C<max_depth>

The directories that are deeper than this value is traversed.

=item C<min_depth>

The directories that are shallower than this value is traversed.

=item C<use_mlsd>

If true, Net::FTP::Find tries to use the "MLSD" to retrieve entries.
By using the "MLSD", we can fetch "mtime" of each file effectivly.
Net::FTP::Find use the "LIST" instead automatically if the "MLSD" cannot be used.
Default value is "1".

=item C<fetch_mtime>

If true, Net::FTP::Find tries to fetch "mtime" of each file.
When "MLSD" can be used, "mtime" is fetched with file list.
When "MLSD" cannot be used, "mtime" is fetched by using "MDTM" internally.
Default value is "0".

=back


=head2 The wanted function

The C<wanted()> function does whatever verifications you want on
each file and directory.  Note that despite its name, the C<wanted()>
function is a generic callback function, and does B<not> tell
Net::FTP::Find if a file is "wanted" or not.  In fact, its return value
is ignored.

The wanted function takes no arguments but rather does its work
through a collection of variables.

=over 4

=item C<$Net::FTP::Find::dir> is the current directory name,

=item C<$_> is the current filename within that directory

=item C<$Net::FTP::Find::name> is the complete pathname to the file.

=item C<$Net::FTP::Find::is_directory> is 1 if C<$_> is directory.

=back

These variables may be able to use if server is supporting.

=over 4

=item C<$Net::FTP::Find::mtime> is modification time (Unix epoch). this value is generated by parsing "YYYYMMDDHHMMSS" as GMT. If a server returns mtime as JST, this value may not be correct.

=item C<$Net::FTP::Find::is_symlink> is 1 if C<$_> is symlink.

=item C<$Net::FTP::Find::mode> is mode.

=item C<$Net::FTP::Find::user> is owner name.

=item C<$Net::FTP::Find::group> is group name.

=item C<$Net::FTP::Find::size> is file size in bytes.

=back

The above variables have all been localized and may be changed without
effecting data outside of the wanted function.

For example, when examining the file F</some/path/foo.ext> you will have:



    $Net::FTP::Find::dir  = /some/path/
    $_                    = foo.ext
    $Net::FTP::Find::name = /some/path/foo.ext

You are cwd()'d to C<$Net::FTP::Find::dir> when the function is called,
unless C<no_chdir> was specified. Note that when changing to
directories is in effect the root directory (F</>) is a somewhat
special case inasmuch as the concatenation of C<$Net::FTP::Find::dir>,
C<'/'> and C<$_> is not literally equal to C<$Net::FTP::Find::name>. The
table below summarizes all variants:

              $Net::FTP::Find::name  $Net::FTP::Find::dir  $_
 default      /                      /                     .
 no_chdir=>0  /etc                   /                     etc
              /etc/x                 /etc                  x

 no_chdir=>1  /                      /                     /
              /etc                   /                     /etc
              /etc/x                 /etc                  /etc/x



=head1 AUTHOR

Taku Amano E<lt>taku@toi-planning.netE<gt>

Some part of code about L<Net::FTPSSL> is referred to L<Net::FTPSSL>.

A mostly parts of the document are from L<File::Find>.

=head1 SEE ALSO

L<File::Find>
L<Net::FTP>
L<Net::FTPSSL>
L<Net::FTP::Find::Mixin>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
