1 #############################################################################
2 # Pod/Find.pm -- finds files containing POD documentation
4 # Author: Marek Rouchal <marek@saftsack.fs.uni-bayreuth.de>
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 = 0.11; ## Current version of this package
17 require 5.004; ## requires this Perl version or later
19 #############################################################################
23 Pod::Find - find POD documents in directory trees
27 use Pod::Find qw(pod_find simplify_name);
28 my %pods = pod_find({ -verbose => 1, -inc => 1 });
30 print "found library POD `$pods{$_}' in $_\n";
33 print "podname=",simplify_name('a/b/c/mymodule.pod'),"\n";
37 B<Pod::Find> provides a function B<pod_find> that searches for POD
38 documents in a given set of files and directories. It returns a hash
39 with the file names as keys and the POD name as value. The POD name
40 is derived from the file name and its position in the directory tree.
42 E.g. when searching in F<$HOME/perl5lib>, the file
43 F<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>,
44 whereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be
45 I<Myclass::Subclass>. The name information can be used for POD
48 Only text files containing at least one valid POD command are found.
50 A warning is printed if more than one POD file with the same POD name
51 is found, e.g. F<CPAN.pm> in different directories. This usually
52 indicates duplicate occurences of modules in the I<@INC> search path.
54 The function B<simplify_name> is equivalent to B<basename>, but also
55 strips Perl-like extensions (.pm, .pl, .pod).
57 Note that neither B<pod_find> nor B<simplify_name> are exported by
58 default so be sure to specify them in the B<use> statement if you need them:
60 use Pod::Find qw(pod_find simplify_name);
64 The first argument for B<pod_find> may be a hash reference with options.
65 The rest are either directories that are searched recursively or files.
66 The POD names of files are the plain basenames with any Perl-like extension
67 (.pm, .pl, .pod) stripped.
73 Print progress information while scanning.
77 Apply Perl-specific heuristics to find the correct PODs. This includes
78 stripping Perl-like extensions, omitting subdirectories that are numeric
79 but do I<not> match the current Perl interpreter's version id, suppressing
80 F<site_perl> as a module hierarchy name etc.
84 Search for PODs in the current Perl interpreter's installation
85 B<scriptdir>. This is taken from the local L<Config|Config> module.
89 Search for PODs in the current Perl interpreter's I<@INC> paths.
95 Marek Rouchal E<lt>marek@saftsack.fs.uni-bayreuth.deE<gt>,
96 heavily borrowing code from Nick Ing-Simmons' PodToHtml.
100 L<Pod::Parser>, L<Pod::Checker>
110 use vars qw(@ISA @EXPORT_OK $VERSION);
112 @EXPORT_OK = qw(&pod_find &simplify_name);
114 # package global variables
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});
139 push(@search, grep($_ ne '.',@INC));
145 # this code simplifies the POD name for Perl modules:
146 # * remove "site_perl"
147 # * remove e.g. "i586-linux"
148 # * remove e.g. 5.00503
149 # * remove pod/ if followed by *.pod (e.g. in pod/perlfunc.pod)
151 qq!^(?i:site_perl/|\Q$Config::Config{archname}\E/|\\d+\\.\\d+([_.]?\\d+)?/|pod/(?=.*?\\.pod\$))*!;
160 foreach my $try (@search) {
161 unless($try =~ m:^/:) {
163 $try = join('/',$pwd,$try);
165 $try =~ s:/\.?(?=/|$)::; # simplify path
168 if($name = _check_and_extract_name($try, $opts{-verbose})) {
169 _check_for_duplicates($try, $name, \%names, \%pods);
173 my $root_rx = qq!^\Q$try\E/!;
174 File::Find::find( sub {
175 my $item = $File::Find::name;
177 if($dirs_visited{$item}) {
178 warn "Directory '$item' already seen, skipping.\n"
180 $File::Find::prune = 1;
184 $dirs_visited{$item} = 1;
186 if($opts{-perl} && /^(\d+\.[\d_]+)$/ && eval "$1" != $]) {
187 $File::Find::prune = 1;
188 warn "Perl $] version mismatch on $_, skipping.\n"
193 if($name = _check_and_extract_name($item, $opts{-verbose}, $root_rx)) {
194 _check_for_duplicates($item, $name, \%names, \%pods);
196 }, $try); # end of File::Find::find
202 sub _check_for_duplicates {
203 my ($file, $name, $names_ref, $pods_ref) = @_;
204 if($$names_ref{$name}) {
205 warn "Duplicate POD found (shadowing?): $name ($file)\n";
206 warn " Already seen in ",
207 join(' ', grep($$pods_ref{$_} eq $name, keys %$pods_ref)),"\n";
210 $$names_ref{$name} = 1;
212 $$pods_ref{$file} = $name;
215 sub _check_and_extract_name {
216 my ($file, $verbose, $root_rx) = @_;
218 # check extension or executable
219 unless($file =~ /\.(pod|pm|pl)$/i || (-f $file && -x _ && -T _)) {
223 # check for one line of POD
224 unless(open(POD,"<$file")) {
225 warn "Error: $file is unreadable: $!\n";
231 unless($pod =~ /\n=(head\d|pod|over|item)\b/) {
232 warn "No POD in $file, skipping.\n"
238 # strip non-significant path components
239 # _TODO_ what happens on e.g. Win32?
241 if(defined $root_rx) {
242 $name =~ s!$root_rx!!;
243 $name =~ s!$SIMPLIFY_RX!!o if(defined $SIMPLIFY_RX);
248 $name =~ s/\.(pod|pm|pl)$//i;
253 # basic simplification of the POD name:
254 # basename & strip extension
258 $str =~ s:\.p([lm]|od)$::i;