make perldoc -f grok nested =items
[p5sagit/p5-mst-13.2.git] / utils / perldoc.PL
1 #!/usr/local/bin/perl
2
3 use Config;
4 use File::Basename qw(&basename &dirname);
5 use Cwd;
6
7 # List explicitly here the variables you want Configure to
8 # generate.  Metaconfig only looks for shell variables, so you
9 # have to mention them as if they were shell variables, not
10 # %Config entries.  Thus you write
11 #  $startperl
12 # to ensure Configure will look for $Config{startperl}.
13
14 # This forces PL files to create target in same directory as PL file.
15 # This is so that make depend always knows where to find PL derivatives.
16 $origdir = cwd;
17 chdir dirname($0);
18 $file = basename($0, '.PL');
19 $file .= '.com' if $^O eq 'VMS';
20
21 open OUT,">$file" or die "Can't create $file: $!";
22
23 print "Extracting $file (with variable substitutions)\n";
24
25 # In this section, perl variables will be expanded during extraction.
26 # You can use $Config{...} to use Configure variables.
27
28 print OUT <<"!GROK!THIS!";
29 $Config{startperl}
30     eval 'exec $Config{perlpath} -S \$0 \${1+"\$@"}'
31         if 0;
32
33 use strict;
34 my \@pagers = ();
35 push \@pagers, "$Config{'pager'}" if -x "$Config{'pager'}";
36 !GROK!THIS!
37
38 # In the following, perl variables are not expanded during extraction.
39
40 print OUT <<'!NO!SUBS!';
41
42 #
43 # Perldoc revision #1 -- look up a piece of documentation in .pod format that
44 # is embedded in the perl installation tree.
45 #
46 # This is not to be confused with Tom Christianson's perlman, which is a
47 # man replacement, written in perl. This perldoc is strictly for reading
48 # the perl manuals, though it too is written in perl.
49
50 if(@ARGV<1) {
51         my $me = $0;            # Editing $0 is unportable
52         $me =~ s,.*/,,;
53         die <<EOF;
54 Usage: $me [-h] [-r] [-i] [-v] [-t] [-u] [-m] [-l] [-F] [-X] PageName|ModuleName|ProgramName
55        $me -f PerlFunc
56        $me -q FAQKeywords
57
58 The -h option prints more help.  Also try "perldoc perldoc" to get
59 aquainted with the system.
60 EOF
61 }
62
63 use Getopt::Std;
64 use Config '%Config';
65
66 my @global_found = ();
67 my $global_target = "";
68
69 my $Is_VMS = $^O eq 'VMS';
70 my $Is_MSWin32 = $^O eq 'MSWin32';
71 my $Is_Dos = $^O eq 'dos';
72
73 sub usage{
74     warn "@_\n" if @_;
75     # Erase evidence of previous errors (if any), so exit status is simple.
76     $! = 0;
77     die <<EOF;
78 perldoc [options] PageName|ModuleName|ProgramName...
79 perldoc [options] -f BuiltinFunction
80 perldoc [options] -q FAQRegex
81
82 Options:
83     -h   Display this help message
84     -r   Recursive search (slow)
85     -i   Ignore case 
86     -t   Display pod using pod2text instead of pod2man and nroff
87              (-t is the default on win32)
88     -u   Display unformatted pod text
89     -m   Display module's file in its entirety
90     -l   Display the module's file name
91     -F   Arguments are file names, not modules
92     -v   Verbosely describe what's going on
93     -X   use index if present (looks for pod.idx at $Config{archlib})
94     -q   Search the text of questions (not answers) in perlfaq[1-9]
95
96 PageName|ModuleName...
97          is the name of a piece of documentation that you want to look at. You 
98          may either give a descriptive name of the page (as in the case of
99          `perlfunc') the name of a module, either like `Term::Info', 
100          `Term/Info', the partial name of a module, like `info', or 
101          `makemaker', or the name of a program, like `perldoc'.
102
103 BuiltinFunction
104          is the name of a perl function.  Will extract documentation from
105          `perlfunc'.
106
107 FAQRegex
108          is a regex. Will search perlfaq[1-9] for and extract any
109          questions that match.
110
111 Any switches in the PERLDOC environment variable will be used before the 
112 command line arguments.  The optional pod index file contains a list of
113 filenames, one per line.
114
115 EOF
116 }
117
118 if( defined $ENV{"PERLDOC"} ) {
119     require Text::ParseWords;
120     unshift(@ARGV, Text::ParseWords::shellwords($ENV{"PERLDOC"}));
121 }
122 !NO!SUBS!
123
124 my $getopts = "mhtluvriFf:Xq:";
125 print OUT <<"!GET!OPTS!";
126
127 use vars qw( @{[map "\$opt_$_", ($getopts =~ /\w/g)]} );
128
129 getopts("$getopts") || usage;
130 !GET!OPTS!
131
132 print OUT <<'!NO!SUBS!';
133
134 usage if $opt_h;
135
136 my $podidx;
137 if( $opt_X ) {
138     $podidx = "$Config{'archlib'}/pod.idx";
139     $podidx = "" unless -f $podidx && -r _ && -M _ <= 7;
140 }
141
142 if( (my $opts = do{ local $^W; $opt_t + $opt_u + $opt_m + $opt_l }) > 1) {
143     usage("only one of -t, -u, -m or -l")
144 } elsif ($Is_MSWin32 || $Is_Dos) {
145     $opt_t = 1 unless $opts
146 }
147
148 if ($opt_t) { require Pod::Text; import Pod::Text; }
149
150 my @pages;
151 if ($opt_f) {
152    @pages = ("perlfunc");
153 } elsif ($opt_q) {
154    @pages = ("perlfaq1" .. "perlfaq9");
155 } else {
156    @pages = @ARGV;
157 }
158
159 # Does this look like a module or extension directory?
160 if (-f "Makefile.PL") {
161         # Add ., lib and blib/* libs to @INC (if they exist)
162         unshift(@INC, '.');
163         unshift(@INC, 'lib') if -d 'lib';
164         require ExtUtils::testlib;
165 }
166
167
168
169 sub containspod {
170     my($file, $readit) = @_;
171     return 1 if !$readit && $file =~ /\.pod$/i;
172     local($_);
173     open(TEST,"<$file");
174     while(<TEST>) {
175         if(/^=head/) {
176             close(TEST);
177             return 1;
178         }
179     }
180     close(TEST);
181     return 0;
182 }
183
184 sub minus_f_nocase {
185      my($dir,$file) = @_;
186      my $path = join('/',$dir,$file);
187      return $path if -f $path and -r _;
188      if (!$opt_i or $Is_VMS or $Is_MSWin32 or $Is_Dos or $^O eq 'os2') {
189         # on a case-forgiving file system or if case is important 
190         # that is it all we can do
191         warn "Ignored $path: unreadable\n" if -f _;
192         return '';
193      }
194      local *DIR;
195      local($")="/";
196      my @p = ($dir);
197      my($p,$cip);
198      foreach $p (split(/\//, $file)){
199         my $try = "@p/$p";
200         stat $try;
201         if (-d _){
202             push @p, $p;
203             if ( $p eq $global_target) {
204                 my $tmp_path = join ('/', @p);
205                 my $path_f = 0;
206                 for (@global_found) {
207                     $path_f = 1 if $_ eq $tmp_path;
208                 }
209                 push (@global_found, $tmp_path) unless $path_f;
210                 print STDERR "Found as @p but directory\n" if $opt_v;
211             }
212         } elsif (-f _ && -r _) {
213             return $try;
214         } elsif (-f _) {
215             warn "Ignored $try: unreadable\n";
216         } else {
217             my $found=0;
218             my $lcp = lc $p;
219             opendir DIR, "@p";
220             while ($cip=readdir(DIR)) {
221                 if (lc $cip eq $lcp){
222                     $found++;
223                     last;
224                 }
225             }
226             closedir DIR;
227             return "" unless $found;
228             push @p, $cip;
229             return "@p" if -f "@p" and -r _;
230             warn "Ignored @p: unreadable\n" if -f _;
231         }
232      }
233      return "";
234 }
235
236
237 sub check_file {
238     my($dir,$file) = @_;
239     if ($opt_m) {
240         return minus_f_nocase($dir,$file);
241     } else {
242         my $path = minus_f_nocase($dir,$file);
243         return $path if length $path and containspod($path);
244     }
245     return "";
246 }
247
248
249 sub searchfor {
250     my($recurse,$s,@dirs) = @_;
251     $s =~ s!::!/!g;
252     $s = VMS::Filespec::unixify($s) if $Is_VMS;
253     return $s if -f $s && containspod($s);
254     printf STDERR "Looking for $s in @dirs\n" if $opt_v;
255     my $ret;
256     my $i;
257     my $dir;
258     $global_target = (split('/', $s))[-1];
259     for ($i=0; $i<@dirs; $i++) {
260         $dir = $dirs[$i];
261         ($dir = VMS::Filespec::unixpath($dir)) =~ s!/$!! if $Is_VMS;
262         if (       ( $ret = check_file $dir,"$s.pod")
263                 or ( $ret = check_file $dir,"$s.pm")
264                 or ( $ret = check_file $dir,$s)
265                 or ( $Is_VMS and
266                      $ret = check_file $dir,"$s.com")
267                 or ( $^O eq 'os2' and 
268                      $ret = check_file $dir,"$s.cmd")
269                 or ( ($Is_MSWin32 or $Is_Dos or $^O eq 'os2') and
270                      $ret = check_file $dir,"$s.bat")
271                 or ( $ret = check_file "$dir/pod","$s.pod")
272                 or ( $ret = check_file "$dir/pod",$s)
273         ) {
274             return $ret;
275         }
276
277         if ($recurse) {
278             opendir(D,$dir);
279             my @newdirs = map "$dir/$_", grep {
280                 not /^\.\.?$/ and
281                 not /^auto$/  and   # save time! don't search auto dirs
282                 -d  "$dir/$_"
283             } readdir D;
284             closedir(D);
285             next unless @newdirs;
286             @newdirs = map((s/.dir$//,$_)[1],@newdirs) if $Is_VMS;
287             print STDERR "Also looking in @newdirs\n" if $opt_v;
288             push(@dirs,@newdirs);
289         }
290     }
291     return ();
292 }
293
294 sub filter_nroff {
295   my @data = split /\n{2,}/, shift;
296   shift @data while @data and $data[0] !~ /\S/; # Go to header
297   shift @data if @data and $data[0] =~ /Contributed\s+Perl/; # Skip header
298   pop @data if @data and $data[-1] =~ /^\w/; # Skip footer, like
299                                 # 28/Jan/99 perl 5.005, patch 53 1
300   join "\n\n", @data;
301 }
302
303 my @found;
304 foreach (@pages) {
305         if ($podidx && open(PODIDX, $podidx)) {
306             my $searchfor = $_;
307             local($_);
308             $searchfor =~ s,::,/,g;
309             print STDERR "Searching for '$searchfor' in $podidx\n" if $opt_v;
310             while (<PODIDX>) {
311                 chomp;
312                 push(@found, $_) if m,/$searchfor(?:\.(?:pod|pm))?$,i;
313             }
314             close(PODIDX);
315             next;
316         }
317         print STDERR "Searching for $_\n" if $opt_v;
318         # We must look both in @INC for library modules and in PATH
319         # for executables, like h2xs or perldoc itself.
320         my @searchdirs = @INC;
321         if ($opt_F) {
322           next unless -r;
323           push @found, $_ if $opt_m or containspod($_);
324           next;
325         }
326         unless ($opt_m) { 
327             if ($Is_VMS) {
328                 my($i,$trn);
329                 for ($i = 0; $trn = $ENV{'DCL$PATH;'.$i}; $i++) {
330                     push(@searchdirs,$trn);
331                 }
332                 push(@searchdirs,'perl_root:[lib.pod]')  # installed pods
333             } else {
334                 push(@searchdirs, grep(-d, split($Config{path_sep}, 
335                                                  $ENV{'PATH'})));
336             }
337         }
338         my @files = searchfor(0,$_,@searchdirs);
339         if( @files ) {
340                 print STDERR "Found as @files\n" if $opt_v;
341         } else {
342                 # no match, try recursive search
343
344                 @searchdirs = grep(!/^\.$/,@INC);
345
346                 @files= searchfor(1,$_,@searchdirs) if $opt_r;
347                 if( @files ) {
348                         print STDERR "Loosely found as @files\n" if $opt_v;
349                 } else {
350                         print STDERR "No documentation found for \"$_\".\n";
351                         if (@global_found) {
352                             print STDERR "However, try\n";
353                             for my $dir (@global_found) {
354                                 opendir(DIR, $dir) or die "$!";
355                                 while (my $file = readdir(DIR)) {
356                                     next if ($file =~ /^\./);
357                                     $file =~ s/\.(pm|pod)$//;
358                                     print STDERR "\tperldoc $_\::$file\n";
359                                 }
360                                 closedir DIR;
361                             }
362                         }
363                 }
364         }
365         push(@found,@files);
366 }
367
368 if(!@found) {
369         exit ($Is_VMS ? 98962 : 1);
370 }
371
372 if ($opt_l) {
373     print join("\n", @found), "\n";
374     exit;
375 }
376
377 my $lines = $ENV{LINES} || 24;
378
379 my $no_tty;
380 if( ! -t STDOUT ) { $no_tty = 1 }
381
382 my $tmp;
383 if ($Is_MSWin32) {
384         $tmp = "$ENV{TEMP}\\perldoc1.$$";
385         push @pagers, qw( more< less notepad );
386         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
387         for (@found) { s,/,\\,g }
388 } elsif ($Is_VMS) {
389         $tmp = 'Sys$Scratch:perldoc.tmp1_'.$$;
390         push @pagers, qw( most more less type/page );
391 } elsif ($Is_Dos) {
392         $tmp = "$ENV{TEMP}/perldoc1.$$";
393         $tmp =~ tr!\\/!//!s;
394         push @pagers, qw( less.exe more.com< );
395         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
396 } else {
397         if ($^O eq 'os2') {
398           require POSIX;
399           $tmp = POSIX::tmpnam();
400           unshift @pagers, 'less', 'cmd /c more <';
401         } else {
402           $tmp = "/tmp/perldoc1.$$";
403         }
404         push @pagers, qw( more less pg view cat );
405         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
406 }
407 unshift @pagers, $ENV{PERLDOC_PAGER} if $ENV{PERLDOC_PAGER};
408
409 if ($opt_m) {
410         foreach my $pager (@pagers) {
411                 system("$pager @found") or exit;
412         }
413         if ($Is_VMS) { eval 'use vmsish qw(status exit); exit $?' }
414         exit 1;
415 }
416
417 my @pod;
418 if ($opt_f) {
419    my $perlfunc = shift @found;
420    open(PFUNC, $perlfunc) or die "Can't open $perlfunc: $!";
421
422    # Functions like -r, -e, etc. are listed under `-X'.
423    my $search_string = ($opt_f =~ /^-[rwxoRWXOeszfdlpSbctugkTBMAC]$/) ? 'I<-X' : $opt_f ;
424
425    # Skip introduction
426    while (<PFUNC>) {
427        last if /^=head2 Alphabetical Listing of Perl Functions/;
428    }
429
430    # Look for our function
431    my $found = 0;
432    my $inlist = 0;
433    while (<PFUNC>) {
434        if (/^=item\s+\Q$search_string\E\b/o)  {
435            $found = 1;
436        } elsif (/^=item/) {
437            last if $found > 1 and not $inlist;
438        }
439        next unless $found;
440        if (/^=over/) {
441            ++$inlist;
442        }
443        elsif (/^=back/) {
444            --$inlist;
445        }
446        push @pod, $_;
447        ++$found if /^\w/;       # found descriptive text
448    }
449    if (!@pod) {
450        die "No documentation for perl function `$opt_f' found\n";
451    }
452 }
453
454 if ($opt_q) {
455    local @ARGV = @found;        # I'm lazy, sue me.
456    my $found = 0;
457    my %found_in;
458
459    while (<>) {
460       if (/^=head2\s+.*(?:$opt_q)/oi) {
461          $found = 1;
462          push @pod, "=head1 Found in $ARGV\n\n" unless $found_in{$ARGV}++;
463       } elsif (/^=head2/) {
464          $found = 0;
465       }
466       next unless $found;
467       push @pod, $_;
468    }
469
470    if (!@pod) {
471       die "No documentation for perl FAQ keyword `$opt_q' found\n";
472    }
473 }
474
475 my $tmp1;
476 my $filter;
477
478 if (@pod) {
479   $tmp1 = $tmp . "_";
480   open(TMP,">$tmp1") or die "open '$tmp1': $!";
481   print TMP "=over 8\n\n";
482   print TMP @pod;
483   print TMP "=back\n";
484   close(TMP) or die "close '$tmp1': $!";
485   @found = $tmp1;
486   $filter = 1;
487 }
488
489 foreach (@found) {
490
491         my $err;
492         if($opt_t) {
493                 open(TMP,">>$tmp");
494                 Pod::Text::pod2text($_,*TMP);
495                 close(TMP);
496         } elsif(not $opt_u) {
497                 my $cmd = "pod2man --lax $_ | nroff -man";
498                 $cmd .= " | col -x" if $^O =~ /hpux/;
499                 my $rslt = `$cmd`;
500                 $rslt = filter_nroff $rslt if $filter;
501                 unless(($err = $?)) {
502                         open(TMP,">>$tmp");
503                         print TMP $rslt;
504                         close TMP;
505                 }
506         }
507
508         if( $opt_u or $err or -z $tmp) {
509                 open(OUT,">>$tmp");
510                 open(IN,"<$_");
511                 my $cut = 1;
512                 while (<IN>) {
513                         $cut = $1 eq 'cut' if /^=(\w+)/;
514                         next if $cut;
515                         print OUT;
516                 }
517                 close(IN);
518                 close(OUT);
519         }
520 }
521
522 if( $no_tty ) {
523         open(TMP,"<$tmp");
524         print while <TMP>;
525         close(TMP);
526 } else {
527         foreach my $pager (@pagers) {
528                 system("$pager $tmp") or last;
529         }
530 }
531
532 1 while unlink($tmp); #Possibly pointless VMSism
533 if (defined $tmp1) {
534   1 while unlink($tmp1); #Possibly pointless VMSism
535 }
536
537 exit 0;
538
539 __END__
540
541 =head1 NAME
542
543 perldoc - Look up Perl documentation in pod format.
544
545 =head1 SYNOPSIS
546
547 B<perldoc> [B<-h>] [B<-v>] [B<-t>] [B<-u>] [B<-m>] [B<-l>] [B<-F>]  [B<-X>] PageName|ModuleName|ProgramName
548
549 B<perldoc> B<-f> BuiltinFunction
550
551 B<perldoc> B<-q> FAQ Keyword
552
553 =head1 DESCRIPTION
554
555 I<perldoc> looks up a piece of documentation in .pod format that is embedded
556 in the perl installation tree or in a perl script, and displays it via
557 C<pod2man | nroff -man | $PAGER>. (In addition, if running under HP-UX,
558 C<col -x> will be used.) This is primarily used for the documentation for
559 the perl library modules.
560
561 Your system may also have man pages installed for those modules, in
562 which case you can probably just use the man(1) command.
563
564 =head1 OPTIONS
565
566 =over 5
567
568 =item B<-h> help
569
570 Prints out a brief help message.
571
572 =item B<-v> verbose
573
574 Describes search for the item in detail.
575
576 =item B<-t> text output
577
578 Display docs using plain text converter, instead of nroff. This may be faster,
579 but it won't look as nice.
580
581 =item B<-u> unformatted
582
583 Find docs only; skip reformatting by pod2*
584
585 =item B<-m> module
586
587 Display the entire module: both code and unformatted pod documentation.
588 This may be useful if the docs don't explain a function in the detail
589 you need, and you'd like to inspect the code directly; perldoc will find
590 the file for you and simply hand it off for display.
591
592 =item B<-l> file name only
593
594 Display the file name of the module found.
595
596 =item B<-F> file names
597
598 Consider arguments as file names, no search in directories will be performed.
599
600 =item B<-f> perlfunc
601
602 The B<-f> option followed by the name of a perl built in function will
603 extract the documentation of this function from L<perlfunc>.
604
605 =item B<-q> perlfaq
606
607 The B<-q> option takes a regular expression as an argument.  It will search
608 the question headings in perlfaq[1-9] and print the entries matching
609 the regular expression.
610
611 =item B<-X> use an index if present
612
613 The B<-X> option looks for a entry whose basename matches the name given on the
614 command line in the file C<$Config{archlib}/pod.idx>.  The pod.idx file should
615 contain fully qualified filenames, one per line.
616
617 =item B<PageName|ModuleName|ProgramName>
618
619 The item you want to look up.  Nested modules (such as C<File::Basename>)
620 are specified either as C<File::Basename> or C<File/Basename>.  You may also
621 give a descriptive name of a page, such as C<perlfunc>. You may also give a
622 partial or wrong-case name, such as "basename" for "File::Basename", but
623 this will be slower, if there is more then one page with the same partial
624 name, you will only get the first one.
625
626 =back
627
628 =head1 ENVIRONMENT
629
630 Any switches in the C<PERLDOC> environment variable will be used before the 
631 command line arguments.  C<perldoc> also searches directories
632 specified by the C<PERL5LIB> (or C<PERLLIB> if C<PERL5LIB> is not
633 defined) and C<PATH> environment variables.
634 (The latter is so that embedded pods for executables, such as
635 C<perldoc> itself, are available.)  C<perldoc> will use, in order of
636 preference, the pager defined in C<PERLDOC_PAGER>, C<MANPAGER>, or
637 C<PAGER> before trying to find a pager on its own.  (C<MANPAGER> is not
638 used if C<perldoc> was told to display plain text or unformatted pod.)
639
640 One useful value for C<PERLDOC_PAGER> is C<less -+C -E>.
641
642 =head1 AUTHOR
643
644 Kenneth Albanowski <kjahds@kjahds.com>
645
646 Minor updates by Andy Dougherty <doughera@lafcol.lafayette.edu>
647
648 =cut
649
650 #
651 # Version 1.14: Wed Jul 15 01:50:20 EST 1998
652 #       Robin Barker <rmb1@cise.npl.co.uk>
653 #       -strict, -w cleanups
654 # Version 1.13: Fri Feb 27 16:20:50 EST 1997
655 #       Gurusamy Sarathy <gsar@umich.edu>
656 #       -doc tweaks for -F and -X options
657 # Version 1.12: Sat Apr 12 22:41:09 EST 1997
658 #       Gurusamy Sarathy <gsar@umich.edu>
659 #       -various fixes for win32
660 # Version 1.11: Tue Dec 26 09:54:33 EST 1995
661 #       Kenneth Albanowski <kjahds@kjahds.com>
662 #   -added Charles Bailey's further VMS patches, and -u switch
663 #   -added -t switch, with pod2text support
664
665 # Version 1.10: Thu Nov  9 07:23:47 EST 1995
666 #               Kenneth Albanowski <kjahds@kjahds.com>
667 #       -added VMS support
668 #       -added better error recognition (on no found pages, just exit. On
669 #        missing nroff/pod2man, just display raw pod.)
670 #       -added recursive/case-insensitive matching (thanks, Andreas). This
671 #        slows things down a bit, unfortunately. Give a precise name, and
672 #        it'll run faster.
673 #
674 # Version 1.01: Tue May 30 14:47:34 EDT 1995
675 #               Andy Dougherty  <doughera@lafcol.lafayette.edu>
676 #   -added pod documentation.
677 #   -added PATH searching.
678 #   -added searching pod/ subdirectory (mainly to pick up perlfunc.pod
679 #    and friends.
680 #
681 #
682 # TODO:
683 #
684 #       Cache directories read during sloppy match
685 !NO!SUBS!
686
687 close OUT or die "Can't close $file: $!";
688 chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
689 exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';
690 chdir $origdir;