The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright 2009, 2010, 2011, 2012, 2013 Kevin Ryde.

# MyLocatePerl.pm is shared by various distributions.
#
# MyLocatePerl.pm is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# MyLocatePerl.pm is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this file.  If not, see <http://www.gnu.org/licenses/>.

package MyLocatePerl;
use 5.006;
use strict;
use warnings;
use base 'File::Locate::Iterator';
use FindBin;
use File::Slurp 'slurp';
use IO::File;
use IO::Uncompress::AnyInflate;
use MyUniqByInode;
use MyUniqByMD5;

# uncomment this to run the ### lines
# use Smart::Comments;


# glob => '/usr/share/perl5/Debconf/FrontEnd/*'

my $compressed_re = qr/\.gz$/;

sub new {
  my ($class, %options) = @_;

  my @suffixes = ('t','pm','pl','PL');
  if (delete $options{'include_pod'}) {
    push @suffixes, 'pod';
  }
  if (delete $options{'exclude_t'}) {
    @suffixes = grep {$_ ne 't'} @suffixes;
  }
  if (delete $options{'only_t'}) {
    @suffixes = ('t');
  }
  my $suffixes = join('|',@suffixes);
  my $suffixes_re = qr/\.($suffixes)($compressed_re)?$/;
  ### $suffixes
  ### $suffixes_re

  my $locate_regexp = $suffixes_re;
  if (defined (my $under_directory = $options{'under_directory'})) {
    unless ($under_directory =~ m{/$}) {
      $under_directory .= '/';
    }
    $locate_regexp = qr/^\Q$under_directory\E.*$suffixes_re/;
  }
  ### $locate_regexp

  my $self = $class->SUPER::new (
                                 # globs => ['/bin/*',
                                 #           '/usr/bin/*',
                                 #           '/usr/local/bin/*',
                                 #           '/usr/local/bin2/*'],
                                 regexp => $locate_regexp,
                                 %options);
  $self->{'uniq_ino'} = MyUniqByInode->new;
  $self->{'uniq_md5'} = MyUniqByMD5->new;
  $self->{'my_suffixes_re'} = $suffixes_re;
  return $self;
}

sub next {
  my ($self) = @_;
  unless ($self->{'done_script'}) {
    $self->{'done_script'} = 1;
    my $filename = File::Spec->catfile ($FindBin::Bin, $FindBin::Script);
    ### $filename
    my $content = slurp($filename);
    return ($filename, $content);
  }
  for (;;) {
    defined(my $filename = $self->SUPER::next) or return;
    ### consider: $filename

    next if $filename =~ m{/blib/};
    next if $filename =~ m{/DateTime/TimeZone/};  # data .pm's
    next if $filename =~ m{/Date/Manip/TZ/};      # data .pm's
    next if $filename =~ m{/Date/Manip/Offset/};  # data .pm's
    next if $filename =~ m{/Text/Unidecode/};     # data .pm's
    next if $filename =~ m{/junk.pl};

    my $io;
    if ($filename =~ $compressed_re) {
      $io = IO::Uncompress::AnyInflate->new ($filename);
    } else {
      $io = IO::File->new ($filename, 'r');
    }
    $io or next;
    $self->{'uniq_ino'}->uniq($filename) or next;  # or $io ???

    my $content = _slurp_if_perl ($self, $filename, $io);
    ### content: $content && "ok"
    next if ! defined $content;

    $self->{'uniq_md5'}->uniq_str($content) or next;

    ### return: $filename
    return ($filename, $content);
  }
}

sub _slurp_if_perl {
  my ($self, $filename, $io) = @_;
  ### slurp: "$io"

  my $first = '';
  if ($filename !~ $self->{'my_suffixes_re'}) {
    # file in /bin etc must have #!.../perl
    $io->read($first, 128) or return;
    ### first part: $first

    if ($first !~ m{^#!([^\r\n]*/|[ \t]*)perl[ \t\r\n]}) {
      ### no #!perl
      return;
    }
  }

  my $rest = do { local $/; $io->getline }; # slurp
  if (! defined $rest) {
    ### read error: $!
    return;
  }
  return $first . $rest;
}
1;
__END__