Upgrade to Math::BigInt 1.66.
[p5sagit/p5-mst-13.2.git] / pod / buildtoc
CommitLineData
41630250 1#!/usr/bin/perl -w
2
3use strict;
4use vars qw($masterpodfile %Build %Targets $Verbose $Up %Ignore
5 @Master %Readmes %Pods %Aux %Readmepods %Pragmata %Modules);
6use File::Spec;
7use File::Find;
8use FindBin;
9use Text::Tabs;
10use Text::Wrap;
11use Getopt::Long;
12
13no locale;
14
15$Up = File::Spec->updir;
16$masterpodfile = File::Spec->catdir($Up, "pod.lst");
17
18# Generate any/all of these files
19# --verbose gives slightly more output
20# --build-all tries to build everything
21# --build-foo updates foo as follows
22# --showfiles shows the files to be changed
23
24%Targets
25 = (
26 toc => "perltoc.pod",
27 manifest => File::Spec->catdir($Up, "MANIFEST"),
28 perlpod => "perl.pod",
29 vms => File::Spec->catdir($Up, "vms", "descrip_mms.template"),
30 nmake => File::Spec->catdir($Up, "win32", "Makefile"),
31 dmake => File::Spec->catdir($Up, "win32", "makefile.mk"),
32 podmak => File::Spec->catdir($Up, "win32", "pod.mak"),
33 # plan9 => File::Spec->catdir($Up, "plan9", "mkfile"),
34 );
35
36{
37 my @files = keys %Targets;
38 my $filesopts = join(" | ", map { "--build-$_" } "all", sort @files);
39 my $showfiles;
40 die <<__USAGE__
41$0: Usage: $0 [--verbose] [--showfiles] $filesopts
42__USAGE__
43 unless @ARGV
44 && GetOptions (verbose => \$Verbose,
45 showfiles => \$showfiles,
46 map {+"build-$_", \$Build{$_}} @files, 'all');
47 # Set them all to true
48 @Build{@files} = @files if ($Build{all});
49 if ($showfiles) {
50 print
51 join(" ",
52 sort { lc $a cmp lc $b }
53 map {
54 my ($v, $d, $f) = File::Spec->splitpath($_);
55 my @d;
56 @d = defined $d ? File::Spec->splitdir($d) : ();
57 shift @d if @d;
58 File::Spec->catfile(@d ?
59 (@d == 1 && $d[0] eq '' ? () : @d)
60 : "pod", $f);
61 } @Targets{grep { $_ ne 'all' && $Build{$_} } keys %Build}),
62 "\n";
63 exit(0);
64 }
65}
66
67# Don't copy these top level READMEs
68%Ignore
69 = (
70 Y2K => 1,
71 micro => 1,
72# vms => 1,
73 );
74
75if ($Verbose) {
76 print "I'm building $_\n" foreach grep {$Build{$_}} keys %Build;
77}
78
79chdir $FindBin::Bin or die "$0: Can't chdir $FindBin::Bin: $!";
80
81open MASTER, $masterpodfile or die "$0: Can't open $masterpodfile: $!";
82
83foreach (<MASTER>) {
84 next if /^\#/;
85
86 # At least one upper case letter somewhere in the first group
87 if (/^(\S+)\s(.*)/ && $1 =~ tr/A-Z//) {
88 # it's a heading
89 my $flags = $1;
90 my %flags = (header => 1);
91 $flags{toc_omit} = 1 if $flags =~ tr/O//d;
92 $flags{include} = 1 if $flags =~ tr/I//d;
93 $flags{aux} = 1 if $flags =~ tr/A//d;
94 die "$0: Unknown flag found in heading line: $_" if length $flags;
95 push @Master, [\%flags, $2];
96
97 } elsif (/^(\S*)\s+(\S+)\s+(.*)/) {
98 # it's a section
99 my ($flags, $filename, $desc) = ($1, $2, $3);
100
101 my %flags = (indent => 0);
102 $flags{indent} = $1 if $flags =~ s/(\d+)//;
103 $flags{toc_omit} = 1 if $flags =~ tr/o//d;
104 $flags{aux} = 1 if $flags =~ tr/a//d;
105 if ($flags =~ tr/r//d) {
106 my $readme = $filename;
107 $readme =~ s/^perl//;
108 $Readmepods{$filename} = $Readmes{$readme} = $desc;
109 $flags{readme} = 1;
110 } elsif ($flags{aux}) {
111 $Aux{$filename} = $desc;
112 } else {
113 $Pods{$filename} = $desc;
114 }
115 die "$0: Unknown flag found in section line: $_" if length $flags;
116 push @Master, [\%flags, $filename, $desc];
117 } elsif (/^$/) {
118 push @Master, undef;
119 } else {
120 die "$0: Malformed line: $_" if $1 =~ tr/A-Z//;
121 }
122}
123
124close MASTER;
125
126# Sanity cross check
127{
128 my (%disk_pods, @disk_pods);
129 my (@manipods, %manipods);
130 my (@manireadmes, %manireadmes);
131 my (@perlpods, %perlpods);
132 my (%our_pods);
133
134 # Convert these to a list of filenames.
135 foreach (keys %Pods, keys %Readmepods) {
136 $our_pods{"$_.pod"}++;
137 }
138
139 # None of these filenames will be boolean false
140 @disk_pods = glob("*.pod");
141 @disk_pods{@disk_pods} = @disk_pods;
142
143 open(MANI, "../MANIFEST") || die "$0: opening ../MANIFEST failed: $!";
144 while (<MANI>) {
145 if (m!^pod/([^.]+\.pod)\s+!i) {
146 push @manipods, $1;
147 } elsif (m!^README\.(\S+)\s+!i) {
148 next if $Ignore{$1};
149 push @manireadmes, "perl$1.pod";
150 }
151 }
152 close(MANI);
153 @manipods{@manipods} = @manipods;
154 @manireadmes{@manireadmes} = @manireadmes;
155
156 open(PERLPOD, "perl.pod") || die "$0: opening perl.pod failed: $!\n";
157 while (<PERLPOD>) {
158 if (/^For ease of access, /../^\(If you're intending /) {
159 if (/^\s+(perl\S*)\s+\w/) {
160 push @perlpods, "$1.pod";
161 }
162 }
163 }
164 close(PERLPOD);
165 die "$0: could not find the pod listing of perl.pod\n"
166 unless @perlpods;
167 @perlpods{@perlpods} = @perlpods;
168
169 foreach my $i (sort keys %disk_pods) {
170 warn "$0: $i exists but is unknown by buildtoc\n"
171 unless $our_pods{$i};
172 warn "$0: $i exists but is unknown by ../MANIFEST\n"
173 if !$manipods{$i} && !$manireadmes{$i};
174 warn "$0: $i exists but is unknown by perl.pod\n"
175 unless $perlpods{$i};
176 }
177 foreach my $i (sort keys %our_pods) {
178 warn "$0: $i is known by buildtoc but does not exist\n"
179 unless $disk_pods{$i};
180 }
181 foreach my $i (sort keys %manipods) {
182 warn "$0: $i is known by ../MANIFEST but does not exist\n"
183 unless $disk_pods{$i};
184 }
185 foreach my $i (sort keys %perlpods) {
186 warn "$0: $i is known by perl.pod but does not exist\n"
187 unless $disk_pods{$i};
188 }
189}
190
191# Find all the mdoules
192{
193 my @modpods;
194 find \&getpods => qw(../lib ../ext);
195
196 sub getpods {
197 if (/\.p(od|m)$/) {
198 my $file = $File::Find::name;
199 return if $file eq '../lib/Pod/Functions.pm'; # Used only by pod itself
200 return if $file =~ m!(?:^|/)t/!;
201 return if $file =~ m!lib/Attribute/Handlers/demo/!;
202 return if $file =~ m!lib/Net/FTP/.+\.pm!; # Hi, Graham! :-)
203 return if $file =~ m!lib/Math/BigInt/t/!;
204 return if $file =~ m!/Devel/PPPort/[Hh]arness|lib/Devel/Harness!i;
205 return if $file =~ m!XS/(?:APItest|Typemap)!;
206 die "$0: tut $File::Find::name" if $file =~ /TUT/;
207 unless (open (F, "< $_\0")) {
208 warn "$0: bogus <$file>: $!";
209 system "ls", "-l", $file;
210 }
211 else {
212 my $line;
213 while ($line = <F>) {
214 if ($line =~ /^=head1\s+NAME\b/) {
215 push @modpods, $file;
216 #warn "GOOD $file\n";
217 return;
218 }
219 }
220 warn "$0: $file: cannot find =head1 NAME\n";
221 }
222 }
223 }
224
225 die "$0: no pods" unless @modpods;
226
227 my %done;
228 for (@modpods) {
229 #($name) = /(\w+)\.p(m|od)$/;
230 my $name = path2modname($_);
231 if ($name =~ /^[a-z]/) {
232 $Pragmata{$name} = $_;
233 } else {
234 if ($done{$name}++) {
235 # warn "already did $_\n";
236 next;
237 }
238 $Modules{$name} = $_;
239 }
240 }
241}
242
243# OK. Now a lot of ancillay function definitions follow
244# Main program returns at "Do stuff"
245
246sub path2modname {
247 local $_ = shift;
248 s/\.p(m|od)$//;
249 s-.*?/(lib|ext)/--;
250 s-/-::-g;
251 s/(\w+)::\1/$1/;
252 return $_;
253}
254
255sub output ($);
256
257sub output_perltoc {
258 open(OUT, ">perltoc.pod") || die "$0: creating perltoc.pod failed: $!";
259
260 $/ = '';
261
262 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
263
97f32038 264 # !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
265 # This file is autogenerated by buildtoc from all the other pods.
266 # Edit those files and run buildtoc --build-toc to effect changes.
97f32038 267
41630250 268 =head1 NAME
269
270 perltoc - perl documentation table of contents
271
272 =head1 DESCRIPTION
273
274 This page provides a brief table of contents for the rest of the Perl
275 documentation set. It is meant to be scanned quickly or grepped
276 through to locate the proper section you're looking for.
277
278 =head1 BASIC DOCUMENTATION
279
280EOPOD2B
281#' make emacs happy
282
283 # All the things in the master list that happen to be pod filenames
284 podset(map {"$_->[1].pod"} grep {defined $_ && @$_ == 3 && !$_->[0]{toc_omit}} @Master);
285
286
287 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
288
289
290
291 =head1 PRAGMA DOCUMENTATION
292
293EOPOD2B
294
295 podset(sort values %Pragmata);
296
297 ($_= <<"EOPOD2B") =~ s/^\t//gm && output($_);
298
299
300
301 =head1 MODULE DOCUMENTATION
302
303EOPOD2B
304
305 podset( @Modules{ sort keys %Modules } );
306
307 $_= <<"EOPOD2B";
308
309
310 =head1 AUXILIARY DOCUMENTATION
311
312 Here should be listed all the extra programs' documentation, but they
313 don't all have manual pages yet:
314
315 =over 4
316
317EOPOD2B
318
319 $_ .= join "\n", map {"\t=item $_\n"} sort keys %Aux;
320 $_ .= <<"EOPOD2B" ;
321
322 =back
323
324 =head1 AUTHOR
325
326 Larry Wall <F<larry\@wall.org>>, with the help of oodles
327 of other folks.
328
329
330EOPOD2B
331
332 s/^\t//gm;
333 output $_;
334 output "\n"; # flush $LINE
335}
336
337# Below are all the auxiliary routines for generating perltoc.pod
338
339my ($inhead1, $inhead2, $initem);
340
341sub podset {
342 local @ARGV = @_;
343 my $pod;
344
345 while(<>) {
16114dde 346 tr/\015//d;
41630250 347 if (s/^=head1 (NAME)\s*/=head2 /) {
348 $pod = path2modname($ARGV);
349 unhead1();
350 output "\n \n\n=head2 ";
351 $_ = <>;
352 if ( /^\s*$pod\b/ ) {
353 s/$pod\.pm/$pod/; # '.pm' in NAME !?
354 output $_;
355 } else {
356 s/^/$pod, /;
357 output $_;
358 }
359 next;
360 }
361 if (s/^=head1 (.*)/=item $1/) {
362 unhead2();
363 output "=over 4\n\n" unless $inhead1;
364 $inhead1 = 1;
365 output $_; nl(); next;
366 }
367 if (s/^=head2 (.*)/=item $1/) {
368 unitem();
369 output "=over 4\n\n" unless $inhead2;
370 $inhead2 = 1;
371 output $_; nl(); next;
372 }
373 if (s/^=item ([^=].*)/$1/) {
374 next if $pod eq 'perldiag';
375 s/^\s*\*\s*$// && next;
376 s/^\s*\*\s*//;
377 s/\n/ /g;
378 s/\s+$//;
379 next if /^[\d.]+$/;
380 next if $pod eq 'perlmodlib' && /^ftp:/;
381 ##print "=over 4\n\n" unless $initem;
382 output ", " if $initem;
383 $initem = 1;
384 s/\.$//;
385 s/^-X\b/-I<X>/;
386 output $_; next;
387 }
388 if (s/^=cut\s*\n//) {
389 unhead1();
390 next;
391 }
392 }
393}
394
395sub unhead1 {
396 unhead2();
397 if ($inhead1) {
398 output "\n\n=back\n\n";
399 }
400 $inhead1 = 0;
401}
402
403sub unhead2 {
404 unitem();
405 if ($inhead2) {
406 output "\n\n=back\n\n";
407 }
408 $inhead2 = 0;
409}
410
411sub unitem {
412 if ($initem) {
413 output "\n\n";
414 ##print "\n\n=back\n\n";
415 }
416 $initem = 0;
417}
418
419sub nl {
420 output "\n";
421}
422
423my $NEWLINE = 0; # how many newlines have we seen recently
424my $LINE; # what remains to be printed
425
426sub output ($) {
427 for (split /(\n)/, shift) {
428 if ($_ eq "\n") {
429 if ($LINE) {
430 print OUT wrap('', '', $LINE);
431 $LINE = '';
432 }
433 if (($NEWLINE) < 2) {
434 print OUT;
435 $NEWLINE++;
436 }
437 }
438 elsif (/\S/ && length) {
439 $LINE .= $_;
440 $NEWLINE = 0;
441 }
442 }
443}
444
445# End of original buildtoc. From here on are routines to generate new sections
446# for and inplace edit other files
447
448sub generate_perlpod {
449 my @output;
450 my $maxlength = 0;
451 foreach (@Master) {
452 my $flags = $_->[0];
453 next if $flags->{aux};
454
455 if (@$_ == 2) {
456 # Heading
457 push @output, "=head2 $_->[1]\n";
458 } elsif (@$_ == 3) {
459 # Section
460 my $start = " " x (4 + $flags->{indent}) . $_->[1];
461 $maxlength = length $start if length ($start) > $maxlength;
462 push @output, [$start, $_->[2]];
463 } elsif (@$_ == 0) {
464 # blank line
465 push @output, "\n";
466 } else {
467 die "$0: Illegal length " . scalar @$_;
468 }
469 }
470 # want at least 2 spaces padding
471 $maxlength += 2;
472 $maxlength = ($maxlength + 3) & ~3;
473 # sprintf gives $1.....$2 where ... are spaces:
474 return unexpand (map {ref $_ ? sprintf "%-${maxlength}s%s\n", @$_ : $_}
475 @output);
476}
477
478
479sub generate_manifest {
480 # Annyoingly unexpand doesn't consider it good form to replace a single
481 # space before a tab with a tab
482 # Annoyingly (2) it returns read only values.
483 my @temp = unexpand (map {sprintf "%-32s%s\n", @$_} @_);
484 map {s/ \t/\t\t/g; $_} @temp;
485}
486sub generate_manifest_pod {
487 generate_manifest map {["pod/$_.pod", $Pods{$_}]} sort keys %Pods;
488}
489sub generate_manifest_readme {
490 generate_manifest map {["README.$_", $Readmes{$_}]} sort keys %Readmes;
491}
492
493sub generate_roffitall {
494 (map ({"\t\$maindir/$_.1\t\\"}sort keys %Pods),
495 "\t\t\\",
496 map ({"\t\$maindir/$_.1\t\\"}sort keys %Aux),
497 "\t\t\\",
498 map ({"\t\$libdir/$_.3\t\\"}sort keys %Pragmata),
499 "\t\t\\",
500 map ({"\t\$libdir/$_.3\t\\"}sort keys %Modules),
501 )
502}
503
504sub generate_descrip_mms_1 {
505 local $Text::Wrap::columns = 150;
506 my $count = 0;
507 my @lines = map {"pod" . $count++ . " = $_"}
508 split /\n/, wrap('', '', join " ", map "[.lib.pod]$_.pod",
509 sort keys %Pods, keys %Readmepods);
510 @lines, "pod = " . join ' ', map {"\$(pod$_)"} 0 .. $count - 1;
511}
512
513sub generate_descrip_mms_2 {
ab1db26f 514 map {sprintf <<'SNIP', $_, $_ eq 'perlvms' ? 'vms' : 'pod', $_}
515[.lib.pod]%s.pod : [.%s]%s.pod
41630250 516 @ If F$Search("[.lib]pod.dir").eqs."" Then Create/Directory [.lib.pod]
517 Copy/NoConfirm/Log $(MMS$SOURCE) [.lib.pod]
518SNIP
519 sort keys %Pods, keys %Readmepods;
520}
521
522sub generate_nmake_1 {
523 map {sprintf "\tcopy ..\\README.%-8s .\\perl$_.pod\n", $_}
524 sort keys %Readmes;
525}
526
527# This doesn't have a trailing newline
528sub generate_nmake_2 {
529 # Spot the special case
530 local $Text::Wrap::columns = 76;
531 my $line = wrap ("\t ", "\t ",
532 join " ", sort map {"perl$_.pod"} "vms", keys %Readmes);
533 $line =~ s/$/ \\/mg;
534 $line;
535}
536
537sub generate_pod_mak {
538 my $variable = shift;
539 my @lines;
540 my $line = join "\\\n", "\U$variable = ",
541 map {"\t$_.$variable\t"} sort keys %Pods;
542 # Special case
543 $line =~ s/.*perltoc.html.*\n//m;
544 $line;
545}
546
547sub do_manifest {
548 my $name = shift;
549 my @manifest =
550 grep {! m!^pod/[^.]+\.pod.*\n!}
551 grep {! m!^README\.(\S+)! || $Ignore{$1}} @_;
552 # Dictionary order - fold and handle non-word chars as nothing
553 map { $_->[0] }
554 sort { $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] }
6578b326 555 map { my $f = lc $_; $f =~ s/[^a-z0-9\s]//g; [ $_, $f ] }
41630250 556 @manifest,
557 &generate_manifest_pod(),
558 &generate_manifest_readme();
559}
560
561sub do_nmake {
562 my $name = shift;
563 my $makefile = join '', @_;
564 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
565 $makefile =~ s/^\tcopy \.\.\\README.*\n/\0/gm;
566 my $sections = () = $makefile =~ m/\0+/g;
567 die "$0: $name contains no README copies" if $sections < 1;
568 die "$0: $name contains discontiguous README copies" if $sections > 1;
569 $makefile =~ s/\0+/join "", &generate_nmake_1/se;
570
571 $makefile =~ s{(cd \$\(PODDIR\) && del /f [^\n]+).*?(pod2html)}
572 {"$1\n" . &generate_nmake_2."\n\t $2"}se;
573 $makefile;
574}
575
576# shut up used only once warning
577*do_dmake = *do_dmake = \&do_nmake;
578
579sub do_perlpod {
580 my $name = shift;
581 my $pod = join '', @_;
582
583 unless ($pod =~ s{(For\ ease\ of\ access,\ .*\n)
584 (?:\s+[a-z]{4,}.*\n # fooo
585 |=head.*\n # =head foo
586 |\s*\n # blank line
587 )+
588 }
589 {$1 . join "", &generate_perlpod}mxe) {
590 die "$0: Failed to insert ammendments in do_perlpod";
591 }
592 $pod;
593}
594
595sub do_podmak {
596 my $name = shift;
597 my $body = join '', @_;
598 foreach my $variable qw(pod man html tex) {
599 die "$0: could not find $variable in $name"
600 unless $body =~ s{\n\U$variable\E = (?:[^\n]*\\\n)*[^\n]*}
601 {"\n" . generate_pod_mak ($variable)}se;
602 }
603 $body;
604}
605
606sub do_vms {
607 my $name = shift;
608 my $makefile = join '', @_;
609 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
610 $makefile =~ s/\npod\d* =[^\n]*/\0/gs;
611 my $sections = () = $makefile =~ m/\0+/g;
612 die "$0: $name contains no pod assignments" if $sections < 1;
613 die "$0: $name contains $sections discontigous pod assignments"
614 if $sections > 1;
615 $makefile =~ s/\0+/join "\n", '', &generate_descrip_mms_1/se;
616
617 die "$0: $name contains NUL bytes" if $makefile =~ /\0/;
618
619# Looking for rules like this
620# [.lib.pod]perl.pod : [.pod]perl.pod
621# @ If F$Search("[.lib]pod.dir").eqs."" Then Create/Directory [.lib.pod]
622# Copy/NoConfirm/Log $(MMS$SOURCE) [.lib.pod]
623
624 $makefile =~ s/\n\Q[.lib.pod]\Eperl[^\n\.]*\.pod[^\n]+\n
625 [^\n]+\n # Another line
626 [^\n]+\Q[.lib.pod]\E\n # ends [.lib.pod]
627 /\0/gsx;
628 $sections = () = $makefile =~ m/\0+/g;
629 die "$0: $name contains no copy rules" if $sections < 1;
630 die "$0: $name contains $sections discontigous copy rules"
631 if $sections > 1;
632 $makefile =~ s/\0+/join "\n", '', &generate_descrip_mms_2/se;
633 $makefile;
634}
635
636# Do stuff
637
638my $built;
639while (my ($target, $name) = each %Targets) {
640 next unless $Build{$target};
641 $built++;
642 if ($target eq "toc") {
643 &output_perltoc;
644 next;
645 }
646 print "Now processing $name\n" if $Verbose;
647 open THING, $name or die "Can't open $name: $!";
648 my @orig = <THING>;
649 my $orig = join '', @orig;
650 close THING;
651 my @new = do {
652 no strict 'refs';
653 &{"do_$target"}($target, @orig);
654 };
655 my $new = join '', @new;
656 if ($new eq $orig) {
657 print "Was not modified\n" if $Verbose;
658 next;
659 }
660 rename $name, "$name.old" or die "$0: Can't rename $name to $name.old: $!";
661 open THING, ">$name" or die "$0: Can't open $name for writing: $!";
662 print THING $new or die "$0: print to $name failed: $!";
663 close THING or die die "$0: close $name failed: $!";
664}
665
666warn "$0: was not instructed to build anything\n" unless $built;