Track /etc/gitconfig
[msysgit/mtrensch.git] / lib / perl5 / 5.8.8 / Pod / Find.pm
blob0b085b8c9e38a1e2fabe0b149525eafd2a7f6273
1 #############################################################################
2 # Pod/Find.pm -- finds files containing POD documentation
4 # Author: Marek Rouchal <marekr@cpan.org>
5 #
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
10 # as Perl itself.
11 #############################################################################
13 package Pod::Find;
15 use vars qw($VERSION);
16 $VERSION = 1.34; ## Current version of this package
17 require 5.005; ## requires this Perl version or later
18 use Carp;
20 #############################################################################
22 =head1 NAME
24 Pod::Find - find POD documents in directory trees
26 =head1 SYNOPSIS
28 use Pod::Find qw(pod_find simplify_name);
29 my %pods = pod_find({ -verbose => 1, -inc => 1 });
30 foreach(keys %pods) {
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" );
38 =head1 DESCRIPTION
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.
49 =cut
51 use strict;
52 #use diagnostics;
53 use Exporter;
54 use File::Spec;
55 use File::Find;
56 use Cwd;
58 use vars qw(@ISA @EXPORT_OK $VERSION);
59 @ISA = qw(Exporter);
60 @EXPORT_OK = qw(&pod_find &simplify_name &pod_where &contains_pod);
62 # package global variables
63 my $SIMPLIFY_RX;
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
76 translators.
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.
89 =over 4
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.
113 =back
115 =cut
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
120 sub pod_find
122 my %opts;
123 if(ref $_[0]) {
124 %opts = %{shift()};
127 $opts{-verbose} ||= 0;
128 $opts{-perl} ||= 0;
130 my (@search) = @_;
132 if($opts{-script}) {
133 require Config;
134 push(@search, $Config::Config{scriptdir})
135 if -d $Config::Config{scriptdir};
136 $opts{-perl} = 1;
139 if($opts{-inc}) {
140 if ($^O eq 'MacOS') {
141 # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
142 my @new_INC = @INC;
143 for (@new_INC) {
144 if ( $_ eq '.' ) {
145 $_ = ':';
146 } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) {
147 $_ = ':'. $_;
148 } else {
149 $_ =~ s|^\./|:|;
152 push(@search, grep($_ ne File::Spec->curdir, @new_INC));
153 } else {
154 push(@search, grep($_ ne File::Spec->curdir, @INC));
157 $opts{-perl} = 1;
160 if($opts{-perl}) {
161 require Config;
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)
168 # Mac OS:
169 # * remove ":?site_perl:"
170 # * remove :?pod: if followed by *.pod (e.g. in :pod:perlfunc.pod)
172 if ($^O eq 'MacOS') {
173 $SIMPLIFY_RX =
174 qq!^(?i:\:?site_perl\:|\:?pod\:(?=.*?\\.pod\\z))*!;
175 } else {
176 $SIMPLIFY_RX =
177 qq!^(?i:site(_perl)?/|\Q$Config::Config{archname}\E/|\\d+\\.\\d+([_.]?\\d+)?/|pod/(?=.*?\\.pod\\z))*!;
181 my %dirs_visited;
182 my %pods;
183 my %names;
184 my $pwd = cwd();
186 foreach my $try (@search) {
187 unless(File::Spec->file_name_is_absolute($try)) {
188 # make path absolute
189 $try = File::Spec->catfile($pwd,$try);
191 # simplify path
192 # on VMS canonpath will vmsify:[the.path], but File::Find::find
193 # wants /unixy/paths
194 $try = File::Spec->canonpath($try) if ($^O ne 'VMS');
195 $try = VMS::Filespec::unixify($try) if ($^O eq 'VMS');
196 my $name;
197 if(-f $try) {
198 if($name = _check_and_extract_name($try, $opts{-verbose})) {
199 _check_for_duplicates($try, $name, \%names, \%pods);
201 next;
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;
206 if(-d) {
207 if($item =~ m{/(?:RCS|CVS|SCCS|\.svn)$}) {
208 $File::Find::prune = 1;
209 return;
211 elsif($dirs_visited{$item}) {
212 warn "Directory '$item' already seen, skipping.\n"
213 if($opts{-verbose});
214 $File::Find::prune = 1;
215 return;
217 else {
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"
223 if($opts{-verbose});
225 return;
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
232 chdir $pwd;
233 %pods;
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";
243 else {
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 )) {
255 return undef;
258 return undef unless contains_pod($file,$verbose);
260 # strip non-significant path components
261 # TODO what happens on e.g. Win32?
262 my $name = $file;
263 if(defined $root_rx) {
264 $name =~ s!$root_rx!!s;
265 $name =~ s!$SIMPLIFY_RX!!os if(defined $SIMPLIFY_RX);
267 else {
268 if ($^O eq 'MacOS') {
269 $name =~ s/^.*://s;
270 } else {
271 $name =~ s:^.*/::s;
274 _simplify($name);
275 $name =~ s!/+!::!g; #/
276 if ($^O eq 'MacOS') {
277 $name =~ s!:+!::!g; # : -> ::
278 } else {
279 $name =~ s!/+!::!g; # / -> ::
281 $name;
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.
290 =cut
292 # basic simplification of the POD name:
293 # basename & strip extension
294 sub simplify_name {
295 my ($str) = @_;
296 # remove all path components
297 if ($^O eq 'MacOS') {
298 $str =~ s/^.*://s;
299 } else {
300 $str =~ s:^.*/::s;
302 _simplify($str);
303 $str;
306 # internal sub only
307 sub _simplify {
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.
323 Options:
325 =over 4
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
336 none are specified.
338 =item C<-verbose =E<gt> 1>
340 List directories as they are searched
342 =back
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
352 found.
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'
356 entry.
358 A check is made to make sure that the file that is found does
359 contain some pod documentation.
361 =cut
363 sub pod_where {
365 # default options
366 my %options = (
367 '-inc' => 0,
368 '-verbose' => 0,
369 '-dirs' => [ File::Spec->curdir ],
372 # Check for an options hash as first argument
373 if (defined $_[0] && ref($_[0]) eq 'HASH') {
374 my $opt = shift;
376 # Merge default options with supplied options
377 %options = (%options, %$opt);
380 # Check usage
381 carp 'Usage: pod_where({options}, $pod)' unless (scalar(@_));
383 # Read argument
384 my $pod = shift;
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'}) {
394 require Config;
396 # Add @INC
397 if ($^O eq 'MacOS' && $options{'-inc'}) {
398 # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
399 my @new_INC = @INC;
400 for (@new_INC) {
401 if ( $_ eq '.' ) {
402 $_ = ':';
403 } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) {
404 $_ = ':'. $_;
405 } else {
406 $_ =~ s|^\./|:|;
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'},
417 # 'pod');
418 #push (@search_dirs, $perlpoddir)
419 # if -d $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
433 if (-d $dir) {
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;
445 if (-f $fullext &&
446 contains_pod($fullext, $options{'-verbose'}) ) {
447 warn "FOUND: $fullext\n" if $options{'-verbose'};
448 return $fullext;
451 } else {
452 warn "Directory $dir does not exist\n"
453 if $options{'-verbose'};
454 next Dir;
456 # for some strange reason the path on MacOS/darwin/cygwin is
457 # 'pods' not 'pod'
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');
465 redo Dir;
467 if(-d File::Spec->catdir($dir,'pod')) {
468 $dir = File::Spec->catdir($dir,'pod');
469 redo Dir;
472 # No match;
473 return undef;
476 =head2 C<contains_pod( $file , $verbose )>
478 Returns true if the supplied filename (not POD module) contains some pod
479 information.
481 =cut
483 sub contains_pod {
484 my $file = shift;
485 my $verbose = 0;
486 $verbose = shift if @_;
488 # check for one line of POD
489 unless(open(POD,"<$file")) {
490 warn "Error: $file is unreadable: $!\n";
491 return undef;
494 local $/ = undef;
495 my $pod = <POD>;
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"
499 if($verbose);
500 return 0;
503 return 1;
506 =head1 AUTHOR
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>.
516 =head1 SEE ALSO
518 L<Pod::Parser>, L<Pod::Checker>, L<perldoc>
520 =cut