Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / perl5 / 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 my @found;
295 foreach (@pages) {
296         if ($podidx && open(PODIDX, $podidx)) {
297             my $searchfor = $_;
298             local($_);
299             $searchfor =~ s,::,/,g;
300             print STDERR "Searching for '$searchfor' in $podidx\n" if $opt_v;
301             while (<PODIDX>) {
302                 chomp;
303                 push(@found, $_) if m,/$searchfor(?:\.(?:pod|pm))?$,i;
304             }
305             close(PODIDX);
306             next;
307         }
308         print STDERR "Searching for $_\n" if $opt_v;
309         # We must look both in @INC for library modules and in PATH
310         # for executables, like h2xs or perldoc itself.
311         my @searchdirs = @INC;
312         if ($opt_F) {
313           next unless -r;
314           push @found, $_ if $opt_m or containspod($_);
315           next;
316         }
317         unless ($opt_m) { 
318             if ($Is_VMS) {
319                 my($i,$trn);
320                 for ($i = 0; $trn = $ENV{'DCL$PATH'.$i}; $i++) {
321                     push(@searchdirs,$trn);
322                 }
323                 push(@searchdirs,'perl_root:[lib.pod]')  # installed pods
324             } else {
325                 push(@searchdirs, grep(-d, split($Config{path_sep}, 
326                                                  $ENV{'PATH'})));
327             }
328         }
329         my @files = searchfor(0,$_,@searchdirs);
330         if( @files ) {
331                 print STDERR "Found as @files\n" if $opt_v;
332         } else {
333                 # no match, try recursive search
334                 
335                 @searchdirs = grep(!/^\.$/,@INC);
336                 
337                 @files= searchfor(1,$_,@searchdirs) if $opt_r;
338                 if( @files ) {
339                         print STDERR "Loosely found as @files\n" if $opt_v;
340                 } else {
341                         print STDERR "No documentation found for \"$_\".\n";
342                         if (@global_found) {
343                             print STDERR "However, try\n";
344                             for my $dir (@global_found) {
345                                 opendir(DIR, $dir) or die "$!";
346                                 while (my $file = readdir(DIR)) {
347                                     next if ($file =~ /^\./);
348                                     $file =~ s/\.(pm|pod)$//;
349                                     print STDERR "\tperldoc $_\::$file\n";
350                                 }
351                                 closedir DIR;
352                             }
353                         }
354                 }
355         }
356         push(@found,@files);
357 }
358
359 if(!@found) {
360         exit ($Is_VMS ? 98962 : 1);
361 }
362
363 if ($opt_l) {
364     print join("\n", @found), "\n";
365     exit;
366 }
367
368 my $lines = $ENV{LINES} || 24;
369
370 my $no_tty;
371 if( ! -t STDOUT ) { $no_tty = 1 }
372
373 my $tmp;
374 if ($Is_MSWin32) {
375         $tmp = "$ENV{TEMP}\\perldoc1.$$";
376         push @pagers, qw( more< less notepad );
377         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
378 } elsif ($Is_VMS) {
379         $tmp = 'Sys$Scratch:perldoc.tmp1_'.$$;
380         push @pagers, qw( most more less type/page );
381 } elsif ($Is_Dos) {
382         $tmp = "$ENV{TEMP}/perldoc1.$$";
383         $tmp =~ tr!\\/!//!s;
384         push @pagers, qw( less.exe more.com< );
385         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
386 } else {
387         if ($^O eq 'os2') {
388           require POSIX;
389           $tmp = POSIX::tmpnam();
390           unshift @pagers, 'less', 'cmd /c more <';
391         } else {
392           $tmp = "/tmp/perldoc1.$$";      
393         }
394         push @pagers, qw( more less pg view cat );
395         unshift @pagers, $ENV{PAGER}  if $ENV{PAGER};
396 }
397 unshift @pagers, $ENV{PERLDOC_PAGER} if $ENV{PERLDOC_PAGER};
398
399 if ($opt_m) {
400         foreach my $pager (@pagers) {
401                 system("$pager @found") or exit;
402         }
403         if ($Is_VMS) { eval 'use vmsish qw(status exit); exit $?' }
404         exit 1;
405
406
407 if ($opt_f) {
408    my $perlfunc = shift @found;
409    open(PFUNC, $perlfunc) or die "Can't open $perlfunc: $!";
410
411    # Functions like -r, -e, etc. are listed under `-X'.
412    my $search_string = ($opt_f =~ /^-[rwxoRWXOeszfdlpSbctugkTBMAC]$/) ? 'I<-X' : $opt_f ;
413
414    # Skip introduction
415    while (<PFUNC>) {
416        last if /^=head2 Alphabetical Listing of Perl Functions/;
417    }
418
419    # Look for our function
420    my $found = 0;
421    my @pod;
422    while (<PFUNC>) {
423        if (/^=item\s+\Q$search_string\E\b/o)  {
424            $found = 1;
425        } elsif (/^=item/) {
426            last if $found > 1;
427        }
428        next unless $found;
429        push @pod, $_;
430        ++$found if /^\w/;       # found descriptive text
431    }
432    if (@pod) {
433        if ($opt_t) {
434            open(FORMATTER, "| pod2text") || die "Can't start filter";
435            print FORMATTER "=over 8\n\n";
436            print FORMATTER @pod;
437            print FORMATTER "=back\n";
438            close(FORMATTER);
439        } elsif (@pod < $lines-2) {
440            print @pod;
441        } else {
442            foreach my $pager (@pagers) {
443                 open (PAGER, "| $pager") or next;
444                 print PAGER @pod ;
445                 close(PAGER) or next;
446                 last;
447            }
448        }
449    } else {
450        die "No documentation for perl function `$opt_f' found\n";
451    }
452    exit;
453 }
454
455 if ($opt_q) {
456    local @ARGV = @found;        # I'm lazy, sue me.
457    my $found = 0;
458    my %found_in;
459    my @pod;
460
461    while (<>) {
462       if (/^=head2\s+.*(?:$opt_q)/oi) {
463          $found = 1;
464          push @pod, "=head1 Found in $ARGV\n\n" unless $found_in{$ARGV}++;
465       } elsif (/^=head2/) {
466          $found = 0;
467       }
468       next unless $found;
469       push @pod, $_;
470    }
471    
472    if (@pod) {
473       if ($opt_t) {
474          open(FORMATTER, "| pod2text") || die "Can't start filter";
475          print FORMATTER "=over 8\n\n";
476          print FORMATTER @pod;
477          print FORMATTER "=back\n";
478          close(FORMATTER);
479       } elsif (@pod < $lines-2) {
480          print @pod;
481       } else {
482          foreach my $pager (@pagers) {
483             open (PAGER, "| $pager") or next;
484             print PAGER @pod ;
485             close(PAGER) or next;
486             last;
487          }
488       }
489    } else {
490       die "No documentation for perl FAQ keyword `$opt_q' found\n";
491    }
492    exit;
493 }
494
495 foreach (@found) {
496
497         my $err;
498         if($opt_t) {
499                 open(TMP,">>$tmp");
500                 Pod::Text::pod2text($_,*TMP);
501                 close(TMP);
502         } elsif(not $opt_u) {
503                 my $cmd = "pod2man --lax $_ | nroff -man";
504                 $cmd .= " | col -x" if $^O =~ /hpux/;
505                 my $rslt = `$cmd`;
506                 unless(($err = $?)) {
507                         open(TMP,">>$tmp");
508                         print TMP $rslt;
509                         close TMP;
510                 }
511         }
512                                                         
513         if( $opt_u or $err or -z $tmp) {
514                 open(OUT,">>$tmp");
515                 open(IN,"<$_");
516                 my $cut = 1;
517                 while (<IN>) {
518                         $cut = $1 eq 'cut' if /^=(\w+)/;
519                         next if $cut;
520                         print OUT;
521                 }
522                 close(IN);
523                 close(OUT);
524         }
525 }
526
527 if( $no_tty ) {
528         open(TMP,"<$tmp");
529         print while <TMP>;
530         close(TMP);
531 } else {
532         foreach my $pager (@pagers) {
533                 system("$pager $tmp") or last;
534         }
535 }
536
537 1 while unlink($tmp); #Possibly pointless VMSism
538
539 exit 0;
540
541 __END__
542
543 =head1 NAME
544
545 perldoc - Look up Perl documentation in pod format.
546
547 =head1 SYNOPSIS
548
549 B<perldoc> [B<-h>] [B<-v>] [B<-t>] [B<-u>] [B<-m>] [B<-l>] [B<-F>]  [B<-X>] PageName|ModuleName|ProgramName
550
551 B<perldoc> B<-f> BuiltinFunction
552
553 B<perldoc> B<-q> FAQ Keyword
554
555 =head1 DESCRIPTION
556
557 I<perldoc> looks up a piece of documentation in .pod format that is embedded
558 in the perl installation tree or in a perl script, and displays it via
559 C<pod2man | nroff -man | $PAGER>. (In addition, if running under HP-UX,
560 C<col -x> will be used.) This is primarily used for the documentation for
561 the perl library modules.
562
563 Your system may also have man pages installed for those modules, in
564 which case you can probably just use the man(1) command.
565
566 =head1 OPTIONS
567
568 =over 5
569
570 =item B<-h> help
571
572 Prints out a brief help message.
573
574 =item B<-v> verbose
575
576 Describes search for the item in detail.
577
578 =item B<-t> text output
579
580 Display docs using plain text converter, instead of nroff. This may be faster,
581 but it won't look as nice.
582
583 =item B<-u> unformatted
584
585 Find docs only; skip reformatting by pod2*
586
587 =item B<-m> module
588
589 Display the entire module: both code and unformatted pod documentation.
590 This may be useful if the docs don't explain a function in the detail
591 you need, and you'd like to inspect the code directly; perldoc will find
592 the file for you and simply hand it off for display.
593
594 =item B<-l> file name only
595
596 Display the file name of the module found.
597
598 =item B<-F> file names
599
600 Consider arguments as file names, no search in directories will be performed.
601
602 =item B<-f> perlfunc
603
604 The B<-f> option followed by the name of a perl built in function will
605 extract the documentation of this function from L<perlfunc>.
606
607 =item B<-q> perlfaq
608
609 The B<-q> option takes a regular expression as an argument.  It will search
610 the question headings in perlfaq[1-9] and print the entries matching
611 the regular expression.
612
613 =item B<-X> use an index if present
614
615 The B<-X> option looks for a entry whose basename matches the name given on the
616 command line in the file C<$Config{archlib}/pod.idx>.  The pod.idx file should
617 contain fully qualified filenames, one per line.
618
619 =item B<PageName|ModuleName|ProgramName>
620
621 The item you want to look up.  Nested modules (such as C<File::Basename>)
622 are specified either as C<File::Basename> or C<File/Basename>.  You may also
623 give a descriptive name of a page, such as C<perlfunc>. You make also give a
624 partial or wrong-case name, such as "basename" for "File::Basename", but
625 this will be slower, if there is more then one page with the same partial
626 name, you will only get the first one.
627
628 =back
629
630 =head1 ENVIRONMENT
631
632 Any switches in the C<PERLDOC> environment variable will be used before the 
633 command line arguments.  C<perldoc> also searches directories
634 specified by the C<PERL5LIB> (or C<PERLLIB> if C<PERL5LIB> is not
635 defined) and C<PATH> environment variables.
636 (The latter is so that embedded pods for executables, such as
637 C<perldoc> itself, are available.)  C<perldoc> will use, in order of
638 preference, the pager defined in C<PERLDOC_PAGER>, C<MANPAGER>, or
639 C<PAGER> before trying to find a pager on its own.  (C<MANPAGER> is not
640 used if C<perldoc> was told to display plain text or unformatted pod.)
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;