PPM::Make::Util - Utility functions for PPM::Make


PPM-Make documentation Contained in the PPM-Make distribution.

Index


Code Index:

NAME

Top

  PPM::Make::Util - Utility functions for PPM::Make

SYNOPSIS

Top

  use PPM::Make::Util qw(:all);

DESCRIPTION

Top

This module contains a number of utility functions used by PPM::Make.

fix_path

Ensures a path is a Unix-type path, with no spaces.

  my $path = 'C:\Program Files\';
  my $unix_version = fix_path($path);

load_cs

Loads a CHECKSUMS file into $cksum (adapted from the MD5 check of CPAN.pm)

  my $cksum = load_cs('CHECKSUMS');

verifyMD5

Verify a CHECKSUM for a $file

   my $ok = verifyMD5($cksum, $file);
   print "$file checked out OK" if $ok;

xml_encode

Escapes &, >, <, and ", as well as high ASCII characters.

  my $escaped = xml_encode('Five is > four');

is_core

Tests to see if a module is part of the core, based on whether or not the file is found within a site type of directory.

  my $is_core = is_core('Net::FTP');
  print "Net::FTP is a core module" if $is_core;

is_ap_core

Tests to see if a package is part of the ActivePerl core (at least for recent ActivePerl versions).

  my $is_ap_core = is_ap_core('libwin32');
  print "libwin32 is a core package" if $is_ap_core;

trim

Trims white space.

  my $string = '    This is a sentence.   ';
  my $trimmed = trim($string);

file_to_dist

In scalar context, returns a CPAN distribution name filename based on an input file A/AB/ABC/filename-1.23.tar.gz:

  my $file = 'A/AB/ABC/defg-1.23.tar.gz';
  my $dist = file_to_dist($file);

In a list context, returns both the distribution name filename and the version number 1.23:

  my $file = 'A/AB/ABC/defg-1.23.tar.gz';
  my ($dist, $version) = file_to_dist($cpan_file);




ppd2cpan_version

Converts a ppd-type of version string (eg, 1,23,0,0) into a ppd one of the form 1.23:

  my $s = "1,23,0,0";
  my $v = ppd2cpan_version($v);

cpan2ppd_version

Converts a cpan-type of version string (eg, 1.23) into a ppd one of the form 1,23,0,0:

  my $v = 1.23;
  my $s = cpan2ppd_version($v);

parse_ppd

Parse a ppd file or a string.

  my $ppd = 'package.ppd';
  my $d = parse_ppd($ppd);
  print $d->{ABSTRACT};
  print $d->{OS}->{NAME};

  my $e = parse_ppd($ppd, 'MSWin32-x86-multi-thread');
  print $e->{ABSTRACT};

This routine takes a required argument of a ppd file containing a .ppd extension or a string and, optionally, an ARCHITECTURE name to restrict the results to. It returns a data structure containing the information of the ppd file or string:

    $d->{SOFTPKG}->{NAME}
    $d->{SOFTPKG}->{VERSION}
    $d->{TITLE}
    $d->{AUTHOR}
    $d->{ABSTRACT}
    $d->{PROVIDE}
    $d->{DEPENDENCY}
    $d->{REQUIRE}
    $d->{OS}->{NAME}
    $d->{ARCHITECTURE}->{NAME}
    $d->{CODEBASE}->{HREF}
    $d->{INSTALL}->{EXEC}
    $d->{INSTALL}->{SCRIPT}
    $d->{INSTALL}->{HREF}

The PROVIDE, REQUIRE and DEPENDENDENCY tags are array references containing lists of, respectively, the prerequisites required and the modules supplied by the package, with keys of NAME and VERSION.

If there is more than one IMPLEMENTATION section in the ppd file, all the results except for the SOFTPKG elements and TITLE, AUTHOR, and ABSTRACT will be placed in a $d->{IMPLENTATION} array reference. If an optional second argument is passed to parse_ppd($file, $arch), this will filter out all implementation sections except for the specified ARCHITECTURE given by $arch.

src_and_build

Returns the source and build directories used with CPAN.pm, if present. If not, returns those used with PPM, if those are present. If neither of these are available, returns the system temp directory.

  my ($src_dir, $build_dir)= src_and_build;

tempfile

Generates the name of a random temporary file.

  my $tmpfile = tempfile;

parse_version

Extracts a version string from a module file.

  my $version = parse_version('C:/Perl/lib/CPAN.pm');

parse_abstract

Attempt to obtain an abstract from a module file.

  my $package = 'CPAN';
  my $file = 'C:/Perl/lib/CPAN.pm';
  my $abstract = parse_abstract($package, $file);

cpan_file {

Given a file of the form file.tar.gz and a CPAN id of the form <ABCDEFG>, will return the CPAN file A/AB/ABCDEFG/file.tar.gz.

  my $cpanid = 'GBARR';
  my $file = 'libnet-1.23.tar.gz';
  my $cpan_file = cpan_file($cpanid, $file);

url_list

Gets a list of CPAN mirrors, incorporating any from CPAN.pm.

  my @list = url_list();

COPYRIGHT

Top

SEE ALSO

Top

PPM.


PPM-Make documentation Contained in the PPM-Make distribution.
package PPM::Make::Util;
use strict;
use warnings;
use Exporter;
use File::Basename;
use Safe;
use File::Copy;
use XML::Parser;
use Digest::MD5;
require File::Spec;
use File::Path;
use Config;
use LWP::Simple qw(getstore is_success);
use CPAN::DistnameInfo;
use File::HomeDir;
use HTML::Entities qw(encode_entities encode_entities_numeric);
use File::Spec;
use PPM::Make::Config qw(WIN32 HAS_CPAN HAS_PPM HAS_MB ACTIVEPERL);

our $VERSION = '0.97';

my %encode = ('&' => '&amp;', '>' => '&gt;',
	      '<' => '&lt;', '"' => '&quot;');

use base qw(Exporter);

our (@EXPORT_OK, %EXPORT_TAGS, $protocol, $ext, $src_dir, $build_dir,
     @url_list, $ERROR);
$protocol = qr{^(http|ftp)://};
$ext = qr{\.(tar\.gz|tgz|tar\.Z|zip)};
@url_list = url_list();

my @exports = qw(load_cs verifyMD5 xml_encode parse_version $ERROR
                 is_core is_ap_core url_list
		 trim parse_ppd parse_abstract
                 ppd2cpan_version cpan2ppd_version tempfile
                 file_to_dist cpan_file fix_path
		 $src_dir $build_dir @url_list);

%EXPORT_TAGS = (all => [@exports]);
@EXPORT_OK = (@exports);

my %ap_core = map {$_ => 1} qw(
			       Archive-Tar
			       Archive-Zip
			       Compress-Zlib
			       Data-Dump
			       Digest-HMAC
			       Digest-MD2
			       Digest-MD4
			       Digest-SHA1
			       File-CounterFile
			       Font-AFM
			       HTML-Parser
			       HTML-Tagset
			       HTML-Tree
			       IO-String
			       IO-Zlib
			       libwin32
			       libwww-perl
			       MD5
			       MIME-Base64-Scripts
			       SOAP-Lite
			       Term-ReadLine-Perl
			       TermReadKey
			       Text-Autoformat
			       Text-Reform
			       Tk
			       Unicode-String
			       URI
			       XML-Parser
			       XML-Simple  );

if (WIN32 and ACTIVEPERL and eval { Win32::BuildNumber() > 818 }) {
  $ap_core{'DBI'}++; $ap_core{'DBD-SQLite'}++;
}
src_and_build();

my %Escape = ('&' => 'amp',
	      '>' => 'gt',
	      '<' => 'lt',
	      '"' => 'quot'
	     );

my %dists;
my $info_soap;
my $info_uri = 'http://theoryx5.uwinnipeg.ca/Apache/InfoServer';
my $info_proxy = 'http://theoryx5.uwinnipeg.ca/cgi-bin/ppminfo.cgi';

sub fix_path {
  my $path = shift;
  $path = Win32::GetShortPathName($path);
  $path =~ s!\\!/!g;
  $path =~ s!/$!!;
  return $path;
}

sub load_cs {
  my $cs = shift;
  open(my $fh, $cs);
  unless ($fh) {
    $ERROR = qq{Could not open "$cs": $!};
    return;
  }
  local($/);
  my $eval = <$fh>;
  close $fh;
  $eval =~ s/\015?\012/\n/g;
  my $comp = Safe->new();
  my $cksum = $comp->reval($eval);
  if ($@) {
    $ERROR = qq{eval of "$cs" failed: $@};
    return;
  }
  return $cksum;
}

sub verifyMD5 {
  my ($cksum, $file) = @_;
  my ($is, $should);
  open (my $fh, $file);
  unless ($fh) {
    $ERROR = qq{Cannot open "$file": $!};
    return;
  }
  binmode($fh);
  unless ($is = Digest::MD5->new->addfile($fh)->hexdigest) {
    $ERROR = qq{Could not compute checksum for "$file": $!};
    close $fh;
    return;
  }
  close $fh;
  if ($should = $cksum->{$file}->{md5}) {
    my $test = ($is eq $should);
    printf qq{  Checksum for "$file" is %s\n}, 
      ($test) ? 'OK.' : 'NOT OK.';
    return $test;
  }
  else {
    $ERROR = qq{Checksum data for "$file" not present.};
    return;
  }
}

sub xml_encode {
    my $s = shift;
    return unless $s;
    $s =~ s/(&(?!(amp|lt|gt|quot);)|>|<|\")/$encode{$1}/g;
    return encode_entities_numeric($s, "\177-\377");
}

sub is_core {
  my $m = shift;
  return unless $m;
  $m =~ s!::|-!/!g;
  $m .= '.pm';
  my $is_core = (-e File::Spec->catfile($Config{privlibexp}, $m)) ? 1 : 0;
  return $is_core;
}

sub is_ap_core {
  my $p = shift;
  return unless defined $p;
  return defined $ap_core{$p} ? 1 : 0;
}

sub trim {
  local $_ = shift;
  s/^\s*//;
  s/\s*$//;
  return $_;
}


sub file_to_dist {
  my $cpan_file = shift;
  return unless $cpan_file;
  my $d = CPAN::DistnameInfo->new($cpan_file);
  my ($dist, $version) = ($d->dist, $d->version);
  unless ($dist and $version) {
      $ERROR = qq{Could not find distribution name from $cpan_file.};
      return;
  }
  return wantarray? ($dist, $version) : $dist;
}

sub ppd2cpan_version {
  local $_ = shift;
  s/(,0)*$//;
  tr/,/./;
  return $_;
}

sub cpan2ppd_version {
  local $_ = shift;
  return join ',', (split (/\./, $_), (0)x4)[0..3];
}


my $i;

sub parse_ppd {
  my $file = shift;
  my $arch = shift;
  my $is_a_file = ($file =~ /\.ppd/);
  if ($is_a_file) {
    unless (-e $file) {
      $ERROR = qq{$file not found.};
      return;
    }
  }
  my $p = XML::Parser->new(Style => 'Subs',
			   Handlers => {Char => \&ppd_char,
					Start => \&ppd_start,
					End => \&ppd_end,
					Init => \&ppd_init,
					Final => \&ppd_final,
				       },
			  );
  my $d = $is_a_file ? $p->parsefile($file) : $p->parse($file);
  my $implem = $d->{IMPLEMENTATION};
  my $size = scalar @$implem;
  if ($size == 1) {
    $d->{PROVIDE} = $implem->[0]->{PROVIDE} || [];
    $d->{DEPENDENCY} = $implem->[0]->{DEPENDENCY} || [];
    $d->{REQUIRE} = $implem->[0]->{DEPENDENCY} || [];
    $d->{OS}->{NAME} = $implem->[0]->{OS}->{NAME} || '';
    $d->{ARCHITECTURE}->{NAME} = $implem->[0]->{ARCHITECTURE}->{NAME} || '';
    $d->{CODEBASE}->{HREF} = $implem->[0]->{CODEBASE}->{HREF};
    $d->{INSTALL}->{EXEC} = $implem->[0]->{INSTALL}->{EXEC};
    $d->{INSTALL}->{SCRIPT} = $implem->[0]->{INSTALL}->{SCRIPT};
    $d->{INSTALL}->{HREF} = $implem->[0]->{INSTALL}->{HREF};
  }
  elsif (defined $arch) {
    my $flag = 0;
    my $i;
    for ($i=0; $i<$size; $i++) {
      if ($implem->[$i]->{ARCHITECTURE}->{NAME} eq $arch) {
	$flag++;
	last;
      }
    }
    return unless $flag;
    $d->{PROVIDE} = $implem->[$i]->{PROVIDE} || [];
    $d->{DEPENDENCY} = $implem->[$i]->{DEPENDENCY} || [];
    $d->{REQUIRE} = $implem->[$i]->{DEPENDENCY} || [];
    $d->{OS}->{NAME} = $implem->[$i]->{OS}->{NAME} || '';
    $d->{ARCHITECTURE}->{NAME} = $implem->[$i]->{ARCHITECTURE}->{NAME} || '';
    $d->{CODEBASE}->{HREF} = $implem->[$i]->{CODEBASE}->{HREF};
    $d->{INSTALL}->{EXEC} = $implem->[$i]->{INSTALL}->{EXEC};
    $d->{INSTALL}->{SCRIPT} = $implem->[$i]->{INSTALL}->{SCRIPT};
    $d->{INSTALL}->{HREF} = $implem->[$i]->{INSTALL}->{HREF};
  }
  return $d;
}

sub ppd_init {
  my $self = shift;
  $i = 0;
  $self->{_mydata} = {
		      SOFTPKG => {NAME => '', VERSION => ''},
		      TITLE => '',
		      AUTHOR => '',
		      ABSTRACT => '',
		      PROVIDE => [],
		      IMPLEMENTATION => [],
		      OS => {NAME => ''},
		      ARCHITECTURE => {NAME => ''},
		      CODEBASE => {HREF => ''},
		      DEPENDENCY => [],
		      REQUIRE => [],
		      INSTALL => {EXEC => '', SCRIPT => '', HREF => ''},
		      wanted => {TITLE => 1, ABSTRACT => 1, AUTHOR => 1},
		      _current => '',
		     };
}

sub ppd_start {
  my ($self, $tag, %attrs) = @_;
  my $internal = $self->{_mydata};
  $internal->{_current} = $tag;
 SWITCH: {
    ($tag eq 'SOFTPKG') and do {
      $internal->{SOFTPKG}->{NAME} = $attrs{NAME};
      $internal->{SOFTPKG}->{VERSION} = $attrs{VERSION};
      last SWITCH;
    };
    ($tag eq 'PROVIDE') and do {
      my $name = $attrs{NAME};
      my $version = $attrs{VERSION};
      if ($version) {
	push @{$internal->{IMPLEMENTATION}->[$i]->{PROVIDE}},
	  {NAME => $name, VERSION => $version};
      }
      else {
	push @{$internal->{IMPLEMENTATION}->[$i]->{PROVIDE}},
	  {NAME => $name};	
      }
      last SWITCH;
    };
    ($tag eq 'CODEBASE') and do {
      $internal->{IMPLEMENTATION}->[$i]->{CODEBASE}->{HREF} =
	$attrs{HREF};
      last SWITCH;
    };
    ($tag eq 'OS') and do {
      $internal->{IMPLEMENTATION}->[$i]->{OS}->{NAME} =
	$attrs{NAME};
      last SWITCH;
    };
    ($tag eq 'ARCHITECTURE') and do {
      $internal->{IMPLEMENTATION}->[$i]->{ARCHITECTURE}->{NAME} =
	$attrs{NAME};
      last SWITCH;
    };
    ($tag eq 'INSTALL') and do {
      $internal->{IMPLEMENTATION}->[$i]->{INSTALL}->{EXEC} =
	$attrs{EXEC};
      $internal->{IMPLEMENTATION}->[$i]->{INSTALL}->{HREF} =
	$attrs{HREF};
      last SWITCH;
    };
    ($tag eq 'DEPENDENCY') and do {
      push @{$internal->{IMPLEMENTATION}->[$i]->{DEPENDENCY}},
	{NAME => $attrs{NAME}, VERSION => $attrs{VERSION}};
      last SWITCH;
    };
    ($tag eq 'REQUIRE') and do {
      push @{$internal->{IMPLEMENTATION}->[$i]->{REQUIRE}},
	{NAME => $attrs{NAME}, VERSION => $attrs{VERSION}};
      last SWITCH;
    };
  }
}

sub ppd_char {
  my ($self, $string) = @_;
  my $internal = $self->{_mydata};
  my $tag = $internal->{_current};
  if ($tag and $internal->{wanted}->{$tag}) {
    $internal->{$tag} .= xml_encode($string);
  }
  elsif ($tag and $tag eq 'INSTALL') {
    $internal->{IMPLEMENTATION}->[$i]->{INSTALL}->{SCRIPT} .= $string;
  }
  else {
  }
}

sub ppd_end {
  my ($self, $tag) = @_;
  $i++ if ($tag eq 'IMPLEMENTATION');
  delete $self->{_mydata}->{_current};
}

sub ppd_final {
  my $self = shift;
  return $self->{_mydata};
}

sub src_and_build {
  return if ($src_dir and $build_dir);
 SWITCH: {
    HAS_CPAN and do {
      $src_dir = $CPAN::Config->{keep_source_where};
      $build_dir = $CPAN::Config->{build_dir};
      last SWITCH if ($src_dir and $build_dir);
    };
    HAS_PPM and do {
      my $d = parse_ppm();
      $src_dir = $d->{OPTIONS}->{BUILDDIR};
      $build_dir = $src_dir;
      last SWITCH if ($src_dir and $build_dir);
    };
    $src_dir = File::Spec->tmpdir() || '.';
    $build_dir = $src_dir;
  }
}

sub tempfile {
  my $rand = int(rand $$);
  return File::Spec->catfile(File::Spec->tmpdir(), 
                             'ppm-make.' . $rand);
}

# from ExtUtils::MM_Unix
sub parse_version {
  my $parsefile = shift;
  return unless -e $parsefile;
  my $version;
  local $/ = "\n";
  my $fh;
  unless (open($fh, $parsefile)) {
    $ERROR = "Could not open '$parsefile': $!";
    return;
  }
  my $inpod = 0;
  while (<$fh>) {
    $inpod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $inpod;
    next if $inpod || /^\s*\#/;
    chop;
    # next unless /\$(([\w\:\']*)\bVERSION)\b.*\=/;
    next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/;
    my $eval = qq{
                                    package ExtUtils::MakeMaker::_version;
                                    no strict;
                                    
                                    local $1$2;
                                    \$$2=undef; do {
                                        $_;
                                        return \$$2;
                                    };
                                  };
    local $^W = 0;
    $version = eval($eval);
    warn "Could not eval '$eval' in $parsefile: $@" if $@;
    last;
  }
  close $fh;
  return $version;
}

sub parse_abstract {
  my ($package, $file) = @_;
  my $basename = basename($file, qr/\.\w+$/);
  (my $stripped = $basename) =~ s!\.\w+$!!;
  (my $trans = $package) =~ s!-!::!g;
  my $result;
  my $inpod = 0;
  open(my $fh, $file) or die "Couldn't open $file: $!";
  while (<$fh>) {
    $inpod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $inpod;
    next if !$inpod;
    chop;
    next unless /^\s*($package|$basename|$stripped|$trans)\s+--*\s+(.*)/;
    $result = $2;
    last;
  }
  close($fh);
  return unless $result;
  chomp($result);
  return $result;
}

sub cpan_file {
  my ($cpanid, $file) = @_;
  return $file if $file =~ m!/!;
  (my $cpan_loc = $cpanid) =~ s{^(\w)(\w)(.*)}{$1/$1$2/$1$2$3};
  return qq{$cpan_loc/$file};
}

sub url_list {
  my @urls;
  if (HAS_CPAN and defined $CPAN::Config->{urllist} and
      ref($CPAN::Config->{urllist}) eq 'ARRAY') {
    push @urls, @{$CPAN::Config->{urllist}};
  }
  push @urls, 'ftp://ftp.cpan.org', 'http://www.cpan.org';
  return @urls;
}

# from Module::Build
sub prompt {
  my ($mess, $def) = @_;
  die "prompt() called without a prompt message" unless @_;
  
# Pipe?
  my $INTERACTIVE = -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT));
  
  ($def, my $dispdef) = defined $def ? ($def, "[$def] ") : ('', ' ');

  {
    local $|=1;
    print "$mess $dispdef";
  }
  my $ans;
  if ($INTERACTIVE) {
    $ans = <STDIN>;
    if ( defined $ans ) {
      chomp $ans;
    } else { # user hit ctrl-D
      print "\n";
    }
  }
  
  unless (defined($ans) and length($ans)) {
    print "$def\n";
    $ans = $def;
  }
  
  return $ans;
}

1;

__END__