1 #############################################################################
2 # Pod/Find.pm -- finds files containing POD documentation
4 # Author: Marek Rouchal <marekr@cpan.org>
6 # Copyright (C) 1999-2000 by Marek Rouchal (and borrowing code
7 # from Nick Ing-Simmon's PodToHtml). All rights reserved.
8 # This file is part of "PodParser". Pod::Find is free software;
9 # you can redistribute it and/or modify it under the same terms
11 #############################################################################
15 use vars
qw($VERSION);
16 $VERSION = 1.34; ## Current version of this package
17 require 5.005; ## requires this Perl version or later
20 #############################################################################
24 Pod::Find - find POD documents in directory trees
28 use Pod::Find qw(pod_find simplify_name);
29 my %pods = pod_find
({ -verbose
=> 1, -inc
=> 1 });
31 print "found library POD `$pods{$_}' in $_\n";
34 print "podname=",simplify_name
('a/b/c/mymodule.pod'),"\n";
36 $location = pod_where
( { -inc
=> 1 }, "Pod::Find" );
40 B<Pod::Find> provides a set of functions to locate POD files. Note that
41 no function is exported by default to avoid pollution of your namespace,
42 so be sure to specify them in the B<use> statement if you need them:
44 use Pod::Find qw(pod_find);
46 From this version on the typical SCM (software configuration management)
47 files/directories like RCS, CVS, SCCS, .svn are ignored.
58 use vars
qw(@ISA @EXPORT_OK $VERSION);
60 @EXPORT_OK = qw(&pod_find &simplify_name &pod_where &contains_pod);
62 # package global variables
65 =head2 C<pod_find( { %opts } , @directories )>
67 The function B<pod_find> searches for POD documents in a given set of
68 files and/or directories. It returns a hash with the file names as keys
69 and the POD name as value. The POD name is derived from the file name
70 and its position in the directory tree.
72 E.g. when searching in F<$HOME/perl5lib>, the file
73 F<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>,
74 whereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be
75 I<Myclass::Subclass>. The name information can be used for POD
78 Only text files containing at least one valid POD command are found.
80 A warning is printed if more than one POD file with the same POD name
81 is found, e.g. F<CPAN.pm> in different directories. This usually
82 indicates duplicate occurrences of modules in the I<@INC> search path.
84 B<OPTIONS> The first argument for B<pod_find> may be a hash reference
85 with options. The rest are either directories that are searched
86 recursively or files. The POD names of files are the plain basenames
87 with any Perl-like extension (.pm, .pl, .pod) stripped.
91 =item C<-verbose =E<gt> 1>
93 Print progress information while scanning.
95 =item C<-perl =E<gt> 1>
97 Apply Perl-specific heuristics to find the correct PODs. This includes
98 stripping Perl-like extensions, omitting subdirectories that are numeric
99 but do I<not> match the current Perl interpreter's version id, suppressing
100 F<site_perl> as a module hierarchy name etc.
102 =item C<-script =E<gt> 1>
104 Search for PODs in the current Perl interpreter's installation
105 B<scriptdir>. This is taken from the local L<Config|Config> module.
107 =item C<-inc =E<gt> 1>
109 Search for PODs in the current Perl interpreter's I<@INC> paths. This
110 automatically considers paths specified in the C<PERL5LIB> environment
111 as this is prepended to I<@INC> by the Perl interpreter itself.
117 # return a hash of the POD files found
118 # first argument may be a hashref (options),
119 # rest is a list of directories to search recursively
127 $opts{-verbose
} ||= 0;
134 push(@search, $Config::Config
{scriptdir
})
135 if -d
$Config::Config
{scriptdir
};
140 if ($^O
eq 'MacOS') {
141 # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
146 } elsif ( $_ =~ s
|^((?
:\
.\
./)+)|':' x (length($1)/3)|e
) {
152 push(@search, grep($_ ne File
::Spec
->curdir, @new_INC));
154 push(@search, grep($_ ne File
::Spec
->curdir, @INC));
162 # this code simplifies the POD name for Perl modules:
163 # * remove "site_perl"
164 # * remove e.g. "i586-linux" (from 'archname')
165 # * remove e.g. 5.00503
166 # * remove pod/ if followed by *.pod (e.g. in pod/perlfunc.pod)
169 # * remove ":?site_perl:"
170 # * remove :?pod: if followed by *.pod (e.g. in :pod:perlfunc.pod)
172 if ($^O
eq 'MacOS') {
174 qq!^(?i
:\
:?site_perl\
:|\
:?pod\
:(?
=.*?
\\.pod
\\z
))*!;
177 qq!^(?i
:site
(_perl
)?
/|\Q$Config::Config{archname}\E/|\\d
+\\.\\d
+([_
.]?
\\d
+)?
/|pod/(?
=.*?
\\.pod
\\z
))*!;
186 foreach my $try (@search) {
187 unless(File
::Spec
->file_name_is_absolute($try)) {
189 $try = File
::Spec
->catfile($pwd,$try);
192 # on VMS canonpath will vmsify:[the.path], but File::Find::find
194 $try = File
::Spec
->canonpath($try) if ($^O
ne 'VMS');
195 $try = VMS
::Filespec
::unixify
($try) if ($^O
eq 'VMS');
198 if($name = _check_and_extract_name
($try, $opts{-verbose
})) {
199 _check_for_duplicates
($try, $name, \
%names, \
%pods);
203 my $root_rx = $^O
eq 'MacOS' ?
qq!^\Q
$try\E
! : qq!^\Q
$try\E
/!;
204 File
::Find
::find
( sub {
205 my $item = $File::Find
::name
;
207 if($item =~ m{/(?:RCS|CVS|SCCS|\.svn)$}) {
208 $File::Find
::prune
= 1;
211 elsif($dirs_visited{$item}) {
212 warn "Directory '$item' already seen, skipping.\n"
214 $File::Find
::prune
= 1;
218 $dirs_visited{$item} = 1;
220 if($opts{-perl
} && /^(\d+\.[\d_]+)\z/s && eval "$1" != $]) {
221 $File::Find
::prune
= 1;
222 warn "Perl $] version mismatch on $_, skipping.\n"
227 if($name = _check_and_extract_name
($item, $opts{-verbose
}, $root_rx)) {
228 _check_for_duplicates
($item, $name, \
%names, \
%pods);
230 }, $try); # end of File::Find::find
236 sub _check_for_duplicates
{
237 my ($file, $name, $names_ref, $pods_ref) = @_;
238 if($$names_ref{$name}) {
239 warn "Duplicate POD found (shadowing?): $name ($file)\n";
240 warn " Already seen in ",
241 join(' ', grep($$pods_ref{$_} eq $name, keys %$pods_ref)),"\n";
244 $$names_ref{$name} = 1;
246 $$pods_ref{$file} = $name;
249 sub _check_and_extract_name
{
250 my ($file, $verbose, $root_rx) = @_;
252 # check extension or executable flag
253 # this involves testing the .bat extension on Win32!
254 unless(-f
$file && -T
$file && ($file =~ /\.(pod|pm|plx?)\z/i || -x
$file )) {
258 return undef unless contains_pod
($file,$verbose);
260 # strip non-significant path components
261 # TODO what happens on e.g. Win32?
263 if(defined $root_rx) {
264 $name =~ s!$root_rx!!s;
265 $name =~ s!$SIMPLIFY_RX!!os if(defined $SIMPLIFY_RX);
268 if ($^O
eq 'MacOS') {
275 $name =~ s!/+!::!g; #/
276 if ($^O
eq 'MacOS') {
277 $name =~ s!:+!::!g; # : -> ::
279 $name =~ s!/+!::!g; # / -> ::
284 =head2 C<simplify_name( $str )>
286 The function B<simplify_name> is equivalent to B<basename>, but also
287 strips Perl-like extensions (.pm, .pl, .pod) and extensions like
288 F<.bat>, F<.cmd> on Win32 and OS/2, or F<.com> on VMS, respectively.
292 # basic simplification of the POD name:
293 # basename & strip extension
296 # remove all path components
297 if ($^O
eq 'MacOS') {
308 # strip Perl's own extensions
309 $_[0] =~ s/\.(pod|pm|plx?)\z//i;
310 # strip meaningless extensions on Win32 and OS/2
311 $_[0] =~ s/\.(bat|exe|cmd)\z//i if($^O
=~ /mswin|os2/i);
312 # strip meaningless extensions on VMS
313 $_[0] =~ s/\.(com)\z//i if($^O
eq 'VMS');
316 # contribution from Tim Jenness <t.jenness@jach.hawaii.edu>
318 =head2 C<pod_where( { %opts }, $pod )>
320 Returns the location of a pod document given a search directory
321 and a module (e.g. C<File::Find>) or script (e.g. C<perldoc>) name.
327 =item C<-inc =E<gt> 1>
329 Search @INC for the pod and also the C<scriptdir> defined in the
330 L<Config|Config> module.
332 =item C<-dirs =E<gt> [ $dir1, $dir2, ... ]>
334 Reference to an array of search directories. These are searched in order
335 before looking in C<@INC> (if B<-inc>). Current directory is used if
338 =item C<-verbose =E<gt> 1>
340 List directories as they are searched
344 Returns the full path of the first occurrence to the file.
345 Package names (eg 'A::B') are automatically converted to directory
346 names in the selected directory. (eg on unix 'A::B' is converted to
347 'A/B'). Additionally, '.pm', '.pl' and '.pod' are appended to the
348 search automatically if required.
350 A subdirectory F<pod/> is also checked if it exists in any of the given
351 search directories. This ensures that e.g. L<perlfunc|perlfunc> is
354 It is assumed that if a module name is supplied, that that name
355 matches the file name. Pods are not opened to check for the 'NAME'
358 A check is made to make sure that the file that is found does
359 contain some pod documentation.
369 '-dirs' => [ File
::Spec
->curdir ],
372 # Check for an options hash as first argument
373 if (defined $_[0] && ref($_[0]) eq 'HASH') {
376 # Merge default options with supplied options
377 %options = (%options, %$opt);
381 carp
'Usage: pod_where({options}, $pod)' unless (scalar(@_));
386 # Split on :: and then join the name together using File::Spec
387 my @parts = split (/::/, $pod);
389 # Get full directory list
390 my @search_dirs = @
{ $options{'-dirs'} };
392 if ($options{'-inc'}) {
397 if ($^O
eq 'MacOS' && $options{'-inc'}) {
398 # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
403 } elsif ( $_ =~ s
|^((?
:\
.\
./)+)|':' x (length($1)/3)|e
) {
409 push (@search_dirs, @new_INC);
410 } elsif ($options{'-inc'}) {
411 push (@search_dirs, @INC);
414 # Add location of pod documentation for perl man pages (eg perlfunc)
415 # This is a pod directory in the private install tree
416 #my $perlpoddir = File::Spec->catdir($Config::Config{'installprivlib'},
418 #push (@search_dirs, $perlpoddir)
421 # Add location of binaries such as pod2text
422 push (@search_dirs, $Config::Config
{'scriptdir'})
423 if -d
$Config::Config
{'scriptdir'};
426 warn "Search path is: ".join(' ', @search_dirs)."\n"
427 if $options{'-verbose'};
429 # Loop over directories
430 Dir
: foreach my $dir ( @search_dirs ) {
432 # Don't bother if can't find the directory
434 warn "Looking in directory $dir\n"
435 if $options{'-verbose'};
437 # Now concatenate this directory with the pod we are searching for
438 my $fullname = File
::Spec
->catfile($dir, @parts);
439 warn "Filename is now $fullname\n"
440 if $options{'-verbose'};
442 # Loop over possible extensions
443 foreach my $ext ('', '.pod', '.pm', '.pl') {
444 my $fullext = $fullname . $ext;
446 contains_pod
($fullext, $options{'-verbose'}) ) {
447 warn "FOUND: $fullext\n" if $options{'-verbose'};
452 warn "Directory $dir does not exist\n"
453 if $options{'-verbose'};
456 # for some strange reason the path on MacOS/darwin/cygwin is
458 # this could be the case also for other systems that
459 # have a case-tolerant file system, but File::Spec
460 # does not recognize 'darwin' yet. And cygwin also has "pods",
461 # but is not case tolerant. Oh well...
462 if((File
::Spec
->case_tolerant || $^O
=~ /macos|darwin|cygwin/i)
463 && -d File
::Spec
->catdir($dir,'pods')) {
464 $dir = File
::Spec
->catdir($dir,'pods');
467 if(-d File
::Spec
->catdir($dir,'pod')) {
468 $dir = File
::Spec
->catdir($dir,'pod');
476 =head2 C<contains_pod( $file , $verbose )>
478 Returns true if the supplied filename (not POD module) contains some pod
486 $verbose = shift if @_;
488 # check for one line of POD
489 unless(open(POD
,"<$file")) {
490 warn "Error: $file is unreadable: $!\n";
496 close(POD
) || die "Error closing $file: $!\n";
497 unless($pod =~ /^=(head\d|pod|over|item)\b/m) {
498 warn "No POD in $file, skipping.\n"
508 Please report bugs using L<http://rt.cpan.org>.
510 Marek Rouchal E<lt>marekr@cpan.orgE<gt>,
511 heavily borrowing code from Nick Ing-Simmons' PodToHtml.
513 Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt> provided
514 C<pod_where> and C<contains_pod>.
518 L<Pod::Parser>, L<Pod::Checker>, L<perldoc>