| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | { | 
| 2 |  |  |  |  |  |  | package Module::Locate; | 
| 3 |  |  |  |  |  |  | $Module::Locate::VERSION = '1.80'; | 
| 4 | 5 |  |  | 5 |  | 44567 | use warnings; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 148 |  | 
| 5 | 5 |  |  | 5 |  | 66 | use 5.8.8; | 
|  | 5 |  |  |  |  | 17 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $Cache    = 0; | 
| 8 |  |  |  |  |  |  | our $Global   = 1; | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | my $ident_re = qr{[_a-z]\w*}i; | 
| 11 |  |  |  |  |  |  | my $sep_re   = qr{'|::}; | 
| 12 |  |  |  |  |  |  | our $PkgRe    = qr{\A(?:$ident_re(?:$sep_re$ident_re)*)\z}; | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | my @All      = qw( | 
| 15 |  |  |  |  |  |  | locate get_source acts_like_fh | 
| 16 |  |  |  |  |  |  | mod_to_path is_mod_loaded is_pkg_loaded | 
| 17 |  |  |  |  |  |  | ); | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | sub import { | 
| 20 | 6 |  |  | 6 |  | 978 | my $pkg = caller; | 
| 21 | 6 |  |  |  |  | 26 | my @args = @_[ 1 .. $#_ ]; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 6 |  |  |  |  | 1786 | while(local $_ = shift @args) { | 
| 24 | 7 | 100 | 50 |  |  | 32 | *{ "$pkg\::$_" } = \&$_ and next | 
|  | 6 |  |  |  |  | 1802 |  | 
| 25 |  |  |  |  |  |  | if defined &$_; | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 1 | 50 |  |  |  | 12 | $Cache = shift @args, next | 
| 28 |  |  |  |  |  |  | if /^cache$/i; | 
| 29 |  |  |  |  |  |  |  | 
| 30 | 0 | 0 |  |  |  | 0 | $Global = shift @args, next | 
| 31 |  |  |  |  |  |  | if /^global$/i; | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 0 | 0 |  |  |  | 0 | if(/^:all$/i) { | 
| 34 | 0 |  |  |  |  | 0 | *{ "$pkg\::$_" } = \&$_ | 
| 35 | 0 |  |  |  |  | 0 | for @All; | 
| 36 | 0 |  |  |  |  | 0 | next; | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 0 |  |  |  |  | 0 | warn("not in ".__PACKAGE__." import list: '$_'"); | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 5 |  |  | 5 |  | 35 | use strict; | 
|  | 5 |  |  |  |  | 7 |  | 
|  | 5 |  |  |  |  | 125 |  | 
| 44 |  |  |  |  |  |  |  | 
| 45 | 5 |  |  | 5 |  | 3156 | use IO::File; | 
|  | 5 |  |  |  |  | 40395 |  | 
|  | 5 |  |  |  |  | 789 |  | 
| 46 | 5 |  |  | 5 |  | 4754 | use overload (); | 
|  | 5 |  |  |  |  | 3441 |  | 
|  | 5 |  |  |  |  | 112 |  | 
| 47 | 5 |  |  | 5 |  | 31 | use Carp 'croak'; | 
|  | 5 |  |  |  |  | 11 |  | 
|  | 5 |  |  |  |  | 245 |  | 
| 48 | 5 |  |  | 5 |  | 3930 | use File::Spec::Functions 'catfile'; | 
|  | 5 |  |  |  |  | 4050 |  | 
|  | 5 |  |  |  |  | 2946 |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub get_source { | 
| 51 | 0 |  |  | 0 | 1 | 0 | my $pkg = $_[-1]; | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 0 |  |  |  |  | 0 | my $f = locate($pkg); | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | my $fh = ( acts_like_fh($f) ? | 
| 56 |  |  |  |  |  |  | $f | 
| 57 |  |  |  |  |  |  | : | 
| 58 | 0 | 0 |  |  |  | 0 | do { my $tmp = IO::File->new($f) | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 59 | 0 |  |  |  |  | 0 | or croak("invalid module '$pkg' [$f] - $!"); $tmp } | 
| 60 |  |  |  |  |  |  | ); | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 0 |  |  |  |  | 0 | local $/; | 
| 63 | 0 |  |  |  |  | 0 | return <$fh>; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | sub locate { | 
| 67 | 13 |  |  | 13 | 1 | 4780 | my $pkg = $_[-1]; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 13 | 50 |  |  |  | 30 | croak("Undefined filename provided") | 
| 70 |  |  |  |  |  |  | unless defined $pkg; | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 13 |  |  |  |  | 25 | my $inc_path = mod_to_path($pkg); | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 13 | 50 | 33 |  |  | 44 | return $INC{$inc_path} if exists($INC{$inc_path}) && !wantarray; | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | # On Windows the inc_path will use '/' for directory separator, | 
| 77 |  |  |  |  |  |  | # but when looking for a module, we need to use the OS's separator. | 
| 78 | 13 |  |  |  |  | 24 | my $partial_path = _mod_to_partial_path($pkg); | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 13 |  |  |  |  | 23 | my @paths; | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 13 |  |  |  |  | 26 | for(@INC) { | 
| 83 | 47 | 100 |  |  |  | 93 | if(ref $_) { | 
| 84 | 9 |  |  |  |  | 33 | my $ret = coderefs_in_INC($_, $inc_path); | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | next | 
| 87 | 9 | 50 |  |  |  | 24 | unless defined $ret; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 9 | 100 |  |  |  | 17 | croak("invalid \@INC subroutine return $ret") | 
| 90 |  |  |  |  |  |  | unless acts_like_fh($ret); | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 6 |  |  |  |  | 2441 | return $ret; | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 38 |  |  |  |  | 140 | my $fullpath = catfile($_, $partial_path); | 
| 96 | 38 | 100 |  |  |  | 828 | push(@paths, $fullpath) if -f $fullpath; | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 4 | 100 |  |  |  | 15 | return unless @paths > 0; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 2 | 50 |  |  |  | 9 | return wantarray ? @paths : $paths[0]; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | sub mod_to_path { | 
| 105 | 16 |  |  | 16 | 1 | 27 | my $pkg  = shift; | 
| 106 | 16 |  |  |  |  | 22 | my $path = $pkg; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 16 | 100 |  |  |  | 312 | croak("Invalid package name '$pkg'") | 
| 109 |  |  |  |  |  |  | unless $pkg =~ $Module::Locate::PkgRe; | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | # %INC always uses / as a directory separator, even on Windows | 
| 112 | 15 |  |  |  |  | 71 | $path =~ s!::!/!g; | 
| 113 | 15 | 50 |  |  |  | 49 | $path .= '.pm' unless $path =~ m!\.pm$!; | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 15 |  |  |  |  | 36 | return $path; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | sub coderefs_in_INC { | 
| 119 | 9 |  |  | 9 | 0 | 19 | my($path, $c) = reverse @_; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 9 | 50 |  |  |  | 50 | my $ret = ref($c) eq 'CODE' ? | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | $c->( $c, $path ) | 
| 123 |  |  |  |  |  |  | : | 
| 124 |  |  |  |  |  |  | ref($c) eq 'ARRAY' ? | 
| 125 |  |  |  |  |  |  | $c->[0]->( $c, $path ) | 
| 126 |  |  |  |  |  |  | : | 
| 127 |  |  |  |  |  |  | UNIVERSAL::can($c, 'INC') ? | 
| 128 |  |  |  |  |  |  | $c->INC( $path ) | 
| 129 |  |  |  |  |  |  | : | 
| 130 |  |  |  |  |  |  | warn("invalid reference in \@INC '$c'") | 
| 131 |  |  |  |  |  |  | ; | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 9 |  |  |  |  | 301 | return $ret; | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | sub acts_like_fh { | 
| 137 | 5 |  |  | 5 |  | 30 | no strict 'refs'; | 
|  | 5 |  |  |  |  | 7 |  | 
|  | 5 |  |  |  |  | 2110 |  | 
| 138 |  |  |  |  |  |  | return ( ref $_[0] and ( | 
| 139 |  |  |  |  |  |  | ( ref $_[0] eq 'GLOB' and defined *{$_[0]}{IO} ) | 
| 140 |  |  |  |  |  |  | or ( UNIVERSAL::isa($_[0], 'IO::Handle')          ) | 
| 141 |  |  |  |  |  |  | or ( overload::Method($_[0], '<>')                ) | 
| 142 | 19 |  | 66 | 19 | 1 | 2678 | ) or ref \$_[0] eq 'GLOB' and defined *{$_[0]}{IO}  ); | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | sub is_mod_loaded { | 
| 146 | 3 |  |  | 3 | 1 | 14 | my $mod  = shift; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 3 | 50 |  |  |  | 31 | croak("Invalid package name '$mod'") | 
| 149 |  |  |  |  |  |  | unless $mod =~ $Module::Locate::PkgRe; | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | ## it looks like %INC entries automagically use / as a separator | 
| 152 | 3 |  |  |  |  | 17 | my $path = join '/', split '::' => "$mod.pm"; | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 3 |  | 66 |  |  | 31 | return (exists $INC{$path} && defined $INC{$path}); | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | sub _mod_to_partial_path { | 
| 158 | 13 |  |  | 13 |  | 16 | my $package = shift; | 
| 159 |  |  |  |  |  |  |  | 
| 160 | 13 |  |  |  |  | 76 | return catfile(split(/::/, $package)).'.pm'; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | sub is_pkg_loaded { | 
| 164 | 3 |  |  | 3 | 1 | 6 | my $pkg = shift; | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 3 | 50 |  |  |  | 25 | croak("Invalid package name '$pkg'") | 
| 167 |  |  |  |  |  |  | unless $pkg =~ $Module::Locate::PkgRe; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 3 |  |  |  |  | 22 | my @tbls = map "${_}::", split('::' => $pkg); | 
| 170 | 3 |  |  |  |  | 7 | my $tbl  = \%main::; | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 3 |  |  |  |  | 8 | for(@tbls) { | 
| 173 | 5 | 100 |  |  |  | 16 | return unless exists $tbl->{$_}; | 
| 174 | 4 |  |  |  |  | 27 | $tbl = $tbl->{$_}; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 2 |  |  |  |  | 10 | return !!$pkg; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | q[ That better be make-up, and it better be good ]; | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =pod | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =head1 NAME | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Module::Locate - locate modules in the same fashion as C and C | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | use Module::Locate qw/ locate get_source /; | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | add_plugin( locate "This::Module" ); | 
| 194 |  |  |  |  |  |  | eval 'use strict; ' . get_source('legacy_code.plx'); | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Using C, return the path that C would find for a given | 
| 199 |  |  |  |  |  |  | module or filename (it can also return a filehandle if a reference in C<@INC> | 
| 200 |  |  |  |  |  |  | has been used). This means you can test for the existence, or find the path | 
| 201 |  |  |  |  |  |  | for, modules without having to evaluate the code they contain. | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | This module also comes with accompanying utility functions that are used within | 
| 204 |  |  |  |  |  |  | the module itself (except for C) and are available for import. | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | =over 4 | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | =item C | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | Given function names, the appropriate functions will be exported into the | 
| 213 |  |  |  |  |  |  | caller's package. | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | If C<:all> is passed then all subroutines are exported. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | The B and B options are no longer supported. | 
| 218 |  |  |  |  |  |  | See the BUGS section below. | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | =item C | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | Given a module name as a string (in standard perl bareword format) locate the | 
| 224 |  |  |  |  |  |  | path of the module. If called in a scalar context the first path found will be | 
| 225 |  |  |  |  |  |  | returned, if called in a list context a list of paths where the module was | 
| 226 |  |  |  |  |  |  | found. Also, if references have been placed in C<@INC> then a filehandle will | 
| 227 |  |  |  |  |  |  | be returned, as defined in the C documentation. An empty C is | 
| 228 |  |  |  |  |  |  | used if the module couldn't be located. | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | As of version C<1.7> a filename can also be provided to further mimic the lookup | 
| 231 |  |  |  |  |  |  | behaviour of C/C | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | =item C | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | When provided with a package name, gets the path using C. | 
| 236 |  |  |  |  |  |  | If C returned a path, then the contents of that file are returned | 
| 237 |  |  |  |  |  |  | by C in a scalar. | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | =item C | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | Given a scalar, check if it behaves like a filehandle. Firstly it checks if it | 
| 242 |  |  |  |  |  |  | is a bareword filehandle, then if it inherits from C and lastly if | 
| 243 |  |  |  |  |  |  | it overloads the CE> operator. If this is missing any other standard | 
| 244 |  |  |  |  |  |  | filehandle behaviour, please send me an e-mail. | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =item C | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | Given a module name, | 
| 249 |  |  |  |  |  |  | converts it to a relative path e.g C would become C. | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | Note that this path will always use '/' for the directory separator, | 
| 252 |  |  |  |  |  |  | even on Windows, | 
| 253 |  |  |  |  |  |  | as that's the format used in C<%INC>. | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | =item C | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | Given a module name, return true if the module has been | 
| 258 |  |  |  |  |  |  | loaded (i.e exists in the C<%INC> hash). | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =item C | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | Given a package name (like C), check if the package has an existing | 
| 263 |  |  |  |  |  |  | symbol table loaded (checks by walking the C<%main::> stash). | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | =back | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | A review of modules that can be used to get the path (and often other information) | 
| 270 |  |  |  |  |  |  | for one or more modules: L. | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | L and L. | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | =head1 REPOSITORY | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | L | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | =head1 BUGS | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | In previous versions of this module, if you specified C 1> | 
| 281 |  |  |  |  |  |  | when use'ing this module, | 
| 282 |  |  |  |  |  |  | then looking up a module's path would update C<%INC>, | 
| 283 |  |  |  |  |  |  | even if the module hadn't actually been loaded (yet). | 
| 284 |  |  |  |  |  |  | This meant that if you subsequently tried to load the module, | 
| 285 |  |  |  |  |  |  | it would wrongly not be loaded. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | Bugs are tracked using RT (bug you can also raise Github issues if you prefer): | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | L | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =head1 AUTHOR | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | Dan Brook C<<  >> | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | =head1 LICENSE | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under the same terms as | 
| 298 |  |  |  |  |  |  | Perl itself. | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | =cut |