xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Pod/Find.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gate#############################################################################
2*0Sstevel@tonic-gate# Pod/Find.pm -- finds files containing POD documentation
3*0Sstevel@tonic-gate#
4*0Sstevel@tonic-gate# Author: Marek Rouchal <marekr@cpan.org>
5*0Sstevel@tonic-gate#
6*0Sstevel@tonic-gate# Copyright (C) 1999-2000 by Marek Rouchal (and borrowing code
7*0Sstevel@tonic-gate# from Nick Ing-Simmon's PodToHtml). All rights reserved.
8*0Sstevel@tonic-gate# This file is part of "PodParser". Pod::Find is free software;
9*0Sstevel@tonic-gate# you can redistribute it and/or modify it under the same terms
10*0Sstevel@tonic-gate# as Perl itself.
11*0Sstevel@tonic-gate#############################################################################
12*0Sstevel@tonic-gate
13*0Sstevel@tonic-gatepackage Pod::Find;
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gateuse vars qw($VERSION);
16*0Sstevel@tonic-gate$VERSION = 0.24_01;   ## Current version of this package
17*0Sstevel@tonic-gaterequire  5.005;   ## requires this Perl version or later
18*0Sstevel@tonic-gateuse Carp;
19*0Sstevel@tonic-gate
20*0Sstevel@tonic-gate#############################################################################
21*0Sstevel@tonic-gate
22*0Sstevel@tonic-gate=head1 NAME
23*0Sstevel@tonic-gate
24*0Sstevel@tonic-gatePod::Find - find POD documents in directory trees
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gate=head1 SYNOPSIS
27*0Sstevel@tonic-gate
28*0Sstevel@tonic-gate  use Pod::Find qw(pod_find simplify_name);
29*0Sstevel@tonic-gate  my %pods = pod_find({ -verbose => 1, -inc => 1 });
30*0Sstevel@tonic-gate  foreach(keys %pods) {
31*0Sstevel@tonic-gate     print "found library POD `$pods{$_}' in $_\n";
32*0Sstevel@tonic-gate  }
33*0Sstevel@tonic-gate
34*0Sstevel@tonic-gate  print "podname=",simplify_name('a/b/c/mymodule.pod'),"\n";
35*0Sstevel@tonic-gate
36*0Sstevel@tonic-gate  $location = pod_where( { -inc => 1 }, "Pod::Find" );
37*0Sstevel@tonic-gate
38*0Sstevel@tonic-gate=head1 DESCRIPTION
39*0Sstevel@tonic-gate
40*0Sstevel@tonic-gateB<Pod::Find> provides a set of functions to locate POD files.  Note that
41*0Sstevel@tonic-gateno function is exported by default to avoid pollution of your namespace,
42*0Sstevel@tonic-gateso be sure to specify them in the B<use> statement if you need them:
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate  use Pod::Find qw(pod_find);
45*0Sstevel@tonic-gate
46*0Sstevel@tonic-gate=cut
47*0Sstevel@tonic-gate
48*0Sstevel@tonic-gateuse strict;
49*0Sstevel@tonic-gate#use diagnostics;
50*0Sstevel@tonic-gateuse Exporter;
51*0Sstevel@tonic-gateuse File::Spec;
52*0Sstevel@tonic-gateuse File::Find;
53*0Sstevel@tonic-gateuse Cwd;
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gateuse vars qw(@ISA @EXPORT_OK $VERSION);
56*0Sstevel@tonic-gate@ISA = qw(Exporter);
57*0Sstevel@tonic-gate@EXPORT_OK = qw(&pod_find &simplify_name &pod_where &contains_pod);
58*0Sstevel@tonic-gate
59*0Sstevel@tonic-gate# package global variables
60*0Sstevel@tonic-gatemy $SIMPLIFY_RX;
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gate=head2 C<pod_find( { %opts } , @directories )>
63*0Sstevel@tonic-gate
64*0Sstevel@tonic-gateThe function B<pod_find> searches for POD documents in a given set of
65*0Sstevel@tonic-gatefiles and/or directories. It returns a hash with the file names as keys
66*0Sstevel@tonic-gateand the POD name as value. The POD name is derived from the file name
67*0Sstevel@tonic-gateand its position in the directory tree.
68*0Sstevel@tonic-gate
69*0Sstevel@tonic-gateE.g. when searching in F<$HOME/perl5lib>, the file
70*0Sstevel@tonic-gateF<$HOME/perl5lib/MyModule.pm> would get the POD name I<MyModule>,
71*0Sstevel@tonic-gatewhereas F<$HOME/perl5lib/Myclass/Subclass.pm> would be
72*0Sstevel@tonic-gateI<Myclass::Subclass>. The name information can be used for POD
73*0Sstevel@tonic-gatetranslators.
74*0Sstevel@tonic-gate
75*0Sstevel@tonic-gateOnly text files containing at least one valid POD command are found.
76*0Sstevel@tonic-gate
77*0Sstevel@tonic-gateA warning is printed if more than one POD file with the same POD name
78*0Sstevel@tonic-gateis found, e.g. F<CPAN.pm> in different directories. This usually
79*0Sstevel@tonic-gateindicates duplicate occurrences of modules in the I<@INC> search path.
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gateB<OPTIONS> The first argument for B<pod_find> may be a hash reference
82*0Sstevel@tonic-gatewith options. The rest are either directories that are searched
83*0Sstevel@tonic-gaterecursively or files.  The POD names of files are the plain basenames
84*0Sstevel@tonic-gatewith any Perl-like extension (.pm, .pl, .pod) stripped.
85*0Sstevel@tonic-gate
86*0Sstevel@tonic-gate=over 4
87*0Sstevel@tonic-gate
88*0Sstevel@tonic-gate=item C<-verbose =E<gt> 1>
89*0Sstevel@tonic-gate
90*0Sstevel@tonic-gatePrint progress information while scanning.
91*0Sstevel@tonic-gate
92*0Sstevel@tonic-gate=item C<-perl =E<gt> 1>
93*0Sstevel@tonic-gate
94*0Sstevel@tonic-gateApply Perl-specific heuristics to find the correct PODs. This includes
95*0Sstevel@tonic-gatestripping Perl-like extensions, omitting subdirectories that are numeric
96*0Sstevel@tonic-gatebut do I<not> match the current Perl interpreter's version id, suppressing
97*0Sstevel@tonic-gateF<site_perl> as a module hierarchy name etc.
98*0Sstevel@tonic-gate
99*0Sstevel@tonic-gate=item C<-script =E<gt> 1>
100*0Sstevel@tonic-gate
101*0Sstevel@tonic-gateSearch for PODs in the current Perl interpreter's installation
102*0Sstevel@tonic-gateB<scriptdir>. This is taken from the local L<Config|Config> module.
103*0Sstevel@tonic-gate
104*0Sstevel@tonic-gate=item C<-inc =E<gt> 1>
105*0Sstevel@tonic-gate
106*0Sstevel@tonic-gateSearch for PODs in the current Perl interpreter's I<@INC> paths. This
107*0Sstevel@tonic-gateautomatically considers paths specified in the C<PERL5LIB> environment
108*0Sstevel@tonic-gateas this is prepended to I<@INC> by the Perl interpreter itself.
109*0Sstevel@tonic-gate
110*0Sstevel@tonic-gate=back
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate=cut
113*0Sstevel@tonic-gate
114*0Sstevel@tonic-gate# return a hash of the POD files found
115*0Sstevel@tonic-gate# first argument may be a hashref (options),
116*0Sstevel@tonic-gate# rest is a list of directories to search recursively
117*0Sstevel@tonic-gatesub pod_find
118*0Sstevel@tonic-gate{
119*0Sstevel@tonic-gate    my %opts;
120*0Sstevel@tonic-gate    if(ref $_[0]) {
121*0Sstevel@tonic-gate        %opts = %{shift()};
122*0Sstevel@tonic-gate    }
123*0Sstevel@tonic-gate
124*0Sstevel@tonic-gate    $opts{-verbose} ||= 0;
125*0Sstevel@tonic-gate    $opts{-perl}    ||= 0;
126*0Sstevel@tonic-gate
127*0Sstevel@tonic-gate    my (@search) = @_;
128*0Sstevel@tonic-gate
129*0Sstevel@tonic-gate    if($opts{-script}) {
130*0Sstevel@tonic-gate        require Config;
131*0Sstevel@tonic-gate        push(@search, $Config::Config{scriptdir})
132*0Sstevel@tonic-gate            if -d $Config::Config{scriptdir};
133*0Sstevel@tonic-gate        $opts{-perl} = 1;
134*0Sstevel@tonic-gate    }
135*0Sstevel@tonic-gate
136*0Sstevel@tonic-gate    if($opts{-inc}) {
137*0Sstevel@tonic-gate        if ($^O eq 'MacOS') {
138*0Sstevel@tonic-gate            # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
139*0Sstevel@tonic-gate            my @new_INC = @INC;
140*0Sstevel@tonic-gate            for (@new_INC) {
141*0Sstevel@tonic-gate                if ( $_ eq '.' ) {
142*0Sstevel@tonic-gate                    $_ = ':';
143*0Sstevel@tonic-gate                } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) {
144*0Sstevel@tonic-gate                    $_ = ':'. $_;
145*0Sstevel@tonic-gate                } else {
146*0Sstevel@tonic-gate                    $_ =~ s|^\./|:|;
147*0Sstevel@tonic-gate                }
148*0Sstevel@tonic-gate            }
149*0Sstevel@tonic-gate            push(@search, grep($_ ne File::Spec->curdir, @new_INC));
150*0Sstevel@tonic-gate        } else {
151*0Sstevel@tonic-gate            push(@search, grep($_ ne File::Spec->curdir, @INC));
152*0Sstevel@tonic-gate        }
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gate        $opts{-perl} = 1;
155*0Sstevel@tonic-gate    }
156*0Sstevel@tonic-gate
157*0Sstevel@tonic-gate    if($opts{-perl}) {
158*0Sstevel@tonic-gate        require Config;
159*0Sstevel@tonic-gate        # this code simplifies the POD name for Perl modules:
160*0Sstevel@tonic-gate        # * remove "site_perl"
161*0Sstevel@tonic-gate        # * remove e.g. "i586-linux" (from 'archname')
162*0Sstevel@tonic-gate        # * remove e.g. 5.00503
163*0Sstevel@tonic-gate        # * remove pod/ if followed by *.pod (e.g. in pod/perlfunc.pod)
164*0Sstevel@tonic-gate
165*0Sstevel@tonic-gate        # Mac OS:
166*0Sstevel@tonic-gate        # * remove ":?site_perl:"
167*0Sstevel@tonic-gate        # * remove :?pod: if followed by *.pod (e.g. in :pod:perlfunc.pod)
168*0Sstevel@tonic-gate
169*0Sstevel@tonic-gate        if ($^O eq 'MacOS') {
170*0Sstevel@tonic-gate            $SIMPLIFY_RX =
171*0Sstevel@tonic-gate              qq!^(?i:\:?site_perl\:|\:?pod\:(?=.*?\\.pod\\z))*!;
172*0Sstevel@tonic-gate        } else {
173*0Sstevel@tonic-gate            $SIMPLIFY_RX =
174*0Sstevel@tonic-gate              qq!^(?i:site(_perl)?/|\Q$Config::Config{archname}\E/|\\d+\\.\\d+([_.]?\\d+)?/|pod/(?=.*?\\.pod\\z))*!;
175*0Sstevel@tonic-gate        }
176*0Sstevel@tonic-gate    }
177*0Sstevel@tonic-gate
178*0Sstevel@tonic-gate    my %dirs_visited;
179*0Sstevel@tonic-gate    my %pods;
180*0Sstevel@tonic-gate    my %names;
181*0Sstevel@tonic-gate    my $pwd = cwd();
182*0Sstevel@tonic-gate
183*0Sstevel@tonic-gate    foreach my $try (@search) {
184*0Sstevel@tonic-gate        unless(File::Spec->file_name_is_absolute($try)) {
185*0Sstevel@tonic-gate            # make path absolute
186*0Sstevel@tonic-gate            $try = File::Spec->catfile($pwd,$try);
187*0Sstevel@tonic-gate        }
188*0Sstevel@tonic-gate        # simplify path
189*0Sstevel@tonic-gate        # on VMS canonpath will vmsify:[the.path], but File::Find::find
190*0Sstevel@tonic-gate        # wants /unixy/paths
191*0Sstevel@tonic-gate        $try = File::Spec->canonpath($try) if ($^O ne 'VMS');
192*0Sstevel@tonic-gate        $try = VMS::Filespec::unixify($try) if ($^O eq 'VMS');
193*0Sstevel@tonic-gate        my $name;
194*0Sstevel@tonic-gate        if(-f $try) {
195*0Sstevel@tonic-gate            if($name = _check_and_extract_name($try, $opts{-verbose})) {
196*0Sstevel@tonic-gate                _check_for_duplicates($try, $name, \%names, \%pods);
197*0Sstevel@tonic-gate            }
198*0Sstevel@tonic-gate            next;
199*0Sstevel@tonic-gate        }
200*0Sstevel@tonic-gate        my $root_rx = $^O eq 'MacOS' ? qq!^\Q$try\E! : qq!^\Q$try\E/!;
201*0Sstevel@tonic-gate        File::Find::find( sub {
202*0Sstevel@tonic-gate            my $item = $File::Find::name;
203*0Sstevel@tonic-gate            if(-d) {
204*0Sstevel@tonic-gate                if($item =~ m{/(?:RCS|CVS|SCCS|\.svn)$}) {
205*0Sstevel@tonic-gate                    $File::Find::prune = 1;
206*0Sstevel@tonic-gate                    return;
207*0Sstevel@tonic-gate                }
208*0Sstevel@tonic-gate                elsif($dirs_visited{$item}) {
209*0Sstevel@tonic-gate                    warn "Directory '$item' already seen, skipping.\n"
210*0Sstevel@tonic-gate                        if($opts{-verbose});
211*0Sstevel@tonic-gate                    $File::Find::prune = 1;
212*0Sstevel@tonic-gate                    return;
213*0Sstevel@tonic-gate                }
214*0Sstevel@tonic-gate                else {
215*0Sstevel@tonic-gate                    $dirs_visited{$item} = 1;
216*0Sstevel@tonic-gate                }
217*0Sstevel@tonic-gate                if($opts{-perl} && /^(\d+\.[\d_]+)\z/s && eval "$1" != $]) {
218*0Sstevel@tonic-gate                    $File::Find::prune = 1;
219*0Sstevel@tonic-gate                    warn "Perl $] version mismatch on $_, skipping.\n"
220*0Sstevel@tonic-gate                        if($opts{-verbose});
221*0Sstevel@tonic-gate                }
222*0Sstevel@tonic-gate                return;
223*0Sstevel@tonic-gate            }
224*0Sstevel@tonic-gate            if($name = _check_and_extract_name($item, $opts{-verbose}, $root_rx)) {
225*0Sstevel@tonic-gate                _check_for_duplicates($item, $name, \%names, \%pods);
226*0Sstevel@tonic-gate            }
227*0Sstevel@tonic-gate        }, $try); # end of File::Find::find
228*0Sstevel@tonic-gate    }
229*0Sstevel@tonic-gate    chdir $pwd;
230*0Sstevel@tonic-gate    %pods;
231*0Sstevel@tonic-gate}
232*0Sstevel@tonic-gate
233*0Sstevel@tonic-gatesub _check_for_duplicates {
234*0Sstevel@tonic-gate    my ($file, $name, $names_ref, $pods_ref) = @_;
235*0Sstevel@tonic-gate    if($$names_ref{$name}) {
236*0Sstevel@tonic-gate        warn "Duplicate POD found (shadowing?): $name ($file)\n";
237*0Sstevel@tonic-gate        warn "    Already seen in ",
238*0Sstevel@tonic-gate            join(' ', grep($$pods_ref{$_} eq $name, keys %$pods_ref)),"\n";
239*0Sstevel@tonic-gate    }
240*0Sstevel@tonic-gate    else {
241*0Sstevel@tonic-gate        $$names_ref{$name} = 1;
242*0Sstevel@tonic-gate    }
243*0Sstevel@tonic-gate    $$pods_ref{$file} = $name;
244*0Sstevel@tonic-gate}
245*0Sstevel@tonic-gate
246*0Sstevel@tonic-gatesub _check_and_extract_name {
247*0Sstevel@tonic-gate    my ($file, $verbose, $root_rx) = @_;
248*0Sstevel@tonic-gate
249*0Sstevel@tonic-gate    # check extension or executable flag
250*0Sstevel@tonic-gate    # this involves testing the .bat extension on Win32!
251*0Sstevel@tonic-gate    unless(-f $file && -T _ && ($file =~ /\.(pod|pm|plx?)\z/i || -x _ )) {
252*0Sstevel@tonic-gate      return undef;
253*0Sstevel@tonic-gate    }
254*0Sstevel@tonic-gate
255*0Sstevel@tonic-gate    return undef unless contains_pod($file,$verbose);
256*0Sstevel@tonic-gate
257*0Sstevel@tonic-gate    # strip non-significant path components
258*0Sstevel@tonic-gate    # TODO what happens on e.g. Win32?
259*0Sstevel@tonic-gate    my $name = $file;
260*0Sstevel@tonic-gate    if(defined $root_rx) {
261*0Sstevel@tonic-gate        $name =~ s!$root_rx!!s;
262*0Sstevel@tonic-gate        $name =~ s!$SIMPLIFY_RX!!os if(defined $SIMPLIFY_RX);
263*0Sstevel@tonic-gate    }
264*0Sstevel@tonic-gate    else {
265*0Sstevel@tonic-gate        if ($^O eq 'MacOS') {
266*0Sstevel@tonic-gate            $name =~ s/^.*://s;
267*0Sstevel@tonic-gate        } else {
268*0Sstevel@tonic-gate            $name =~ s:^.*/::s;
269*0Sstevel@tonic-gate        }
270*0Sstevel@tonic-gate    }
271*0Sstevel@tonic-gate    _simplify($name);
272*0Sstevel@tonic-gate    $name =~ s!/+!::!g; #/
273*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
274*0Sstevel@tonic-gate        $name =~ s!:+!::!g; # : -> ::
275*0Sstevel@tonic-gate    } else {
276*0Sstevel@tonic-gate        $name =~ s!/+!::!g; # / -> ::
277*0Sstevel@tonic-gate    }
278*0Sstevel@tonic-gate    $name;
279*0Sstevel@tonic-gate}
280*0Sstevel@tonic-gate
281*0Sstevel@tonic-gate=head2 C<simplify_name( $str )>
282*0Sstevel@tonic-gate
283*0Sstevel@tonic-gateThe function B<simplify_name> is equivalent to B<basename>, but also
284*0Sstevel@tonic-gatestrips Perl-like extensions (.pm, .pl, .pod) and extensions like
285*0Sstevel@tonic-gateF<.bat>, F<.cmd> on Win32 and OS/2, or F<.com> on VMS, respectively.
286*0Sstevel@tonic-gate
287*0Sstevel@tonic-gate=cut
288*0Sstevel@tonic-gate
289*0Sstevel@tonic-gate# basic simplification of the POD name:
290*0Sstevel@tonic-gate# basename & strip extension
291*0Sstevel@tonic-gatesub simplify_name {
292*0Sstevel@tonic-gate    my ($str) = @_;
293*0Sstevel@tonic-gate    # remove all path components
294*0Sstevel@tonic-gate    if ($^O eq 'MacOS') {
295*0Sstevel@tonic-gate        $str =~ s/^.*://s;
296*0Sstevel@tonic-gate    } else {
297*0Sstevel@tonic-gate        $str =~ s:^.*/::s;
298*0Sstevel@tonic-gate    }
299*0Sstevel@tonic-gate    _simplify($str);
300*0Sstevel@tonic-gate    $str;
301*0Sstevel@tonic-gate}
302*0Sstevel@tonic-gate
303*0Sstevel@tonic-gate# internal sub only
304*0Sstevel@tonic-gatesub _simplify {
305*0Sstevel@tonic-gate    # strip Perl's own extensions
306*0Sstevel@tonic-gate    $_[0] =~ s/\.(pod|pm|plx?)\z//i;
307*0Sstevel@tonic-gate    # strip meaningless extensions on Win32 and OS/2
308*0Sstevel@tonic-gate    $_[0] =~ s/\.(bat|exe|cmd)\z//i if($^O =~ /mswin|os2/i);
309*0Sstevel@tonic-gate    # strip meaningless extensions on VMS
310*0Sstevel@tonic-gate    $_[0] =~ s/\.(com)\z//i if($^O eq 'VMS');
311*0Sstevel@tonic-gate}
312*0Sstevel@tonic-gate
313*0Sstevel@tonic-gate# contribution from Tim Jenness <t.jenness@jach.hawaii.edu>
314*0Sstevel@tonic-gate
315*0Sstevel@tonic-gate=head2 C<pod_where( { %opts }, $pod )>
316*0Sstevel@tonic-gate
317*0Sstevel@tonic-gateReturns the location of a pod document given a search directory
318*0Sstevel@tonic-gateand a module (e.g. C<File::Find>) or script (e.g. C<perldoc>) name.
319*0Sstevel@tonic-gate
320*0Sstevel@tonic-gateOptions:
321*0Sstevel@tonic-gate
322*0Sstevel@tonic-gate=over 4
323*0Sstevel@tonic-gate
324*0Sstevel@tonic-gate=item C<-inc =E<gt> 1>
325*0Sstevel@tonic-gate
326*0Sstevel@tonic-gateSearch @INC for the pod and also the C<scriptdir> defined in the
327*0Sstevel@tonic-gateL<Config|Config> module.
328*0Sstevel@tonic-gate
329*0Sstevel@tonic-gate=item C<-dirs =E<gt> [ $dir1, $dir2, ... ]>
330*0Sstevel@tonic-gate
331*0Sstevel@tonic-gateReference to an array of search directories. These are searched in order
332*0Sstevel@tonic-gatebefore looking in C<@INC> (if B<-inc>). Current directory is used if
333*0Sstevel@tonic-gatenone are specified.
334*0Sstevel@tonic-gate
335*0Sstevel@tonic-gate=item C<-verbose =E<gt> 1>
336*0Sstevel@tonic-gate
337*0Sstevel@tonic-gateList directories as they are searched
338*0Sstevel@tonic-gate
339*0Sstevel@tonic-gate=back
340*0Sstevel@tonic-gate
341*0Sstevel@tonic-gateReturns the full path of the first occurrence to the file.
342*0Sstevel@tonic-gatePackage names (eg 'A::B') are automatically converted to directory
343*0Sstevel@tonic-gatenames in the selected directory. (eg on unix 'A::B' is converted to
344*0Sstevel@tonic-gate'A/B'). Additionally, '.pm', '.pl' and '.pod' are appended to the
345*0Sstevel@tonic-gatesearch automatically if required.
346*0Sstevel@tonic-gate
347*0Sstevel@tonic-gateA subdirectory F<pod/> is also checked if it exists in any of the given
348*0Sstevel@tonic-gatesearch directories. This ensures that e.g. L<perlfunc|perlfunc> is
349*0Sstevel@tonic-gatefound.
350*0Sstevel@tonic-gate
351*0Sstevel@tonic-gateIt is assumed that if a module name is supplied, that that name
352*0Sstevel@tonic-gatematches the file name. Pods are not opened to check for the 'NAME'
353*0Sstevel@tonic-gateentry.
354*0Sstevel@tonic-gate
355*0Sstevel@tonic-gateA check is made to make sure that the file that is found does
356*0Sstevel@tonic-gatecontain some pod documentation.
357*0Sstevel@tonic-gate
358*0Sstevel@tonic-gate=cut
359*0Sstevel@tonic-gate
360*0Sstevel@tonic-gatesub pod_where {
361*0Sstevel@tonic-gate
362*0Sstevel@tonic-gate  # default options
363*0Sstevel@tonic-gate  my %options = (
364*0Sstevel@tonic-gate         '-inc' => 0,
365*0Sstevel@tonic-gate         '-verbose' => 0,
366*0Sstevel@tonic-gate         '-dirs' => [ File::Spec->curdir ],
367*0Sstevel@tonic-gate        );
368*0Sstevel@tonic-gate
369*0Sstevel@tonic-gate  # Check for an options hash as first argument
370*0Sstevel@tonic-gate  if (defined $_[0] && ref($_[0]) eq 'HASH') {
371*0Sstevel@tonic-gate    my $opt = shift;
372*0Sstevel@tonic-gate
373*0Sstevel@tonic-gate    # Merge default options with supplied options
374*0Sstevel@tonic-gate    %options = (%options, %$opt);
375*0Sstevel@tonic-gate  }
376*0Sstevel@tonic-gate
377*0Sstevel@tonic-gate  # Check usage
378*0Sstevel@tonic-gate  carp 'Usage: pod_where({options}, $pod)' unless (scalar(@_));
379*0Sstevel@tonic-gate
380*0Sstevel@tonic-gate  # Read argument
381*0Sstevel@tonic-gate  my $pod = shift;
382*0Sstevel@tonic-gate
383*0Sstevel@tonic-gate  # Split on :: and then join the name together using File::Spec
384*0Sstevel@tonic-gate  my @parts = split (/::/, $pod);
385*0Sstevel@tonic-gate
386*0Sstevel@tonic-gate  # Get full directory list
387*0Sstevel@tonic-gate  my @search_dirs = @{ $options{'-dirs'} };
388*0Sstevel@tonic-gate
389*0Sstevel@tonic-gate  if ($options{'-inc'}) {
390*0Sstevel@tonic-gate
391*0Sstevel@tonic-gate    require Config;
392*0Sstevel@tonic-gate
393*0Sstevel@tonic-gate    # Add @INC
394*0Sstevel@tonic-gate    if ($^O eq 'MacOS' && $options{'-inc'}) {
395*0Sstevel@tonic-gate        # tolerate '.', './some_dir' and '(../)+some_dir' on Mac OS
396*0Sstevel@tonic-gate        my @new_INC = @INC;
397*0Sstevel@tonic-gate        for (@new_INC) {
398*0Sstevel@tonic-gate            if ( $_ eq '.' ) {
399*0Sstevel@tonic-gate                $_ = ':';
400*0Sstevel@tonic-gate            } elsif ( $_ =~ s|^((?:\.\./)+)|':' x (length($1)/3)|e ) {
401*0Sstevel@tonic-gate                $_ = ':'. $_;
402*0Sstevel@tonic-gate            } else {
403*0Sstevel@tonic-gate                $_ =~ s|^\./|:|;
404*0Sstevel@tonic-gate            }
405*0Sstevel@tonic-gate        }
406*0Sstevel@tonic-gate        push (@search_dirs, @new_INC);
407*0Sstevel@tonic-gate    } elsif ($options{'-inc'}) {
408*0Sstevel@tonic-gate        push (@search_dirs, @INC);
409*0Sstevel@tonic-gate    }
410*0Sstevel@tonic-gate
411*0Sstevel@tonic-gate    # Add location of pod documentation for perl man pages (eg perlfunc)
412*0Sstevel@tonic-gate    # This is a pod directory in the private install tree
413*0Sstevel@tonic-gate    #my $perlpoddir = File::Spec->catdir($Config::Config{'installprivlib'},
414*0Sstevel@tonic-gate    #					'pod');
415*0Sstevel@tonic-gate    #push (@search_dirs, $perlpoddir)
416*0Sstevel@tonic-gate    #  if -d $perlpoddir;
417*0Sstevel@tonic-gate
418*0Sstevel@tonic-gate    # Add location of binaries such as pod2text
419*0Sstevel@tonic-gate    push (@search_dirs, $Config::Config{'scriptdir'})
420*0Sstevel@tonic-gate      if -d $Config::Config{'scriptdir'};
421*0Sstevel@tonic-gate  }
422*0Sstevel@tonic-gate
423*0Sstevel@tonic-gate  warn "Search path is: ".join(' ', @search_dirs)."\n"
424*0Sstevel@tonic-gate        if $options{'-verbose'};
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gate  # Loop over directories
427*0Sstevel@tonic-gate  Dir: foreach my $dir ( @search_dirs ) {
428*0Sstevel@tonic-gate
429*0Sstevel@tonic-gate    # Don't bother if can't find the directory
430*0Sstevel@tonic-gate    if (-d $dir) {
431*0Sstevel@tonic-gate      warn "Looking in directory $dir\n"
432*0Sstevel@tonic-gate        if $options{'-verbose'};
433*0Sstevel@tonic-gate
434*0Sstevel@tonic-gate      # Now concatenate this directory with the pod we are searching for
435*0Sstevel@tonic-gate      my $fullname = File::Spec->catfile($dir, @parts);
436*0Sstevel@tonic-gate      warn "Filename is now $fullname\n"
437*0Sstevel@tonic-gate        if $options{'-verbose'};
438*0Sstevel@tonic-gate
439*0Sstevel@tonic-gate      # Loop over possible extensions
440*0Sstevel@tonic-gate      foreach my $ext ('', '.pod', '.pm', '.pl') {
441*0Sstevel@tonic-gate        my $fullext = $fullname . $ext;
442*0Sstevel@tonic-gate        if (-f $fullext &&
443*0Sstevel@tonic-gate         contains_pod($fullext, $options{'-verbose'}) ) {
444*0Sstevel@tonic-gate          warn "FOUND: $fullext\n" if $options{'-verbose'};
445*0Sstevel@tonic-gate          return $fullext;
446*0Sstevel@tonic-gate        }
447*0Sstevel@tonic-gate      }
448*0Sstevel@tonic-gate    } else {
449*0Sstevel@tonic-gate      warn "Directory $dir does not exist\n"
450*0Sstevel@tonic-gate        if $options{'-verbose'};
451*0Sstevel@tonic-gate      next Dir;
452*0Sstevel@tonic-gate    }
453*0Sstevel@tonic-gate    # for some strange reason the path on MacOS/darwin/cygwin is
454*0Sstevel@tonic-gate    # 'pods' not 'pod'
455*0Sstevel@tonic-gate    # this could be the case also for other systems that
456*0Sstevel@tonic-gate    # have a case-tolerant file system, but File::Spec
457*0Sstevel@tonic-gate    # does not recognize 'darwin' yet. And cygwin also has "pods",
458*0Sstevel@tonic-gate    # but is not case tolerant. Oh well...
459*0Sstevel@tonic-gate    if((File::Spec->case_tolerant || $^O =~ /macos|darwin|cygwin/i)
460*0Sstevel@tonic-gate     && -d File::Spec->catdir($dir,'pods')) {
461*0Sstevel@tonic-gate      $dir = File::Spec->catdir($dir,'pods');
462*0Sstevel@tonic-gate      redo Dir;
463*0Sstevel@tonic-gate    }
464*0Sstevel@tonic-gate    if(-d File::Spec->catdir($dir,'pod')) {
465*0Sstevel@tonic-gate      $dir = File::Spec->catdir($dir,'pod');
466*0Sstevel@tonic-gate      redo Dir;
467*0Sstevel@tonic-gate    }
468*0Sstevel@tonic-gate  }
469*0Sstevel@tonic-gate  # No match;
470*0Sstevel@tonic-gate  return undef;
471*0Sstevel@tonic-gate}
472*0Sstevel@tonic-gate
473*0Sstevel@tonic-gate=head2 C<contains_pod( $file , $verbose )>
474*0Sstevel@tonic-gate
475*0Sstevel@tonic-gateReturns true if the supplied filename (not POD module) contains some pod
476*0Sstevel@tonic-gateinformation.
477*0Sstevel@tonic-gate
478*0Sstevel@tonic-gate=cut
479*0Sstevel@tonic-gate
480*0Sstevel@tonic-gatesub contains_pod {
481*0Sstevel@tonic-gate  my $file = shift;
482*0Sstevel@tonic-gate  my $verbose = 0;
483*0Sstevel@tonic-gate  $verbose = shift if @_;
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate  # check for one line of POD
486*0Sstevel@tonic-gate  unless(open(POD,"<$file")) {
487*0Sstevel@tonic-gate    warn "Error: $file is unreadable: $!\n";
488*0Sstevel@tonic-gate    return undef;
489*0Sstevel@tonic-gate  }
490*0Sstevel@tonic-gate
491*0Sstevel@tonic-gate  local $/ = undef;
492*0Sstevel@tonic-gate  my $pod = <POD>;
493*0Sstevel@tonic-gate  close(POD) || die "Error closing $file: $!\n";
494*0Sstevel@tonic-gate  unless($pod =~ /\n=(head\d|pod|over|item)\b/s) {
495*0Sstevel@tonic-gate    warn "No POD in $file, skipping.\n"
496*0Sstevel@tonic-gate      if($verbose);
497*0Sstevel@tonic-gate    return 0;
498*0Sstevel@tonic-gate  }
499*0Sstevel@tonic-gate
500*0Sstevel@tonic-gate  return 1;
501*0Sstevel@tonic-gate}
502*0Sstevel@tonic-gate
503*0Sstevel@tonic-gate=head1 AUTHOR
504*0Sstevel@tonic-gate
505*0Sstevel@tonic-gatePlease report bugs using L<http://rt.cpan.org>.
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gateMarek Rouchal E<lt>marekr@cpan.orgE<gt>,
508*0Sstevel@tonic-gateheavily borrowing code from Nick Ing-Simmons' PodToHtml.
509*0Sstevel@tonic-gate
510*0Sstevel@tonic-gateTim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt> provided
511*0Sstevel@tonic-gateC<pod_where> and C<contains_pod>.
512*0Sstevel@tonic-gate
513*0Sstevel@tonic-gate=head1 SEE ALSO
514*0Sstevel@tonic-gate
515*0Sstevel@tonic-gateL<Pod::Parser>, L<Pod::Checker>, L<perldoc>
516*0Sstevel@tonic-gate
517*0Sstevel@tonic-gate=cut
518*0Sstevel@tonic-gate
519*0Sstevel@tonic-gate1;
520*0Sstevel@tonic-gate
521