#!/usr/bin/perl -w use strict; use vars qw(%Found $Quiet %Lengths %MD5s); use File::Spec; use File::Find; use FindBin; use Text::Wrap; use Getopt::Long; use Digest::MD5 'md5'; no locale; # Assumption is that we're either already being run from the top level (*nix, # VMS), or have absolute paths in @INC (Win32, pod/Makefile) BEGIN { my $Top = File::Spec->catdir($FindBin::Bin, File::Spec->updir); chdir $Top or die "Can't chdir to $Top: $!"; require 'Porting/pod_lib.pl'; } die "$0: Usage: $0 [--quiet]\n" unless GetOptions (quiet => \$Quiet) && !@ARGV; my $state = get_pod_metadata(0, 'pod/perltoc.pod'); warn @{$state->{inconsistent}} if @{$state->{inconsistent}}; # Find all the modules my @modpods; find(sub { if (/\.p(od|m)$/) { my $file = $File::Find::name; return if $file =~ qr!/Pod/Functions.pm\z!; # Used only by pod itself return if $file =~ m!(?:^|/)t/!; return if $file =~ m!lib/Attribute/Handlers/demo/!; return if $file =~ m!lib/Net/FTP/.+\.pm!; # Hi, Graham! :-) return if $file =~ m!lib/Math/BigInt/t/!; return if $file =~ m!/Devel/PPPort/[Hh]arness|lib/Devel/Harness!i; return if $file =~ m!XS/(?:APItest|Typemap)!; my $pod = $file; return if $pod =~ s/pm$/pod/ && -e $pod; unless (open my $f, '<', $_) { warn "$0: bogus <$file>: $!"; system "ls", "-l", $file; } else { my $line; while ($line = <$f>) { if ($line =~ /^=head1\s+NAME\b/) { push @modpods, $file; return; } } warn "$0: NOTE: cannot find '=head1 NAME' in:\n $file\n" unless $Quiet; } } }, 'lib'); my_die "Can't find any pods!\n" unless @modpods; my %done; for (@modpods) { my $name = $_; $name =~ s/\.p(m|od)$//; $name =~ s-\Alib/--; $name =~ s-/-::-g; next if $done{$name}++; $Found{$name =~ /^[a-z]/ ? 'PRAGMA' : 'MODULE'}{$name} = $_; } # Accumulating everything into a lexical before writing to disk dates from the # time when this script also provided the functionality of regen/pod_rules.pl # and this code was in a subroutine do_toc(). In turn, the use of a file scoped # lexical instead of a parameter or return value is because the code dates back # further still, and used *only* to create pod/perltoc.pod by printing direct my $OUT; ($_= <<"EOPOD2B") =~ s/^\t//gm and $OUT .= $_; # !!!!!!! DO NOT EDIT THIS FILE !!!!!!! # This file is autogenerated by buildtoc from all the other pods. # Edit those files and run $0 to effect changes. =head1 NAME perltoc - perl documentation table of contents =head1 DESCRIPTION This page provides a brief table of contents for the rest of the Perl documentation set. It is meant to be scanned quickly or grepped through to locate the proper section you're looking for. =head1 BASIC DOCUMENTATION EOPOD2B # All the things in the master list that happen to be pod filenames foreach (grep {defined $_ && @$_ == 5 && !$_->[0]{toc_omit}} @{$state->{master}}) { podset($_->[4], $_->[2], $_->[1] ne $_->[4]); } foreach my $type (qw(PRAGMA MODULE)) { ($_= <<"EOPOD2B") =~ s/^\t//gm and $OUT .= $_; =head1 $type DOCUMENTATION EOPOD2B foreach my $name (sort keys %{$Found{$type}}) { podset($name, $Found{$type}{$name}); } } $_= <<"EOPOD2B"; =head1 AUXILIARY DOCUMENTATION Here should be listed all the extra programs' documentation, but they don't all have manual pages yet: =over 4 EOPOD2B $_ .= join "\n", map {"\t=item $_\n"} sort keys %{$state->{aux}}; $_ .= <<"EOPOD2B" ; =back =head1 AUTHOR Larry Wall >, with the help of oodles of other folks. EOPOD2B s/^\t//gm; $OUT .= "$_\n"; $OUT =~ s/\n\s+\n/\n\n/gs; $OUT =~ s/\n{3,}/\n\n/g; $OUT =~ s/([^\n]+)/wrap('', '', $1)/ge; write_or_die('pod/perltoc.pod', $OUT); exit(0); # Below are all the auxiliary routines for generating perltoc.pod my ($inhead1, $inhead2, $initem); sub podset { my ($pod, $file, $possibly_duplicated) = @_; local $/ = ''; open my $fh, '<', $file or my_die "Can't open file '$file' for $pod: $!"; if ($possibly_duplicated) { # We are a dual-life perl*.pod file, which will have be copied to lib/ # by the build process, and hence also found there. ++$Lengths{-s $file}; ++$MD5s{md5(slurp_or_die($file))}; } elsif (!defined $possibly_duplicated) { # We are a file in lib. Are we a duplicate? # Don't bother calculating the MD5 if there's no intersting file of this # length. return if $Lengths{-s $file} && $MD5s{md5(slurp_or_die($file))}; } while(<$fh>) { tr/\015//d; if (s/^=head1 (NAME)\s*/=head2 /) { unhead1(); $OUT .= "\n\n=head2 "; $_ = <$fh>; # Remove svn keyword expansions from the Perl FAQ s/ \(\$Revision: \d+ \$\)//g; if ( /^\s*\Q$pod\E\b/ ) { s/$pod\.pm/$pod/; # '.pm' in NAME !? } else { s/^/$pod, /; } } elsif (s/^=head1 (.*)/=item $1/) { unhead2(); $OUT .= "=over 4\n\n" unless $inhead1; $inhead1 = 1; $_ .= "\n"; } elsif (s/^=head2 (.*)/=item $1/) { unitem(); $OUT .= "=over 4\n\n" unless $inhead2; $inhead2 = 1; $_ .= "\n"; } elsif (s/^=item ([^=].*)/$1/) { next if $pod eq 'perldiag'; s/^\s*\*\s*$// && next; s/^\s*\*\s*//; s/\n/ /g; s/\s+$//; next if /^[\d.]+$/; next if $pod eq 'perlmodlib' && /^ftp:/; $OUT .= ", " if $initem; $initem = 1; s/\.$//; s/^-X\b/-I/; } else { unhead1() if /^=cut\s*\n/; next; } $OUT .= $_; } } sub unhead1 { unhead2(); if ($inhead1) { $OUT .= "\n\n=back\n\n"; } $inhead1 = 0; } sub unhead2 { unitem(); if ($inhead2) { $OUT .= "\n\n=back\n\n"; } $inhead2 = 0; } sub unitem { if ($initem) { $OUT .= "\n\n"; } $initem = 0; } # Code added in commit 416302502f485afa, but never used. # Probably roffitall should become something that buildtoc generates, instead # of something that we ship in the distribution. sub generate_roffitall { (map ({"\t\$maindir/$_.1\t\\"}sort keys %{$state->{pods}}), "\t\t\\", map ({"\t\$maindir/$_.1\t\\"}sort keys %{$state->{aux}}), "\t\t\\", map ({"\t\$libdir/$_.3\t\\"}sort keys %{$Found{PRAGMA}}), "\t\t\\", map ({"\t\$libdir/$_.3\t\\"}sort keys %{$Found{MODULE}}), ) }