avoid $@-clearing sideeffect of require in Carp
[p5sagit/p5-mst-13.2.git] / lib / Pod / Find.pm
CommitLineData
e2c3adef 1#############################################################################
2# Pod/Find.pm -- finds files containing POD documentation
3#
4# Author: Marek Rouchal <marek@saftsack.fs.uni-bayreuth.de>
5#
6# borrowing code from Nick Ing-Simmon's PodToHtml
7# This file is part of "PodParser". Pod::Find is free software;
8# you can redistribute it and/or modify it under the same terms
9# as Perl itself.
10#############################################################################
11
12package Pod::Find;
13
14use vars qw($VERSION);
15$VERSION = 0.10; ## Current version of this package
16require 5.005; ## requires this Perl version or later
17
18#############################################################################
19
20=head1 NAME
21
22Pod::Find - find POD documents in directory trees
23
24=head1 SYNOPSIS
25
26 use Pod::Find qw(pod_find simplify_name);
27 my %pods = pod_find({ -verbose => 1, -inc => 1 });
28 foreach(keys %pods) {
29 print "found library POD `$pods{$_}' in $_\n";
30 }
31
32 print "podname=",simplify_name('a/b/c/mymodule.pod'),"\n";
33
34=head1 DESCRIPTION
35
36B<Pod::Find> provides a function B<pod_find> that searches for POD
37documents in a given set of files and directories. It returns a hash
38with the file names as keys and the POD name as value. The POD name
39is derived from the file name and its position in the directory tree.
40
41E.g. when searching in F<$HOME/perl5lib>, the file
42F<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>,
43whereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be
44I<Myclass::Subclass>. The name information can be used for POD
45translators.
46
47Only text files containing at least one valid POD command are found.
48
49A warning is printed if more than one POD file with the same POD name
50is found, e.g. F<CPAN.pm> in different directories. This usually
51indicates duplicate occurences of modules in the I<@INC> search path.
52
53The function B<simplify_name> is equivalent to B<basename>, but also
54strips Perl-like extensions (.pm, .pl, .pod).
55
56Note that neither B<pod_find> nor B<simplify_name> are exported by
57default so be sure to specify them in the B<use> statement if you need them:
58
59 use Pod::Find qw(pod_find simplify_name);
60
61=head1 OPTIONS
62
63The first argument for B<pod_find> may be a hash reference with options.
64The rest are either directories that are searched recursively or files.
65The POD names of files are the plain basenames with any Perl-like extension
66(.pm, .pl, .pod) stripped.
67
68=over 4
69
70=item B<-verbose>
71
72Print progress information while scanning.
73
74=item B<-perl>
75
76Apply Perl-specific heuristics to find the correct PODs. This includes
77stripping Perl-like extensions, omitting subdirectories that are numeric
78but do I<not> match the current Perl interpreter's version id, suppressing
79F<site_perl> as a module hierarchy name etc.
80
81=item B<-script>
82
83Search for PODs in the current Perl interpreter's installation
84B<scriptdir>. This is taken from the local L<Config|Config> module.
85
86=item B<-inc>
87
88Search for PODs in the current Perl interpreter's I<@INC> paths.
89
90=back
91
92=head1 AUTHOR
93
94Marek Rouchal E<lt>marek@saftsack.fs.uni-bayreuth.deE<gt>,
95heavily borrowing code from Nick Ing-Simmons' PodToHtml.
96
97=head1 SEE ALSO
98
99L<Pod::Parser>, L<Pod::Checker>
100
101=cut
102
103use strict;
104#use diagnostics;
105use Exporter;
106use File::Find;
107use Cwd;
108
109use vars qw(@ISA @EXPORT_OK $VERSION);
110@ISA = qw(Exporter);
111@EXPORT_OK = qw(&pod_find &simplify_name);
112
113# package global variables
114my $SIMPLIFY_RX;
115
116# return a hash of the
117sub pod_find
118{
119 my %opts;
120 if(ref $_[0]) {
121 %opts = %{shift()};
122 }
123
124 $opts{-verbose} ||= 0;
125 $opts{-perl} ||= 0;
126
127 my (@search) = @_;
128
129 if($opts{-script}) {
130 require Config;
131 push(@search, $Config::Config{scriptdir});
132 $opts{-perl} = 1;
133 }
134
135 if($opts{-inc}) {
136 push(@search, grep($_ ne '.',@INC));
137 $opts{-perl} = 1;
138 }
139
140 if($opts{-perl}) {
141 require Config;
142 # this code simplifies the POD name for Perl modules:
143 # * remove "site_perl"
144 # * remove e.g. "i586-linux"
145 # * remove e.g. 5.00503
146 # * remove pod/ if followed by *.pod (e.g. in pod/perlfunc.pod)
147 $SIMPLIFY_RX =
148 qr!^(?i:site_perl/|$Config::Config{archname}/|\d+\.\d+([_.]?\d+)?/|pod/(?=.*?\.pod$))*!o;
149 }
150
151 my %dirs_visited;
152 my %pods;
153 my %names;
154 my $pwd = cwd();
155
156 foreach my $try (@search) {
157 unless($try =~ m:^/:) {
158 # make path absolute
159 $try = join('/',$pwd,$try);
160 }
161 $try =~ s:/\.?(?=/|$)::; # simplify path
162 my $name;
163 if(-f $try) {
164 if($name = _check_and_extract_name($try, $opts{-verbose})) {
165 _check_for_duplicates($try, $name, \%names, \%pods);
166 }
167 next;
168 }
169 my $root_rx = qr!^\Q$try\E/!;
170 File::Find::find( sub {
171 my $item = $File::Find::name;
172 if(-d) {
173 if($dirs_visited{$item}) {
174 warn "Directory '$item' already seen, skipping.\n"
175 if($opts{-verbose});
176 $File::Find::prune = 1;
177 return;
178 }
179 else {
180 $dirs_visited{$item} = 1;
181 }
182 if($opts{-perl} && /^(\d+\.[\d_]+)$/ && eval "$1" != $]) {
183 $File::Find::prune = 1;
184 warn "Perl $] version mismatch on $_, skipping.\n"
185 if($opts{-verbose});
186 }
187 return;
188 }
189 if($name = _check_and_extract_name($item, $opts{-verbose}, $root_rx)) {
190 _check_for_duplicates($item, $name, \%names, \%pods);
191 }
192 }, $try); # end of File::Find::find
193 }
194 chdir $pwd;
195 %pods;
196}
197
198sub _check_for_duplicates {
199 my ($file, $name, $names_ref, $pods_ref) = @_;
200 if($$names_ref{$name}) {
201 warn "Duplicate POD found (shadowing?): $name ($file)\n";
202 warn " Already seen in ",
203 join(' ', grep($$pods_ref{$_} eq $name, keys %$pods_ref)),"\n";
204 }
205 else {
206 $$names_ref{$name} = 1;
207 }
208 $$pods_ref{$file} = $name;
209}
210
211sub _check_and_extract_name {
212 my ($file, $verbose, $root_rx) = @_;
213
214 # check extension or executable
215 unless($file =~ /\.(pod|pm|pl)$/i || (-f $file && -x _ && -T _)) {
216 return undef;
217 }
218
219 # check for one line of POD
220 unless(open(POD,"<$file")) {
221 warn "Error: $file is unreadable: $!\n";
222 return undef;
223 }
224 local $/ = undef;
225 my $pod = <POD>;
226 close(POD);
227 unless($pod =~ /\n=(head\d|pod|over|item)\b/) {
228 warn "No POD in $file, skipping.\n"
229 if($verbose);
230 return;
231 }
232 undef $pod;
233
234 # strip non-significant path components
235 # _TODO_ what happens on e.g. Win32?
236 my $name = $file;
237 if(defined $root_rx) {
238 $name =~ s!$root_rx!!;
239 $name =~ s!$SIMPLIFY_RX!!o if(defined $SIMPLIFY_RX);
240 }
241 else {
242 $name =~ s:^.*/::;
243 }
244 $name =~ s/\.(pod|pm|pl)$//i;
245 $name =~ s!/+!::!g;
246 $name;
247}
248
249# basic simplification of the POD name:
250# basename & strip extension
251sub simplify_name {
252 my ($str) = @_;
253 $str =~ s:^.*/::;
254 $str =~ s:\.p([lm]|od)$::i;
255 $str;
256}
257
2581;
259