adjust searchdict.t for EBCDIC (still needs documenting)
[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
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 $file: 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 $file: 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    # Skip introduction
412    while (<PFUNC>) {
413        last if /^=head2 Alphabetical Listing of Perl Functions/;
414    }
415
416    # Look for our function
417    my $found = 0;
418    my @pod;
419    while (<PFUNC>) {
420        if (/^=item\s+\Q$opt_f\E\b/o)  {
421            $found = 1;
422        } elsif (/^=item/) {
423            last if $found > 1;
424        }
425        next unless $found;
426        push @pod, $_;
427        ++$found if /^\w/;       # found descriptive text
428    }
429    if (@pod) {
430        if ($opt_t) {
431            open(FORMATTER, "| pod2text") || die "Can't start filter";
432            print FORMATTER "=over 8\n\n";
433            print FORMATTER @pod;
434            print FORMATTER "=back\n";
435            close(FORMATTER);
436        } elsif (@pod < $lines-2) {
437            print @pod;
438        } else {
439            foreach my $pager (@pagers) {
440                 open (PAGER, "| $pager") or next;
441                 print PAGER @pod ;
442                 close(PAGER) or next;
443                 last;
444            }
445        }
446    } else {
447        die "No documentation for perl function `$opt_f' found\n";
448    }
449    exit;
450 }
451
452 if ($opt_q) {
453    local @ARGV = @found;        # I'm lazy, sue me.
454    my $found = 0;
455    my %found_in;
456    my @pod;
457
458    while (<>) {
459       if (/^=head2\s+.*$opt_q/oi) {
460          $found = 1;
461          push @pod, "=head1 Found in $ARGV\n\n" unless $found_in{$ARGV}++;
462       } elsif (/^=head2/) {
463          $found = 0;
464       }
465       next unless $found;
466       push @pod, $_;
467    }
468    
469    if (@pod) {
470       if ($opt_t) {
471          open(FORMATTER, "| pod2text") || die "Can't start filter";
472          print FORMATTER "=over 8\n\n";
473          print FORMATTER @pod;
474          print FORMATTER "=back\n";
475          close(FORMATTER);
476       } elsif (@pod < $lines-2) {
477          print @pod;
478       } else {
479          foreach my $pager (@pagers) {
480             open (PAGER, "| $pager") or next;
481             print PAGER @pod ;
482             close(PAGER) or next;
483             last;
484          }
485       }
486    } else {
487       die "No documentation for perl FAQ keyword `$opt_q' found\n";
488    }
489    exit;
490 }
491
492 foreach (@found) {
493
494         my $err;
495         if($opt_t) {
496                 open(TMP,">>$tmp");
497                 Pod::Text::pod2text($_,*TMP);
498                 close(TMP);
499         } elsif(not $opt_u) {
500                 my $cmd = "pod2man --lax $_ | nroff -man";
501                 $cmd .= " | col -x" if $^O =~ /hpux/;
502                 my $rslt = `$cmd`;
503                 unless(($err = $?)) {
504                         open(TMP,">>$tmp");
505                         print TMP $rslt;
506                         close TMP;
507                 }
508         }
509                                                         
510         if( $opt_u or $err or -z $tmp) {
511                 open(OUT,">>$tmp");
512                 open(IN,"<$_");
513                 my $cut = 1;
514                 while (<IN>) {
515                         $cut = $1 eq 'cut' if /^=(\w+)/;
516                         next if $cut;
517                         print OUT;
518                 }
519                 close(IN);
520                 close(OUT);
521         }
522 }
523
524 if( $no_tty ) {
525         open(TMP,"<$tmp");
526         print while <TMP>;
527         close(TMP);
528 } else {
529         foreach my $pager (@pagers) {
530                 system("$pager $tmp") or last;
531         }
532 }
533
534 1 while unlink($tmp); #Possibly pointless VMSism
535
536 exit 0;
537
538 __END__
539
540 =head1 NAME
541
542 perldoc - Look up Perl documentation in pod format.
543
544 =head1 SYNOPSIS
545
546 B<perldoc> [B<-h>] [B<-v>] [B<-t>] [B<-u>] [B<-m>] [B<-l>] [B<-F>]  [B<-X>] PageName|ModuleName|ProgramName
547
548 B<perldoc> B<-f> BuiltinFunction
549
550 B<perldoc> B<-q> FAQ Keyword
551
552 =head1 DESCRIPTION
553
554 I<perldoc> looks up a piece of documentation in .pod format that is embedded
555 in the perl installation tree or in a perl script, and displays it via
556 C<pod2man | nroff -man | $PAGER>. (In addition, if running under HP-UX,
557 C<col -x> will be used.) This is primarily used for the documentation for
558 the perl library modules.
559
560 Your system may also have man pages installed for those modules, in
561 which case you can probably just use the man(1) command.
562
563 =head1 OPTIONS
564
565 =over 5
566
567 =item B<-h> help
568
569 Prints out a brief help message.
570
571 =item B<-v> verbose
572
573 Describes search for the item in detail.
574
575 =item B<-t> text output
576
577 Display docs using plain text converter, instead of nroff. This may be faster,
578 but it won't look as nice.
579
580 =item B<-u> unformatted
581
582 Find docs only; skip reformatting by pod2*
583
584 =item B<-m> module
585
586 Display the entire module: both code and unformatted pod documentation.
587 This may be useful if the docs don't explain a function in the detail
588 you need, and you'd like to inspect the code directly; perldoc will find
589 the file for you and simply hand it off for display.
590
591 =item B<-l> file name only
592
593 Display the file name of the module found.
594
595 =item B<-F> file names
596
597 Consider arguments as file names, no search in directories will be performed.
598
599 =item B<-f> perlfunc
600
601 The B<-f> option followed by the name of a perl built in function will
602 extract the documentation of this function from L<perlfunc>.
603
604 =item B<-q> perlfaq
605
606 The B<-q> option takes a regular expression as an argument.  It will search
607 the question headings in perlfaq[1-9] and print the entries matching
608 the regular expression.
609
610 =item B<-X> use an index if present
611
612 The B<-X> option looks for a entry whose basename matches the name given on the
613 command line in the file C<$Config{archlib}/pod.idx>.  The pod.idx file should
614 contain fully qualified filenames, one per line.
615
616 =item B<PageName|ModuleName|ProgramName>
617
618 The item you want to look up.  Nested modules (such as C<File::Basename>)
619 are specified either as C<File::Basename> or C<File/Basename>.  You may also
620 give a descriptive name of a page, such as C<perlfunc>. You make also give a
621 partial or wrong-case name, such as "basename" for "File::Basename", but
622 this will be slower, if there is more then one page with the same partial
623 name, you will only get the first one.
624
625 =back
626
627 =head1 ENVIRONMENT
628
629 Any switches in the C<PERLDOC> environment variable will be used before the 
630 command line arguments.  C<perldoc> also searches directories
631 specified by the C<PERL5LIB> (or C<PERLLIB> if C<PERL5LIB> is not
632 defined) and C<PATH> environment variables.
633 (The latter is so that embedded pods for executables, such as
634 C<perldoc> itself, are available.)  C<perldoc> will use, in order of
635 preference, the pager defined in C<PERLDOC_PAGER>, C<MANPAGER>, or
636 C<PAGER> before trying to find a pager on its own.  (C<MANPAGER> is not
637 used if C<perldoc> was told to display plain text or unformatted pod.)
638
639 =head1 AUTHOR
640
641 Kenneth Albanowski <kjahds@kjahds.com>
642
643 Minor updates by Andy Dougherty <doughera@lafcol.lafayette.edu>
644
645 =cut
646
647 #
648 # Version 1.14: Wed Jul 15 01:50:20 EST 1998
649 #       Robin Barker <rmb1@cise.npl.co.uk>
650 #       -strict, -w cleanups
651 # Version 1.13: Fri Feb 27 16:20:50 EST 1997
652 #       Gurusamy Sarathy <gsar@umich.edu>
653 #       -doc tweaks for -F and -X options
654 # Version 1.12: Sat Apr 12 22:41:09 EST 1997
655 #       Gurusamy Sarathy <gsar@umich.edu>
656 #       -various fixes for win32
657 # Version 1.11: Tue Dec 26 09:54:33 EST 1995
658 #       Kenneth Albanowski <kjahds@kjahds.com>
659 #   -added Charles Bailey's further VMS patches, and -u switch
660 #   -added -t switch, with pod2text support
661
662 # Version 1.10: Thu Nov  9 07:23:47 EST 1995
663 #               Kenneth Albanowski <kjahds@kjahds.com>
664 #       -added VMS support
665 #       -added better error recognition (on no found pages, just exit. On
666 #        missing nroff/pod2man, just display raw pod.)
667 #       -added recursive/case-insensitive matching (thanks, Andreas). This
668 #        slows things down a bit, unfortunately. Give a precise name, and
669 #        it'll run faster.
670 #
671 # Version 1.01: Tue May 30 14:47:34 EDT 1995
672 #               Andy Dougherty  <doughera@lafcol.lafayette.edu>
673 #   -added pod documentation.
674 #   -added PATH searching.
675 #   -added searching pod/ subdirectory (mainly to pick up perlfunc.pod
676 #    and friends.
677 #
678 #
679 # TODO:
680 #
681 #       Cache directories read during sloppy match
682 !NO!SUBS!
683
684 close OUT or die "Can't close $file: $!";
685 chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
686 exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';
687 chdir $origdir;