1*0Sstevel@tonic-gate############################################################################# 2*0Sstevel@tonic-gate# Pod/Find.pm -- finds files containing POD documentation 3*0Sstevel@tonic-gate# 4*0Sstevel@tonic-gate# Author: Marek Rouchal <marekr@cpan.org> 5*0Sstevel@tonic-gate# 6*0Sstevel@tonic-gate# Copyright (C) 1999-2000 by Marek Rouchal (and borrowing code 7*0Sstevel@tonic-gate# from Nick Ing-Simmon's PodToHtml). All rights reserved. 8*0Sstevel@tonic-gate# This file is part of "PodParser". Pod::Find is free software; 9*0Sstevel@tonic-gate# you can redistribute it and/or modify it under the same terms 10*0Sstevel@tonic-gate# as Perl itself. 11*0Sstevel@tonic-gate############################################################################# 12*0Sstevel@tonic-gate 13*0Sstevel@tonic-gatepackage Pod::Find; 14*0Sstevel@tonic-gate 15*0Sstevel@tonic-gateuse vars qw($VERSION); 16*0Sstevel@tonic-gate$VERSION = 0.24_01; ## Current version of this package 17*0Sstevel@tonic-gaterequire 5.005; ## requires this Perl version or later 18*0Sstevel@tonic-gateuse Carp; 19*0Sstevel@tonic-gate 20*0Sstevel@tonic-gate############################################################################# 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate=head1 NAME 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gatePod::Find - find POD documents in directory trees 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate=head1 SYNOPSIS 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate use Pod::Find qw(pod_find simplify_name); 29*0Sstevel@tonic-gate my %pods = pod_find({ -verbose => 1, -inc => 1 }); 30*0Sstevel@tonic-gate foreach(keys %pods) { 31*0Sstevel@tonic-gate print "found library POD `$pods{$_}' in $_\n"; 32*0Sstevel@tonic-gate } 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate print "podname=",simplify_name('a/b/c/mymodule.pod'),"\n"; 35*0Sstevel@tonic-gate 36*0Sstevel@tonic-gate $location = pod_where( { -inc => 1 }, "Pod::Find" ); 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate=head1 DESCRIPTION 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gateB<Pod::Find> provides a set of functions to locate POD files. Note that 41*0Sstevel@tonic-gateno function is exported by default to avoid pollution of your namespace, 42*0Sstevel@tonic-gateso be sure to specify them in the B<use> statement if you need them: 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate use Pod::Find qw(pod_find); 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate=cut 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gateuse strict; 49*0Sstevel@tonic-gate#use diagnostics; 50*0Sstevel@tonic-gateuse Exporter; 51*0Sstevel@tonic-gateuse File::Spec; 52*0Sstevel@tonic-gateuse File::Find; 53*0Sstevel@tonic-gateuse Cwd; 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gateuse vars qw(@ISA @EXPORT_OK $VERSION); 56*0Sstevel@tonic-gate@ISA = qw(Exporter); 57*0Sstevel@tonic-gate@EXPORT_OK = qw(&pod_find &simplify_name &pod_where &contains_pod); 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate# package global variables 60*0Sstevel@tonic-gatemy $SIMPLIFY_RX; 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate=head2 C<pod_find( { %opts } , @directories )> 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gateThe function B<pod_find> searches for POD documents in a given set of 65*0Sstevel@tonic-gatefiles and/or directories. It returns a hash with the file names as keys 66*0Sstevel@tonic-gateand the POD name as value. The POD name is derived from the file name 67*0Sstevel@tonic-gateand its position in the directory tree. 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gateE.g. when searching in F<$HOME/perl5lib>, the file 70*0Sstevel@tonic-gateF<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>, 71*0Sstevel@tonic-gatewhereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be 72*0Sstevel@tonic-gateI<Myclass::Subclass>. The name information can be used for POD 73*0Sstevel@tonic-gatetranslators. 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gateOnly text files containing at least one valid POD command are found. 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gateA warning is printed if more than one POD file with the same POD name 78*0Sstevel@tonic-gateis found, e.g. F<CPAN.pm> in different directories. This usually 79*0Sstevel@tonic-gateindicates duplicate occurrences of modules in the I<@INC> search path. 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gateB<OPTIONS> The first argument for B<pod_find> may be a hash reference 82*0Sstevel@tonic-gatewith options. The rest are either directories that are searched 83*0Sstevel@tonic-gaterecursively or files. The POD names of files are the plain basenames 84*0Sstevel@tonic-gatewith any Perl-like extension (.pm, .pl, .pod) stripped. 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate=over 4 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate=item C<-verbose =E<gt> 1> 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gatePrint progress information while scanning. 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate=item C<-perl =E<gt> 1> 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gateApply Perl-specific heuristics to find the correct PODs. This includes 95*0Sstevel@tonic-gatestripping Perl-like extensions, omitting subdirectories that are numeric 96*0Sstevel@tonic-gatebut do I<not> match the current Perl interpreter's version id, suppressing 97*0Sstevel@tonic-gateF<site_perl> as a module hierarchy name etc. 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate=item C<-script =E<gt> 1> 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gateSearch for PODs in the current Perl interpreter's installation 102*0Sstevel@tonic-gateB<scriptdir>. This is taken from the local L<Config|Config> module. 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate=item C<-inc =E<gt> 1> 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gateSearch for PODs in the current Perl interpreter's I<@INC> paths. This 107*0Sstevel@tonic-gateautomatically considers paths specified in the C<PERL5LIB> environment 108*0Sstevel@tonic-gateas this is prepended to I<@INC> by the Perl interpreter itself. 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate=back 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate=cut 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate# return a hash of the POD files found 115*0Sstevel@tonic-gate# first argument may be a hashref (options), 116*0Sstevel@tonic-gate# rest is a list of directories to search recursively 117*0Sstevel@tonic-gatesub pod_find 118*0Sstevel@tonic-gate{ 119*0Sstevel@tonic-gate my %opts; 120*0Sstevel@tonic-gate if(ref $_[0]) { 121*0Sstevel@tonic-gate %opts = %{shift()}; 122*0Sstevel@tonic-gate } 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate $opts{-verbose} ||= 0; 125*0Sstevel@tonic-gate $opts{-perl} ||= 0; 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate my (@search) = @_; 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate if($opts{-script}) { 130*0Sstevel@tonic-gate require Config; 131*0Sstevel@tonic-gate push(@search, $Config::Config{scriptdir}) 132*0Sstevel@tonic-gate if -d $Config::Config{scriptdir}; 133*0Sstevel@tonic-gate $opts{-perl} = 1; 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate if($opts{-inc}) { 137*0Sstevel@tonic-gate if ($^O eq 'MacOS') { 138*0Sstevel@tonic-gate # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS 139*0Sstevel@tonic-gate my @new_INC = @INC; 140*0Sstevel@tonic-gate for (@new_INC) { 141*0Sstevel@tonic-gate if ( $_ eq '.' ) { 142*0Sstevel@tonic-gate $_ = ':'; 143*0Sstevel@tonic-gate } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) { 144*0Sstevel@tonic-gate $_ = ':'. $_; 145*0Sstevel@tonic-gate } else { 146*0Sstevel@tonic-gate $_ =~ s|^\./|:|; 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate } 149*0Sstevel@tonic-gate push(@search, grep($_ ne File::Spec->curdir, @new_INC)); 150*0Sstevel@tonic-gate } else { 151*0Sstevel@tonic-gate push(@search, grep($_ ne File::Spec->curdir, @INC)); 152*0Sstevel@tonic-gate } 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate $opts{-perl} = 1; 155*0Sstevel@tonic-gate } 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate if($opts{-perl}) { 158*0Sstevel@tonic-gate require Config; 159*0Sstevel@tonic-gate # this code simplifies the POD name for Perl modules: 160*0Sstevel@tonic-gate # * remove "site_perl" 161*0Sstevel@tonic-gate # * remove e.g. "i586-linux" (from 'archname') 162*0Sstevel@tonic-gate # * remove e.g. 5.00503 163*0Sstevel@tonic-gate # * remove pod/ if followed by *.pod (e.g. in pod/perlfunc.pod) 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate # Mac OS: 166*0Sstevel@tonic-gate # * remove ":?site_perl:" 167*0Sstevel@tonic-gate # * remove :?pod: if followed by *.pod (e.g. in :pod:perlfunc.pod) 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate if ($^O eq 'MacOS') { 170*0Sstevel@tonic-gate $SIMPLIFY_RX = 171*0Sstevel@tonic-gate qq!^(?i:\:?site_perl\:|\:?pod\:(?=.*?\\.pod\\z))*!; 172*0Sstevel@tonic-gate } else { 173*0Sstevel@tonic-gate $SIMPLIFY_RX = 174*0Sstevel@tonic-gate qq!^(?i:site(_perl)?/|\Q$Config::Config{archname}\E/|\\d+\\.\\d+([_.]?\\d+)?/|pod/(?=.*?\\.pod\\z))*!; 175*0Sstevel@tonic-gate } 176*0Sstevel@tonic-gate } 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate my %dirs_visited; 179*0Sstevel@tonic-gate my %pods; 180*0Sstevel@tonic-gate my %names; 181*0Sstevel@tonic-gate my $pwd = cwd(); 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate foreach my $try (@search) { 184*0Sstevel@tonic-gate unless(File::Spec->file_name_is_absolute($try)) { 185*0Sstevel@tonic-gate # make path absolute 186*0Sstevel@tonic-gate $try = File::Spec->catfile($pwd,$try); 187*0Sstevel@tonic-gate } 188*0Sstevel@tonic-gate # simplify path 189*0Sstevel@tonic-gate # on VMS canonpath will vmsify:[the.path], but File::Find::find 190*0Sstevel@tonic-gate # wants /unixy/paths 191*0Sstevel@tonic-gate $try = File::Spec->canonpath($try) if ($^O ne 'VMS'); 192*0Sstevel@tonic-gate $try = VMS::Filespec::unixify($try) if ($^O eq 'VMS'); 193*0Sstevel@tonic-gate my $name; 194*0Sstevel@tonic-gate if(-f $try) { 195*0Sstevel@tonic-gate if($name = _check_and_extract_name($try, $opts{-verbose})) { 196*0Sstevel@tonic-gate _check_for_duplicates($try, $name, \%names, \%pods); 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate next; 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate my $root_rx = $^O eq 'MacOS' ? qq!^\Q$try\E! : qq!^\Q$try\E/!; 201*0Sstevel@tonic-gate File::Find::find( sub { 202*0Sstevel@tonic-gate my $item = $File::Find::name; 203*0Sstevel@tonic-gate if(-d) { 204*0Sstevel@tonic-gate if($item =~ m{/(?:RCS|CVS|SCCS|\.svn)$}) { 205*0Sstevel@tonic-gate $File::Find::prune = 1; 206*0Sstevel@tonic-gate return; 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate elsif($dirs_visited{$item}) { 209*0Sstevel@tonic-gate warn "Directory '$item' already seen, skipping.\n" 210*0Sstevel@tonic-gate if($opts{-verbose}); 211*0Sstevel@tonic-gate $File::Find::prune = 1; 212*0Sstevel@tonic-gate return; 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate else { 215*0Sstevel@tonic-gate $dirs_visited{$item} = 1; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate if($opts{-perl} && /^(\d+\.[\d_]+)\z/s && eval "$1" != $]) { 218*0Sstevel@tonic-gate $File::Find::prune = 1; 219*0Sstevel@tonic-gate warn "Perl $] version mismatch on $_, skipping.\n" 220*0Sstevel@tonic-gate if($opts{-verbose}); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate return; 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate if($name = _check_and_extract_name($item, $opts{-verbose}, $root_rx)) { 225*0Sstevel@tonic-gate _check_for_duplicates($item, $name, \%names, \%pods); 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate }, $try); # end of File::Find::find 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate chdir $pwd; 230*0Sstevel@tonic-gate %pods; 231*0Sstevel@tonic-gate} 232*0Sstevel@tonic-gate 233*0Sstevel@tonic-gatesub _check_for_duplicates { 234*0Sstevel@tonic-gate my ($file, $name, $names_ref, $pods_ref) = @_; 235*0Sstevel@tonic-gate if($$names_ref{$name}) { 236*0Sstevel@tonic-gate warn "Duplicate POD found (shadowing?): $name ($file)\n"; 237*0Sstevel@tonic-gate warn " Already seen in ", 238*0Sstevel@tonic-gate join(' ', grep($$pods_ref{$_} eq $name, keys %$pods_ref)),"\n"; 239*0Sstevel@tonic-gate } 240*0Sstevel@tonic-gate else { 241*0Sstevel@tonic-gate $$names_ref{$name} = 1; 242*0Sstevel@tonic-gate } 243*0Sstevel@tonic-gate $$pods_ref{$file} = $name; 244*0Sstevel@tonic-gate} 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gatesub _check_and_extract_name { 247*0Sstevel@tonic-gate my ($file, $verbose, $root_rx) = @_; 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate # check extension or executable flag 250*0Sstevel@tonic-gate # this involves testing the .bat extension on Win32! 251*0Sstevel@tonic-gate unless(-f $file && -T _ && ($file =~ /\.(pod|pm|plx?)\z/i || -x _ )) { 252*0Sstevel@tonic-gate return undef; 253*0Sstevel@tonic-gate } 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate return undef unless contains_pod($file,$verbose); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate # strip non-significant path components 258*0Sstevel@tonic-gate # TODO what happens on e.g. Win32? 259*0Sstevel@tonic-gate my $name = $file; 260*0Sstevel@tonic-gate if(defined $root_rx) { 261*0Sstevel@tonic-gate $name =~ s!$root_rx!!s; 262*0Sstevel@tonic-gate $name =~ s!$SIMPLIFY_RX!!os if(defined $SIMPLIFY_RX); 263*0Sstevel@tonic-gate } 264*0Sstevel@tonic-gate else { 265*0Sstevel@tonic-gate if ($^O eq 'MacOS') { 266*0Sstevel@tonic-gate $name =~ s/^.*://s; 267*0Sstevel@tonic-gate } else { 268*0Sstevel@tonic-gate $name =~ s:^.*/::s; 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate _simplify($name); 272*0Sstevel@tonic-gate $name =~ s!/+!::!g; #/ 273*0Sstevel@tonic-gate if ($^O eq 'MacOS') { 274*0Sstevel@tonic-gate $name =~ s!:+!::!g; # : -> :: 275*0Sstevel@tonic-gate } else { 276*0Sstevel@tonic-gate $name =~ s!/+!::!g; # / -> :: 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate $name; 279*0Sstevel@tonic-gate} 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate=head2 C<simplify_name( $str )> 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gateThe function B<simplify_name> is equivalent to B<basename>, but also 284*0Sstevel@tonic-gatestrips Perl-like extensions (.pm, .pl, .pod) and extensions like 285*0Sstevel@tonic-gateF<.bat>, F<.cmd> on Win32 and OS/2, or F<.com> on VMS, respectively. 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate=cut 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate# basic simplification of the POD name: 290*0Sstevel@tonic-gate# basename & strip extension 291*0Sstevel@tonic-gatesub simplify_name { 292*0Sstevel@tonic-gate my ($str) = @_; 293*0Sstevel@tonic-gate # remove all path components 294*0Sstevel@tonic-gate if ($^O eq 'MacOS') { 295*0Sstevel@tonic-gate $str =~ s/^.*://s; 296*0Sstevel@tonic-gate } else { 297*0Sstevel@tonic-gate $str =~ s:^.*/::s; 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate _simplify($str); 300*0Sstevel@tonic-gate $str; 301*0Sstevel@tonic-gate} 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate# internal sub only 304*0Sstevel@tonic-gatesub _simplify { 305*0Sstevel@tonic-gate # strip Perl's own extensions 306*0Sstevel@tonic-gate $_[0] =~ s/\.(pod|pm|plx?)\z//i; 307*0Sstevel@tonic-gate # strip meaningless extensions on Win32 and OS/2 308*0Sstevel@tonic-gate $_[0] =~ s/\.(bat|exe|cmd)\z//i if($^O =~ /mswin|os2/i); 309*0Sstevel@tonic-gate # strip meaningless extensions on VMS 310*0Sstevel@tonic-gate $_[0] =~ s/\.(com)\z//i if($^O eq 'VMS'); 311*0Sstevel@tonic-gate} 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate# contribution from Tim Jenness <t.jenness@jach.hawaii.edu> 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate=head2 C<pod_where( { %opts }, $pod )> 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gateReturns the location of a pod document given a search directory 318*0Sstevel@tonic-gateand a module (e.g. C<File::Find>) or script (e.g. C<perldoc>) name. 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gateOptions: 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate=over 4 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate=item C<-inc =E<gt> 1> 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gateSearch @INC for the pod and also the C<scriptdir> defined in the 327*0Sstevel@tonic-gateL<Config|Config> module. 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate=item C<-dirs =E<gt> [ $dir1, $dir2, ... ]> 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gateReference to an array of search directories. These are searched in order 332*0Sstevel@tonic-gatebefore looking in C<@INC> (if B<-inc>). Current directory is used if 333*0Sstevel@tonic-gatenone are specified. 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate=item C<-verbose =E<gt> 1> 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gateList directories as they are searched 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate=back 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gateReturns the full path of the first occurrence to the file. 342*0Sstevel@tonic-gatePackage names (eg 'A::B') are automatically converted to directory 343*0Sstevel@tonic-gatenames in the selected directory. (eg on unix 'A::B' is converted to 344*0Sstevel@tonic-gate'A/B'). Additionally, '.pm', '.pl' and '.pod' are appended to the 345*0Sstevel@tonic-gatesearch automatically if required. 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gateA subdirectory F<pod/> is also checked if it exists in any of the given 348*0Sstevel@tonic-gatesearch directories. This ensures that e.g. L<perlfunc|perlfunc> is 349*0Sstevel@tonic-gatefound. 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gateIt is assumed that if a module name is supplied, that that name 352*0Sstevel@tonic-gatematches the file name. Pods are not opened to check for the 'NAME' 353*0Sstevel@tonic-gateentry. 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gateA check is made to make sure that the file that is found does 356*0Sstevel@tonic-gatecontain some pod documentation. 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate=cut 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gatesub pod_where { 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate # default options 363*0Sstevel@tonic-gate my %options = ( 364*0Sstevel@tonic-gate '-inc' => 0, 365*0Sstevel@tonic-gate '-verbose' => 0, 366*0Sstevel@tonic-gate '-dirs' => [ File::Spec->curdir ], 367*0Sstevel@tonic-gate ); 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate # Check for an options hash as first argument 370*0Sstevel@tonic-gate if (defined $_[0] && ref($_[0]) eq 'HASH') { 371*0Sstevel@tonic-gate my $opt = shift; 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate # Merge default options with supplied options 374*0Sstevel@tonic-gate %options = (%options, %$opt); 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate # Check usage 378*0Sstevel@tonic-gate carp 'Usage: pod_where({options}, $pod)' unless (scalar(@_)); 379*0Sstevel@tonic-gate 380*0Sstevel@tonic-gate # Read argument 381*0Sstevel@tonic-gate my $pod = shift; 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate # Split on :: and then join the name together using File::Spec 384*0Sstevel@tonic-gate my @parts = split (/::/, $pod); 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate # Get full directory list 387*0Sstevel@tonic-gate my @search_dirs = @{ $options{'-dirs'} }; 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate if ($options{'-inc'}) { 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate require Config; 392*0Sstevel@tonic-gate 393*0Sstevel@tonic-gate # Add @INC 394*0Sstevel@tonic-gate if ($^O eq 'MacOS' && $options{'-inc'}) { 395*0Sstevel@tonic-gate # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS 396*0Sstevel@tonic-gate my @new_INC = @INC; 397*0Sstevel@tonic-gate for (@new_INC) { 398*0Sstevel@tonic-gate if ( $_ eq '.' ) { 399*0Sstevel@tonic-gate $_ = ':'; 400*0Sstevel@tonic-gate } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) { 401*0Sstevel@tonic-gate $_ = ':'. $_; 402*0Sstevel@tonic-gate } else { 403*0Sstevel@tonic-gate $_ =~ s|^\./|:|; 404*0Sstevel@tonic-gate } 405*0Sstevel@tonic-gate } 406*0Sstevel@tonic-gate push (@search_dirs, @new_INC); 407*0Sstevel@tonic-gate } elsif ($options{'-inc'}) { 408*0Sstevel@tonic-gate push (@search_dirs, @INC); 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate # Add location of pod documentation for perl man pages (eg perlfunc) 412*0Sstevel@tonic-gate # This is a pod directory in the private install tree 413*0Sstevel@tonic-gate #my $perlpoddir = File::Spec->catdir($Config::Config{'installprivlib'}, 414*0Sstevel@tonic-gate # 'pod'); 415*0Sstevel@tonic-gate #push (@search_dirs, $perlpoddir) 416*0Sstevel@tonic-gate # if -d $perlpoddir; 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate # Add location of binaries such as pod2text 419*0Sstevel@tonic-gate push (@search_dirs, $Config::Config{'scriptdir'}) 420*0Sstevel@tonic-gate if -d $Config::Config{'scriptdir'}; 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate warn "Search path is: ".join(' ', @search_dirs)."\n" 424*0Sstevel@tonic-gate if $options{'-verbose'}; 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate # Loop over directories 427*0Sstevel@tonic-gate Dir: foreach my $dir ( @search_dirs ) { 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate # Don't bother if can't find the directory 430*0Sstevel@tonic-gate if (-d $dir) { 431*0Sstevel@tonic-gate warn "Looking in directory $dir\n" 432*0Sstevel@tonic-gate if $options{'-verbose'}; 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate # Now concatenate this directory with the pod we are searching for 435*0Sstevel@tonic-gate my $fullname = File::Spec->catfile($dir, @parts); 436*0Sstevel@tonic-gate warn "Filename is now $fullname\n" 437*0Sstevel@tonic-gate if $options{'-verbose'}; 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate # Loop over possible extensions 440*0Sstevel@tonic-gate foreach my $ext ('', '.pod', '.pm', '.pl') { 441*0Sstevel@tonic-gate my $fullext = $fullname . $ext; 442*0Sstevel@tonic-gate if (-f $fullext && 443*0Sstevel@tonic-gate contains_pod($fullext, $options{'-verbose'}) ) { 444*0Sstevel@tonic-gate warn "FOUND: $fullext\n" if $options{'-verbose'}; 445*0Sstevel@tonic-gate return $fullext; 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate } 448*0Sstevel@tonic-gate } else { 449*0Sstevel@tonic-gate warn "Directory $dir does not exist\n" 450*0Sstevel@tonic-gate if $options{'-verbose'}; 451*0Sstevel@tonic-gate next Dir; 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate # for some strange reason the path on MacOS/darwin/cygwin is 454*0Sstevel@tonic-gate # 'pods' not 'pod' 455*0Sstevel@tonic-gate # this could be the case also for other systems that 456*0Sstevel@tonic-gate # have a case-tolerant file system, but File::Spec 457*0Sstevel@tonic-gate # does not recognize 'darwin' yet. And cygwin also has "pods", 458*0Sstevel@tonic-gate # but is not case tolerant. Oh well... 459*0Sstevel@tonic-gate if((File::Spec->case_tolerant || $^O =~ /macos|darwin|cygwin/i) 460*0Sstevel@tonic-gate && -d File::Spec->catdir($dir,'pods')) { 461*0Sstevel@tonic-gate $dir = File::Spec->catdir($dir,'pods'); 462*0Sstevel@tonic-gate redo Dir; 463*0Sstevel@tonic-gate } 464*0Sstevel@tonic-gate if(-d File::Spec->catdir($dir,'pod')) { 465*0Sstevel@tonic-gate $dir = File::Spec->catdir($dir,'pod'); 466*0Sstevel@tonic-gate redo Dir; 467*0Sstevel@tonic-gate } 468*0Sstevel@tonic-gate } 469*0Sstevel@tonic-gate # No match; 470*0Sstevel@tonic-gate return undef; 471*0Sstevel@tonic-gate} 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate=head2 C<contains_pod( $file , $verbose )> 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gateReturns true if the supplied filename (not POD module) contains some pod 476*0Sstevel@tonic-gateinformation. 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate=cut 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gatesub contains_pod { 481*0Sstevel@tonic-gate my $file = shift; 482*0Sstevel@tonic-gate my $verbose = 0; 483*0Sstevel@tonic-gate $verbose = shift if @_; 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate # check for one line of POD 486*0Sstevel@tonic-gate unless(open(POD,"<$file")) { 487*0Sstevel@tonic-gate warn "Error: $file is unreadable: $!\n"; 488*0Sstevel@tonic-gate return undef; 489*0Sstevel@tonic-gate } 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate local $/ = undef; 492*0Sstevel@tonic-gate my $pod = <POD>; 493*0Sstevel@tonic-gate close(POD) || die "Error closing $file: $!\n"; 494*0Sstevel@tonic-gate unless($pod =~ /\n=(head\d|pod|over|item)\b/s) { 495*0Sstevel@tonic-gate warn "No POD in $file, skipping.\n" 496*0Sstevel@tonic-gate if($verbose); 497*0Sstevel@tonic-gate return 0; 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate return 1; 501*0Sstevel@tonic-gate} 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate=head1 AUTHOR 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gatePlease report bugs using L<http://rt.cpan.org>. 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gateMarek Rouchal E<lt>marekr@cpan.orgE<gt>, 508*0Sstevel@tonic-gateheavily borrowing code from Nick Ing-Simmons' PodToHtml. 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gateTim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt> provided 511*0Sstevel@tonic-gateC<pod_where> and C<contains_pod>. 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate=head1 SEE ALSO 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gateL<Pod::Parser>, L<Pod::Checker>, L<perldoc> 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate=cut 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate1; 520*0Sstevel@tonic-gate 521