develooper Front page | perl.perl5.porters | Postings from September 2000

progmod example: PM::Tools::pmpath

From:
Tom Christiansen
Date:
September 5, 2000 08:37
Subject:
progmod example: PM::Tools::pmpath
Message ID:
5413.968168272@chthon
Here's a progmod that does just one thing, not many.  It has
more documentation, too.  

These are all quick prototypes, of course, not really
finished products.  I'll probably end up using getopts.

--tom

#!/usr/bin/perl
#
# pmpath - find path to perl modules
# Tom Christiansen, tchrist@perl.com
# Mon Sep  4 17:40:31 MDT 2000
#
# It's simultaneously a program, a manpage, *and* a module.

package PM::Tools::pmpath;

use strict;
use warnings;

use File::Spec;

our $Am_Standalone = !caller;

exit main(@ARGV) if $Am_Standalone;

require Exporter;
our @ISA = qw/Exporter/;
our @EXPORT = qw/pmpath/;   # only autoexport ourself

sub pmpath {
    my @paths = ();
    usage("Missing argument")   if @_ == 0;
    usage("Too many arguments") if @_ > 1;

    my $mod = $_[0];
    my $filename = File::Spec->join(split(/::/, $mod));
    $filename .= ".pm" unless $filename =~ /\.pm\z/;
    for my $dir (@INC) {
	my $fullpath = File::Spec->join($dir, $filename);
	if (-f $fullpath) {
	    return $fullpath unless wantarray;
	    push @paths, $fullpath;
	} 
    } 

    return undef unless wantarray; # good scalar return already caught
    return @paths;
} 

sub usage {
    if ($Am_Standalone) { 
	print STDERR "@_\n" if @_;
	die "usage: pmpath [-Idir ...] modulename ...\n";
    } else {
	my $msg = @_ ? "Usage error: @_\n" : "";
	die <<'EO_USAGE';
${msg}Usage: 
       $dir  = pmpath("modulename");
       @dirs = pmpath("modulename");
EO_USAGE
    } 
} 

sub main { 
    my @candidates = @_ ? @_ : @ARGV;

    my $status = 0;

    while (@candidates && $candidates[0] =~ /^-I(.*)/) {
	shift @candidates;
	my $libdir = $1 || shift @candidates;
	usage("Missing argument to -I") unless defined $libdir;
	eval "use lib '$libdir'";
	die if $@;
    } 

    unless (@candidates) {
	usage("$0: module argument required");
    } 

    for my $mod (@candidates) {
	my @paths = pmpath($mod);
	$status++ if @paths == 0;
	if (@candidates > 1) {
	    print "$mod: @paths\n";
	} else {
	    print join("\n", @paths), "\n" if @paths;
	} 
    } 
    return $status;
} 

1;

__END__

=head1 NAME

pmpath - find path to perl modules

=head1 SYNOPSIS

As a program:

    $ pmpath IO::Socket
    /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/IO/Socket.pm

    $ pmpath LWP
    /usr/local/lib/perl5/site_perl/5.6.0/LWP.pm
    /usr/local/lib/perl5/site_perl/5.00554/LWP.pm

    $ pmpath CGI LWP
    CGI: /usr/local/lib/perl5/5.6.0/CGI.pm
    LWP: /usr/local/lib/perl5/site_perl/5.6.0/LWP.pm /usr/local/lib/perl5/site_perl/5.00554/LWP.pm

    $ pmpath -I/some/dir MyMod
    /some/Dir/MyMod.pm

First load as a module:

    use PM::Tools qw(pmpath);   

In scalar context:

    my $path = pmpath("CGI");

In list context:

    my @paths = pmpath("LWP");

=head1 DESCRIPTION

B<pmpath> is used to infer the real pathname that Perl will use for
the given module by looking in the directories listed in the
predefined C<@INC> variable.

As a program, B<pmpath> takes one or more module names as arguments.
If only one module name is given, the valid paths to that module
are printed out on separate lines.  If multiple module names are
given, the output is all on one line for each module, but the module
name itself is prepended to that line.  If no path is found, the
program exits non-zero (actually, the number of modules for which
paths were not found).

As a function, B<pmpath> takes only one argument.  In scalar context,
returns the first valid path, or C<undef> on failure.  In list
context, returns all paths, or the null list if none are found.
Raises an exception if called with zero or multiple arguments.

The optional B<-I>I<directory> flag can be passed to the program
(repeatedly, if desired) to set a C<use lib> pragma.  Users of the
function version are assumed capable of diddling C<@INC> on their own.

You can from a larger program, mimic the B<pmpath> program without
using an C<exec> by invoking the program/module's C<main> function:

    PM::Tools::pmpath::main("-Ilib", "LWP");

But then you'll get the output printed on your standard output, not
returned to you.  The return value from C<main> is the normal program
exit status.

Although technically you should provide module names in the same
format as you would C<use> them, allowing Perl to translate C<::>
and add a F<.pm> to the end, if you've already done that, this is
tolerated.

=head1 AUTHOR

Tom Christiansen, tchrist@perl.com




nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About