#!/usr/bin/perl

use strict;
use warnings;

# Removing this dependency
# use Text::Trim qw( trim );
use List::Util qw( max );
use Module::Util qw( :all );

use Pod::Usage;
use Getopt::Long qw( :config posix_default bundling );

=head1 NAME

pm_which - find installed modules

=head1 SYNOPSIS

    pm_which [ options ] module(s)

    Returns the path to the given module(s)

=head2 OPTIONS
  
    -q, --quiet     Just print paths
    -p, --paths	    Just convert the module name into a relative path
    -a, --all	    Print all paths, not just the first one found
    -n, --namspace  Print all modules in the given namespace
    -m              Only print module names, not paths
    -I libpath	    Add a path to search (like perl -I)
    -d, --dump	    Dump paths that would be searched (@INC by default)
    -h, --help	    Print this message
    -               Read modules from stdin, one per line
  
=cut

our($quiet, $all, $namespace, $name_only, $paths, $dump, $stdin);
our @search = @INC;

GetOptions(
    'q|quiet'       => \$quiet,
    'a|all'         => \$all,
    'p|paths'       => \$paths,
    'd|dump'        => \$dump,
    'n|namespace'   => \$namespace,
    'm'             => \$name_only,
    ''	            => \$stdin,
    'h|help'        => sub { pod2usage(-exitval => 0) },
    'I=s'           => sub { unshift @search, $_[1] },
) or pod2usage( -exitval => 1 );

if ($dump) {
    print join("\n", @search), "\n";
    exit 0;
}

our @modules = @ARGV;

# Also read module names from STDIN if we have '-' switch

# Removing Text::Trim dependency
# push @modules, trim <STDIN> if $stdin;

if ($stdin) {
    my @from_stdin = <STDIN>;

    for (@from_stdin) {
        s/\A\s*//;
        s/\s*\z//;
    }

    push @modules, @from_stdin;
}

pod2usage( -exitval => 1, -message => 'No modules selected')
    unless @modules;

if ($namespace) {
    @modules = map { $_, find_in_namespace($_) } @modules;
}

if ($name_only) {
    print "$_\n" for @modules;
    exit 0;
}

# Find the maximum length of module names
my $width = max map { length } @modules;
my $exit = 0;

MODULE:
for my $module (@modules) {
    unless (is_valid_module_name($module)) {
        # Maybe the module is actually a path:
        my $new = path_to_module($module)
               || fs_path_to_module($module);

        if ($new) {
            $module = $new;
        }
        else {
            $exit = 2;
            warn "'$module' is not a valid module name\n";
            next MODULE;
        }
    }

    if ($paths) {
        print module_path $module;
        next MODULE;
    }

    my @paths = $all ? all_installed($module) : find_installed($module);

    my $prefix = '';
    unless ($quiet or @modules == 1) {
        # print the module name as well as the path
        $prefix = sprintf("%-${width}s - ", $module);
    }

    if (@paths) {
        for my $path (@paths) {
            print $prefix, $path, "\n";
        }
    }
    else {
        $exit = 2;
        print $prefix, "not found\n" unless $quiet;
    }
}

exit $exit;

__END__

=head1 DESCRIPTION

This tool reports the locations of installed perl modules.

By default it lists the location of each specified module that would be loaded
by require.

=head1 OPTION DETAILS

=head2 quiet

Under quiet mode, module names are suppressed and missing modules are not
reported.

Normal output:

    $ pm_which Module::One Module::Two Missing::Module
    Module::One     - /path/to/Module/One.pm
    Module::Two     - /path/to/Module/Two.pm
    Missing::Module - not found

Under --quiet:

    $ pm_which -q Module::One Module::Two Missing::Module
    /path/to/Module/One.pm
    /path/to/Module/Two.pm

=head2 paths

In "paths" mode, each module is simply converted into a relative file path. This
is possible even when the module is not installed.

    $ pm_which -p Missing::Module
    Missing/Module.pm

=head2 all

When the "all" switch is specified, all installed modules will be reported, not
just the first one. This is useful for determining when there is a module
installed in multiple locations.

    $ pm_which -a MyModule
    /path/to/MyModule.pm
    /home/me/perl/MyModule.pm

=head2 namespace

Arguments are taken as namespaces to search under.

    $ pm_which -n MyModule
    MyModule            - /path/to/MyModule.pm
    MyModule::Foo       - /path/to/MyModule/Foo.pm
    MyModule::Foo::Bar  - /path/to/MyModule/Foo/Bar.pm

=head2 -m

Disables printing of module paths. This is only really useful in conjunction with --namespace.

    $ pm_which -nm MyModule
    MyModule
    MyModule::Foo
    MyModule::Foo::Bar

=head2 dump

Dumps the paths that would be searched and exits. This is @INC modified by any
-I switches.

    $ pm_which --dump
    /usr/lib/perl5/site_perl/5.8.6
    /usr/lib/perl5/vendor_perl/5.8.6
    ...

    $ pm_which -I lib --dump -I blib/lib
    lib
    blib/lib
    /usr/lib/perl5/site_perl/5.8.6
    ...

=head1 EXIT CODES

=over 4

=item * 0 - Everything was OK

=item * 1 - Initialisation failed (bad switches?)

=item * 2 - Some modules were not installed

=back

=head1 SEE ALSO

This utility comes with L<Module::Util>.

=head1 AUTHOR

Matt Lawrence E<lt>mattlaw@cpan.orgE<gt>


