source: for-distributions/trunk/bin/windows/perl/lib/Pod/Find.pm@ 14489

Last change on this file since 14489 was 14489, checked in by oranfry, 17 years ago

upgrading to perl 5.8

File size: 14.7 KB
Line 
1#############################################################################
2# Pod/Find.pm -- finds files containing POD documentation
3#
4# Author: Marek Rouchal <[email protected]>
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#############################################################################
12
13package Pod::Find;
14
15use vars qw($VERSION);
16$VERSION = 1.34; ## Current version of this package
17require 5.005; ## requires this Perl version or later
18use Carp;
19
20#############################################################################
21
22=head1 NAME
23
24Pod::Find - find POD documents in directory trees
25
26=head1 SYNOPSIS
27
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";
32 }
33
34 print "podname=",simplify_name('a/b/c/mymodule.pod'),"\n";
35
36 $location = pod_where( { -inc => 1 }, "Pod::Find" );
37
38=head1 DESCRIPTION
39
40B<Pod::Find> provides a set of functions to locate POD files. Note that
41no function is exported by default to avoid pollution of your namespace,
42so be sure to specify them in the B<use> statement if you need them:
43
44 use Pod::Find qw(pod_find);
45
46From this version on the typical SCM (software configuration management)
47files/directories like RCS, CVS, SCCS, .svn are ignored.
48
49=cut
50
51use strict;
52#use diagnostics;
53use Exporter;
54use File::Spec;
55use File::Find;
56use Cwd;
57
58use vars qw(@ISA @EXPORT_OK $VERSION);
59@ISA = qw(Exporter);
60@EXPORT_OK = qw(&pod_find &simplify_name &pod_where &contains_pod);
61
62# package global variables
63my $SIMPLIFY_RX;
64
65=head2 C<pod_find( { %opts } , @directories )>
66
67The function B<pod_find> searches for POD documents in a given set of
68files and/or directories. It returns a hash with the file names as keys
69and the POD name as value. The POD name is derived from the file name
70and its position in the directory tree.
71
72E.g. when searching in F<$HOME/perl5lib>, the file
73F<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>,
74whereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be
75I<Myclass::Subclass>. The name information can be used for POD
76translators.
77
78Only text files containing at least one valid POD command are found.
79
80A warning is printed if more than one POD file with the same POD name
81is found, e.g. F<CPAN.pm> in different directories. This usually
82indicates duplicate occurrences of modules in the I<@INC> search path.
83
84B<OPTIONS> The first argument for B<pod_find> may be a hash reference
85with options. The rest are either directories that are searched
86recursively or files. The POD names of files are the plain basenames
87with any Perl-like extension (.pm, .pl, .pod) stripped.
88
89=over 4
90
91=item C<-verbose =E<gt> 1>
92
93Print progress information while scanning.
94
95=item C<-perl =E<gt> 1>
96
97Apply Perl-specific heuristics to find the correct PODs. This includes
98stripping Perl-like extensions, omitting subdirectories that are numeric
99but do I<not> match the current Perl interpreter's version id, suppressing
100F<site_perl> as a module hierarchy name etc.
101
102=item C<-script =E<gt> 1>
103
104Search for PODs in the current Perl interpreter's installation
105B<scriptdir>. This is taken from the local L<Config|Config> module.
106
107=item C<-inc =E<gt> 1>
108
109Search for PODs in the current Perl interpreter's I<@INC> paths. This
110automatically considers paths specified in the C<PERL5LIB> environment
111as this is prepended to I<@INC> by the Perl interpreter itself.
112
113=back
114
115=cut
116
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
120sub pod_find
121{
122 my %opts;
123 if(ref $_[0]) {
124 %opts = %{shift()};
125 }
126
127 $opts{-verbose} ||= 0;
128 $opts{-perl} ||= 0;
129
130 my (@search) = @_;
131
132 if($opts{-script}) {
133 require Config;
134 push(@search, $Config::Config{scriptdir})
135 if -d $Config::Config{scriptdir};
136 $opts{-perl} = 1;
137 }
138
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|^\./|:|;
150 }
151 }
152 push(@search, grep($_ ne File::Spec->curdir, @new_INC));
153 } else {
154 push(@search, grep($_ ne File::Spec->curdir, @INC));
155 }
156
157 $opts{-perl} = 1;
158 }
159
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)
167
168 # Mac OS:
169 # * remove ":?site_perl:"
170 # * remove :?pod: if followed by *.pod (e.g. in :pod:perlfunc.pod)
171
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))*!;
178 }
179 }
180
181 my %dirs_visited;
182 my %pods;
183 my %names;
184 my $pwd = cwd();
185
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);
190 }
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);
200 }
201 next;
202 }
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;
210 }
211 elsif($dirs_visited{$item}) {
212 warn "Directory '$item' already seen, skipping.\n"
213 if($opts{-verbose});
214 $File::Find::prune = 1;
215 return;
216 }
217 else {
218 $dirs_visited{$item} = 1;
219 }
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});
224 }
225 return;
226 }
227 if($name = _check_and_extract_name($item, $opts{-verbose}, $root_rx)) {
228 _check_for_duplicates($item, $name, \%names, \%pods);
229 }
230 }, $try); # end of File::Find::find
231 }
232 chdir $pwd;
233 %pods;
234}
235
236sub _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";
242 }
243 else {
244 $$names_ref{$name} = 1;
245 }
246 $$pods_ref{$file} = $name;
247}
248
249sub _check_and_extract_name {
250 my ($file, $verbose, $root_rx) = @_;
251
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;
256 }
257
258 return undef unless contains_pod($file,$verbose);
259
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);
266 }
267 else {
268 if ($^O eq 'MacOS') {
269 $name =~ s/^.*://s;
270 } else {
271 $name =~ s:^.*/::s;
272 }
273 }
274 _simplify($name);
275 $name =~ s!/+!::!g; #/
276 if ($^O eq 'MacOS') {
277 $name =~ s!:+!::!g; # : -> ::
278 } else {
279 $name =~ s!/+!::!g; # / -> ::
280 }
281 $name;
282}
283
284=head2 C<simplify_name( $str )>
285
286The function B<simplify_name> is equivalent to B<basename>, but also
287strips Perl-like extensions (.pm, .pl, .pod) and extensions like
288F<.bat>, F<.cmd> on Win32 and OS/2, or F<.com> on VMS, respectively.
289
290=cut
291
292# basic simplification of the POD name:
293# basename & strip extension
294sub 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;
301 }
302 _simplify($str);
303 $str;
304}
305
306# internal sub only
307sub _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');
314}
315
316# contribution from Tim Jenness <[email protected]>
317
318=head2 C<pod_where( { %opts }, $pod )>
319
320Returns the location of a pod document given a search directory
321and a module (e.g. C<File::Find>) or script (e.g. C<perldoc>) name.
322
323Options:
324
325=over 4
326
327=item C<-inc =E<gt> 1>
328
329Search @INC for the pod and also the C<scriptdir> defined in the
330L<Config|Config> module.
331
332=item C<-dirs =E<gt> [ $dir1, $dir2, ... ]>
333
334Reference to an array of search directories. These are searched in order
335before looking in C<@INC> (if B<-inc>). Current directory is used if
336none are specified.
337
338=item C<-verbose =E<gt> 1>
339
340List directories as they are searched
341
342=back
343
344Returns the full path of the first occurrence to the file.
345Package names (eg 'A::B') are automatically converted to directory
346names in the selected directory. (eg on unix 'A::B' is converted to
347'A/B'). Additionally, '.pm', '.pl' and '.pod' are appended to the
348search automatically if required.
349
350A subdirectory F<pod/> is also checked if it exists in any of the given
351search directories. This ensures that e.g. L<perlfunc|perlfunc> is
352found.
353
354It is assumed that if a module name is supplied, that that name
355matches the file name. Pods are not opened to check for the 'NAME'
356entry.
357
358A check is made to make sure that the file that is found does
359contain some pod documentation.
360
361=cut
362
363sub pod_where {
364
365 # default options
366 my %options = (
367 '-inc' => 0,
368 '-verbose' => 0,
369 '-dirs' => [ File::Spec->curdir ],
370 );
371
372 # Check for an options hash as first argument
373 if (defined $_[0] && ref($_[0]) eq 'HASH') {
374 my $opt = shift;
375
376 # Merge default options with supplied options
377 %options = (%options, %$opt);
378 }
379
380 # Check usage
381 carp 'Usage: pod_where({options}, $pod)' unless (scalar(@_));
382
383 # Read argument
384 my $pod = shift;
385
386 # Split on :: and then join the name together using File::Spec
387 my @parts = split (/::/, $pod);
388
389 # Get full directory list
390 my @search_dirs = @{ $options{'-dirs'} };
391
392 if ($options{'-inc'}) {
393
394 require Config;
395
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|^\./|:|;
407 }
408 }
409 push (@search_dirs, @new_INC);
410 } elsif ($options{'-inc'}) {
411 push (@search_dirs, @INC);
412 }
413
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;
420
421 # Add location of binaries such as pod2text
422 push (@search_dirs, $Config::Config{'scriptdir'})
423 if -d $Config::Config{'scriptdir'};
424 }
425
426 warn "Search path is: ".join(' ', @search_dirs)."\n"
427 if $options{'-verbose'};
428
429 # Loop over directories
430 Dir: foreach my $dir ( @search_dirs ) {
431
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'};
436
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'};
441
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;
449 }
450 }
451 } else {
452 warn "Directory $dir does not exist\n"
453 if $options{'-verbose'};
454 next Dir;
455 }
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;
466 }
467 if(-d File::Spec->catdir($dir,'pod')) {
468 $dir = File::Spec->catdir($dir,'pod');
469 redo Dir;
470 }
471 }
472 # No match;
473 return undef;
474}
475
476=head2 C<contains_pod( $file , $verbose )>
477
478Returns true if the supplied filename (not POD module) contains some pod
479information.
480
481=cut
482
483sub contains_pod {
484 my $file = shift;
485 my $verbose = 0;
486 $verbose = shift if @_;
487
488 # check for one line of POD
489 unless(open(POD,"<$file")) {
490 warn "Error: $file is unreadable: $!\n";
491 return undef;
492 }
493
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;
501 }
502
503 return 1;
504}
505
506=head1 AUTHOR
507
508Please report bugs using L<http://rt.cpan.org>.
509
510Marek Rouchal E<lt>[email protected]<gt>,
511heavily borrowing code from Nick Ing-Simmons' PodToHtml.
512
513Tim Jenness E<lt>[email protected]<gt> provided
514C<pod_where> and C<contains_pod>.
515
516=head1 SEE ALSO
517
518L<Pod::Parser>, L<Pod::Checker>, L<perldoc>
519
520=cut
521
5221;
523
Note: See TracBrowser for help on using the repository browser.