perl/installhtml
<<
>>
Prefs
   1#!./perl -Ilib -w
   2
   3# This file should really be extracted from a .PL file
   4
   5use strict;
   6use Config;             # for config options in the makefile
   7use File::Spec;
   8use Getopt::Long;       # for command-line parsing
   9use Cwd;
  10use Pod::Html 'anchorify';
  11
  12=head1 NAME
  13
  14installhtml - converts a collection of POD pages to HTML format.
  15
  16=head1 SYNOPSIS
  17
  18    installhtml  [--help] [--podpath=<name>:...:<name>] [--podroot=<name>]
  19         [--htmldir=<name>] [--htmlroot=<name>]  [--norecurse] [--recurse]
  20         [--splithead=<name>,...,<name>]   [--splititem=<name>,...,<name>]
  21         [--libpods=<name>,...,<name>]        [--ignore=<name>,...,<name>]
  22         [--verbose]
  23
  24=head1 DESCRIPTION
  25
  26I<installhtml> converts a collection of POD pages to a corresponding
  27collection of HTML pages.  This is primarily used to convert the pod
  28pages found in the perl distribution.
  29
  30=head1 OPTIONS
  31
  32=over 4
  33
  34=item B<--help> help
  35
  36Displays the usage.
  37
  38=item B<--podroot> POD search path base directory
  39
  40The base directory to search for all .pod and .pm files to be converted.
  41Default is current directory.
  42
  43=item B<--podpath> POD search path
  44
  45The list of directories to search for .pod and .pm files to be converted.
  46Default is 'podroot/.'.
  47
  48=item B<--recurse> recurse on subdirectories
  49
  50Whether or not to convert all .pm and .pod files found in subdirectories
  51too.  Default is to not recurse.
  52
  53=item B<--htmldir> HTML destination directory
  54
  55The base directory which all HTML files will be written to.  This should
  56be a path relative to the filesystem, not the resulting URL.
  57
  58=item B<--htmlroot> URL base directory
  59
  60The base directory which all resulting HTML files will be visible at in
  61a URL.  The default is '/'.
  62
  63=item B<--splithead> POD files to split on =head directive
  64
  65Comma-separated list of pod files to split by the =head directive.  The
  66.pod suffix is optional. These files should have names specified
  67relative to podroot.
  68
  69=item B<--splititem> POD files to split on =item directive
  70
  71Comma-separated list of all pod files to split by the =item directive.
  72The .pod suffix is optional.  I<installhtml> does not do the actual
  73split, rather it invokes I<splitpod> to do the dirty work.  As with
  74--splithead, these files should have names specified relative to podroot.
  75
  76=item B<--splitpod> Directory containing the splitpod program
  77
  78The directory containing the splitpod program. The default is 'podroot/pod'.
  79
  80=item B<--libpods> library PODs for LE<lt>E<gt> links
  81
  82Comma-separated list of "library" pod files.  This is the same list that
  83will be passed to pod2html when any pod is converted.
  84
  85=item B<--ignore> files to be ignored
  86
  87Comma-separated of files that shouldn't be installed, given relative
  88to podroot.
  89
  90=item B<--verbose> verbose output
  91
  92Self-explanatory.
  93
  94=back
  95
  96=head1 EXAMPLE
  97
  98The following command-line is an example of the one we use to convert
  99perl documentation:
 100
 101    ./installhtml --podpath=lib:ext:pod:vms   \
 102                        --podroot=/usr/src/perl     \
 103                        --htmldir=/perl/nmanual     \
 104                        --htmlroot=/perl/nmanual    \
 105                        --splithead=pod/perlipc     \
 106                        --splititem=pod/perlfunc    \
 107                        --libpods=perlfunc,perlguts,perlvar,perlrun,perlop \
 108                        --recurse \
 109                        --verbose
 110
 111=head1 AUTHOR
 112
 113Chris Hall E<lt>hallc@cs.colorado.eduE<gt>
 114
 115=cut
 116
 117my $usage;
 118
 119$usage =<<END_OF_USAGE;
 120Usage: $0 --help --podpath=<name>:...:<name> --podroot=<name>
 121         --htmldir=<name> --htmlroot=<name> --norecurse --recurse
 122         --splithead=<name>,...,<name> --splititem=<name>,...,<name>
 123         --libpods=<name>,...,<name> --ignore=<name>,...,<name> --verbose
 124
 125    --help      - this message
 126    --podpath   - colon-separated list of directories containing .pod and
 127                  .pm files to be converted (. by default).
 128    --podroot   - filesystem base directory from which all relative paths in
 129                  podpath stem (default is .).
 130    --htmldir   - directory to store resulting html files in relative
 131                  to the filesystem (\$podroot/html by default).
 132    --htmlroot  - http-server base directory from which all relative paths
 133                  in podpath stem (default is /).
 134    --libpods   - comma-separated list of files to search for =item pod
 135                  directives in as targets of C<> and implicit links (empty
 136                  by default).
 137    --norecurse - don't recurse on those subdirectories listed in podpath.
 138                  (default behavior).
 139    --recurse   - recurse on those subdirectories listed in podpath
 140    --splithead - comma-separated list of .pod or .pm files to split.  will
 141                  split each file into several smaller files at every occurrence
 142                  of a pod =head[1-6] directive.
 143    --splititem - comma-separated list of .pod or .pm files to split using
 144                  splitpod.
 145    --splitpod  - directory where the program splitpod can be found
 146                  (\$podroot/pod by default).
 147    --ignore    - comma-separated list of files that shouldn't be installed.
 148    --verbose   - self-explanatory.
 149
 150END_OF_USAGE
 151
 152my (@libpods, @podpath, $podroot, $htmldir, $htmlroot, $recurse, @splithead,
 153    @splititem, $splitpod, $verbose, $pod2html, @ignore);
 154
 155@libpods = ();
 156@podpath = ( "." );     # colon-separated list of directories containing .pod
 157                        # and .pm files to be converted.
 158$podroot = ".";         # assume the pods we want are here
 159$htmldir = "";          # nothing for now...
 160$htmlroot = "/";        # default value
 161$recurse = 0;           # default behavior
 162@splithead = ();        # don't split any files by default
 163@splititem = ();        # don't split any files by default
 164$splitpod = "";         # nothing for now.
 165
 166$verbose = 0;           # whether or not to print debugging info
 167
 168$pod2html = "pod/pod2html";
 169
 170usage("") unless @ARGV;
 171
 172# Overcome shell's p1,..,p8 limitation.  
 173# See vms/descrip_mms.template -> descrip.mms for invokation.
 174if ( $^O eq 'VMS' ) { @ARGV = split(/\s+/,$ARGV[0]); }
 175
 176use vars qw( %Options );
 177
 178# parse the command-line
 179my $result = GetOptions( \%Options, qw(
 180        help
 181        podpath=s
 182        podroot=s
 183        htmldir=s
 184        htmlroot=s
 185        libpods=s
 186        ignore=s
 187        recurse!
 188        splithead=s
 189        splititem=s
 190        splitpod=s
 191        verbose
 192));
 193usage("invalid parameters") unless $result;
 194parse_command_line();
 195
 196
 197# set these variables to appropriate values if the user didn't specify
 198#  values for them.
 199$htmldir = "$htmlroot/html" unless $htmldir;
 200$splitpod = "$podroot/pod" unless $splitpod;
 201
 202
 203# make sure that the destination directory exists
 204(mkdir($htmldir, 0755) ||
 205        die "$0: cannot make directory $htmldir: $!\n") if ! -d $htmldir;
 206
 207
 208# the following array will eventually contain files that are to be
 209# ignored in the conversion process.  these are files that have been
 210# process by splititem or splithead and should not be converted as a
 211# result.
 212my @splitdirs;
 213
 214# split pods. It's important to do this before convert ANY pods because
 215# it may affect some of the links
 216@splitdirs = ();    # files in these directories won't get an index
 217split_on_head($podroot, $htmldir, \@splitdirs, \@ignore, @splithead);
 218split_on_item($podroot,           \@splitdirs, \@ignore, @splititem);
 219
 220
 221# convert the pod pages found in @poddirs
 222#warn "converting files\n" if $verbose;
 223#warn "\@ignore\t= @ignore\n" if $verbose;
 224foreach my $dir (@podpath) {
 225    installdir($dir, $recurse, $podroot, \@splitdirs, \@ignore);
 226}
 227
 228
 229# now go through and create master indices for each pod we split
 230foreach my $dir (@splititem) {
 231    print "creating index $htmldir/$dir.html\n" if $verbose;
 232    create_index("$htmldir/$dir.html", "$htmldir/$dir");
 233}
 234
 235foreach my $dir (@splithead) {
 236    (my $pod = $dir) =~ s,^.*/,,;
 237    $dir .= ".pod" unless $dir =~ /(\.pod|\.pm)$/;
 238    # let pod2html create the file
 239    runpod2html($dir, 1);
 240
 241    # now go through and truncate after the index
 242    $dir =~ /^(.*?)(\.pod|\.pm)?$/sm;
 243    my $file = "$htmldir/$1";
 244    print "creating index $file.html\n" if $verbose;
 245
 246    # read in everything until what would have been the first =head
 247    # directive, patching the index as we go.
 248    open(H, "<$file.html") ||
 249        die "$0: error opening $file.html for input: $!\n";
 250    $/ = "";
 251    my @data = ();
 252    while (<H>) {
 253        last if /name="name"/i;
 254        $_ =~ s{href="#(.*)">}{
 255            my $url = "$pod/$1.html" ;
 256            $url = Pod::Html::relativize_url( $url, "$file.html" )
 257            if ( ! defined $Options{htmlroot} || $Options{htmlroot} eq '' );
 258            "href=\"$url\">" ;
 259        }egi;
 260        push @data, $_;
 261    }
 262    close(H);
 263
 264    # now rewrite the file
 265    open(H, ">$file.html") ||
 266        die "$0: error opening $file.html for output: $!\n";
 267    print H "@data", "\n";
 268    close(H);
 269}
 270
 271##############################################################################
 272
 273
 274sub usage {
 275    warn "$0: @_\n" if @_;
 276    die $usage;
 277}
 278
 279
 280sub parse_command_line {
 281    usage() if defined $Options{help};
 282    $Options{help} = "";                    # make -w shut up
 283
 284    # list of directories
 285    @podpath   = split(":", $Options{podpath}) if defined $Options{podpath};
 286
 287    # lists of files
 288    @splithead = split(",", $Options{splithead}) if defined $Options{splithead};
 289    @splititem = split(",", $Options{splititem}) if defined $Options{splititem};
 290    @libpods   = split(",", $Options{libpods}) if defined $Options{libpods};
 291
 292    $htmldir  = $Options{htmldir}           if defined $Options{htmldir};
 293    $htmlroot = $Options{htmlroot}          if defined $Options{htmlroot};
 294    $podroot  = $Options{podroot}           if defined $Options{podroot};
 295    $splitpod = $Options{splitpod}          if defined $Options{splitpod};
 296
 297    $recurse  = $Options{recurse}           if defined $Options{recurse};
 298    $verbose  = $Options{verbose}           if defined $Options{verbose};
 299
 300    @ignore = map "$podroot/$_", split(",", $Options{ignore}) if defined $Options{ignore};
 301}
 302
 303
 304sub create_index {
 305    my($html, $dir) = @_;
 306    (my $pod = $dir) =~ s,^.*/,,;
 307    my(@files, @filedata, @index, $file);
 308    my($lcp1,$lcp2);
 309
 310
 311    # get the list of .html files in this directory
 312    opendir(DIR, $dir) ||
 313        die "$0: error opening directory $dir for reading: $!\n";
 314    @files = sort(grep(/\.html?$/, readdir(DIR)));
 315    closedir(DIR);
 316
 317    open(HTML, ">$html") ||
 318        die "$0: error opening $html for output: $!\n";
 319
 320    # for each .html file in the directory, extract the index
 321    #   embedded in the file and throw it into the big index.
 322    print HTML "<DL COMPACT>\n";
 323    foreach $file (@files) {
 324        $/ = "";
 325
 326        open(IN, "<$dir/$file") ||
 327            die "$0: error opening $dir/$file for input: $!\n";
 328        @filedata = <IN>;
 329        close(IN);
 330
 331        # pull out the NAME section
 332        my $name;
 333        ($name) = grep(/name="name"/i, @filedata);
 334        ($lcp1,$lcp2) = ($name =~ m,/H1>\s(\S+)\s[\s-]*(.*?)\s*$,smi);
 335        if (defined $lcp1 and $lcp1 =~ m,^<P>$,i) { # Uninteresting.  Try again.
 336            ($lcp1,$lcp2) = ($name =~ m,/H1>\s<P>\s*(\S+)\s[\s-]*(.*?)\s*$,smi);
 337        }
 338        my $url= "$pod/$file" ;
 339        if ( ! defined $Options{htmlroot} || $Options{htmlroot} eq '' ) {
 340            $url = Pod::Html::relativize_url( "$pod/$file", $html ) ;
 341        }
 342
 343        if (defined $lcp1) {
 344            print HTML qq(<DT><A HREF="$url">);
 345            print HTML "$lcp1</A></DT><DD>$lcp2</DD>\n";
 346        }
 347
 348        next;
 349
 350        @index = grep(/<!-- INDEX BEGIN -->.*<!-- INDEX END -->/s,
 351                    @filedata);
 352        for (@index) {
 353            s/<!-- INDEX BEGIN -->(\s*<!--)(.*)(-->\s*)<!-- INDEX END -->/$lcp2/s;
 354            s,#,$dir/$file#,g;
 355            print HTML "$_\n<P><HR><P>\n";
 356        }
 357    }
 358    print HTML "</DL>\n";
 359
 360    close(HTML);
 361}
 362
 363
 364sub split_on_head {
 365    my($podroot, $htmldir, $splitdirs, $ignore, @splithead) = @_;
 366    my($pod, $dirname, $filename);
 367
 368    # split the files specified in @splithead on =head[1-6] pod directives
 369    print "splitting files by head.\n" if $verbose && $#splithead >= 0;
 370    foreach $pod (@splithead) {
 371        # figure out the directory name and filename
 372        $pod      =~ s,^([^/]*)$,/$1,;
 373        $pod      =~ m,(.*)/(.*?)(\.pod)?$,;
 374        $dirname  = $1;
 375        $filename = "$2.pod";
 376
 377        # since we are splitting this file it shouldn't be converted.
 378        push(@$ignore, "$podroot/$dirname/$filename");
 379
 380        # split the pod
 381        splitpod("$podroot/$dirname/$filename", "$podroot/$dirname", $htmldir,
 382            $splitdirs);
 383    }
 384}
 385
 386
 387sub split_on_item {
 388    my($podroot, $splitdirs, $ignore, @splititem) = @_;
 389    my($pwd, $dirname, $filename);
 390
 391    print "splitting files by item.\n" if $verbose && $#splititem >= 0;
 392    $pwd = getcwd();
 393    my $splitter = File::Spec->rel2abs("$splitpod/splitpod", $pwd);
 394    my $perl = File::Spec->rel2abs($^X, $pwd);
 395    foreach my $pod (@splititem) {
 396        # figure out the directory to split into
 397        $pod      =~ s,^([^/]*)$,/$1,;
 398        $pod      =~ m,(.*)/(.*?)(\.pod)?$,;
 399        $dirname  = "$1/$2";
 400        $filename = "$2.pod";
 401
 402        # since we are splitting this file it shouldn't be converted.
 403        push(@$ignore, "$podroot/$dirname.pod");
 404
 405        # split the pod
 406        push(@$splitdirs, "$podroot/$dirname");
 407        if (! -d "$podroot/$dirname") {
 408            mkdir("$podroot/$dirname", 0755) ||
 409                    die "$0: error creating directory $podroot/$dirname: $!\n";
 410        }
 411        chdir("$podroot/$dirname") ||
 412            die "$0: error changing to directory $podroot/$dirname: $!\n";
 413        die "$splitter not found. Use '-splitpod dir' option.\n"
 414            unless -f $splitter;
 415        system($perl, $splitter, "../$filename") &&
 416            warn "$0: error running '$splitter ../$filename'"
 417                 ." from $podroot/$dirname";
 418    }
 419    chdir($pwd);
 420}
 421
 422
 423#
 424# splitpod - splits a .pod file into several smaller .pod files
 425#  where a new file is started each time a =head[1-6] pod directive
 426#  is encountered in the input file.
 427#
 428sub splitpod {
 429    my($pod, $poddir, $htmldir, $splitdirs) = @_;
 430    my(@poddata, @filedata, @heads);
 431    my($file, $i, $j, $prevsec, $section, $nextsec);
 432
 433    print "splitting $pod\n" if $verbose;
 434
 435    # read the file in paragraphs
 436    $/ = "";
 437    open(SPLITIN, "<$pod") ||
 438        die "$0: error opening $pod for input: $!\n";
 439    @filedata = <SPLITIN>;
 440    close(SPLITIN) ||
 441        die "$0: error closing $pod: $!\n";
 442
 443    # restore the file internally by =head[1-6] sections
 444    @poddata = ();
 445    for ($i = 0, $j = -1; $i <= $#filedata; $i++) {
 446        $j++ if ($filedata[$i] =~ /^\s*=head[1-6]/);
 447        if ($j >= 0) { 
 448            $poddata[$j]  = "" unless defined $poddata[$j];
 449            $poddata[$j] .= "\n$filedata[$i]" if $j >= 0;
 450        }
 451    }
 452
 453    # create list of =head[1-6] sections so that we can rewrite
 454    #  L<> links as necessary.
 455    my %heads = ();
 456    foreach $i (0..$#poddata) {
 457        $heads{anchorify($1)} = 1 if $poddata[$i] =~ /=head[1-6]\s+(.*)/;
 458    }
 459
 460    # create a directory of a similar name and store all the
 461    #  files in there
 462    $pod =~ s,.*/(.*),$1,;      # get the last part of the name
 463    my $dir = $pod;
 464    $dir =~ s/\.pod//g;
 465    push(@$splitdirs, "$poddir/$dir");
 466    mkdir("$poddir/$dir", 0755) ||
 467        die "$0: could not create directory $poddir/$dir: $!\n"
 468        unless -d "$poddir/$dir";
 469
 470    $poddata[0] =~ /^\s*=head[1-6]\s+(.*)/;
 471    $section    = "";
 472    $nextsec    = $1;
 473
 474    # for each section of the file create a separate pod file
 475    for ($i = 0; $i <= $#poddata; $i++) {
 476        # determine the "prev" and "next" links
 477        $prevsec = $section;
 478        $section = $nextsec;
 479        if ($i < $#poddata) {
 480            $poddata[$i+1] =~ /^\s*=head[1-6]\s+(.*)/;
 481            $nextsec       = $1;
 482        } else {
 483            $nextsec = "";
 484        }
 485
 486        # determine an appropriate filename (this must correspond with
 487        #  what pod2html will try and guess)
 488        # $poddata[$i] =~ /^\s*=head[1-6]\s+(.*)/;
 489        $file = "$dir/" . anchorify($section) . ".pod";
 490
 491        # create the new .pod file
 492        print "\tcreating $poddir/$file\n" if $verbose;
 493        open(SPLITOUT, ">$poddir/$file") ||
 494            die "$0: error opening $poddir/$file for output: $!\n";
 495        $poddata[$i] =~ s,L<([^<>]*)>,
 496                        defined $heads{anchorify($1)} ? "L<$dir/$1>" : "L<$1>"
 497                     ,ge;
 498        print SPLITOUT $poddata[$i]."\n\n";
 499        print SPLITOUT "=over 4\n\n";
 500        print SPLITOUT "=item *\n\nBack to L<$dir/\"$prevsec\">\n\n" if $prevsec;
 501        print SPLITOUT "=item *\n\nForward to L<$dir/\"$nextsec\">\n\n" if $nextsec;
 502        print SPLITOUT "=item *\n\nUp to L<$dir>\n\n";
 503        print SPLITOUT "=back\n\n";
 504        close(SPLITOUT) ||
 505            die "$0: error closing $poddir/$file: $!\n";
 506    }
 507}
 508
 509
 510#
 511# installdir - takes care of converting the .pod and .pm files in the
 512#  current directory to .html files and then installing those.
 513#
 514sub installdir {
 515    my($dir, $recurse, $podroot, $splitdirs, $ignore) = @_;
 516    my(@dirlist, @podlist, @pmlist, $doindex);
 517
 518    @dirlist = ();      # directories to recurse on
 519    @podlist = ();      # .pod files to install
 520    @pmlist  = ();      # .pm files to install
 521
 522    # should files in this directory get an index?
 523    $doindex = (grep($_ eq "$podroot/$dir", @$splitdirs) ? 0 : 1);
 524
 525    opendir(DIR, "$podroot/$dir")
 526        || die "$0: error opening directory $podroot/$dir: $!\n";
 527
 528    # find the directories to recurse on
 529    @dirlist = map { if ($^O eq 'VMS') {/^(.*)\.dir$/i; "$dir/$1";} else {"$dir/$_";}}
 530        grep(-d "$podroot/$dir/$_" && !/^\.{1,2}/, readdir(DIR)) if $recurse;
 531    rewinddir(DIR);
 532
 533    # find all the .pod files within the directory
 534    @podlist = map { /^(.*)\.pod$/; "$dir/$1" }
 535        grep(! -d "$podroot/$dir/$_" && /\.pod$/, readdir(DIR));
 536    rewinddir(DIR);
 537
 538    # find all the .pm files within the directory
 539    @pmlist = map { /^(.*)\.pm$/; "$dir/$1" }
 540        grep(! -d "$podroot/$dir/$_" && /\.pm$/, readdir(DIR));
 541
 542    closedir(DIR);
 543
 544    # recurse on all subdirectories we kept track of
 545    foreach $dir (@dirlist) {
 546        installdir($dir, $recurse, $podroot, $splitdirs, $ignore);
 547    }
 548
 549    # install all the pods we found
 550    foreach my $pod (@podlist) {
 551        # check if we should ignore it.
 552        next if $pod =~ m(/t/); # comes from a test file
 553        next if grep($_ eq "$pod.pod", @$ignore);
 554
 555        # check if a .pm files exists too
 556        if (grep($_ eq $pod, @pmlist)) {
 557            print  "$0: Warning both '$podroot/$pod.pod' and "
 558                . "'$podroot/$pod.pm' exist, using pod\n";
 559            push(@ignore, "$pod.pm");
 560        }
 561        runpod2html("$pod.pod", $doindex);
 562    }
 563
 564    # install all the .pm files we found
 565    foreach my $pm (@pmlist) {
 566        # check if we should ignore it.
 567        next if $pm =~ m(/t/); # comes from a test file
 568        next if grep($_ eq "$pm.pm", @ignore);
 569
 570        runpod2html("$pm.pm", $doindex);
 571    }
 572}
 573
 574
 575#
 576# runpod2html - invokes pod2html to convert a .pod or .pm file to a .html
 577#  file.
 578#
 579sub runpod2html {
 580    my($pod, $doindex) = @_;
 581    my($html, $i, $dir, @dirs);
 582
 583    $html = $pod;
 584    $html =~ s/\.(pod|pm)$/.html/g;
 585
 586    # make sure the destination directories exist
 587    @dirs = split("/", $html);
 588    $dir  = "$htmldir/";
 589    for ($i = 0; $i < $#dirs; $i++) {
 590        if (! -d "$dir$dirs[$i]") {
 591            mkdir("$dir$dirs[$i]", 0755) ||
 592                die "$0: error creating directory $dir$dirs[$i]: $!\n";
 593        }
 594        $dir .= "$dirs[$i]/";
 595    }
 596
 597    # invoke pod2html
 598    print "$podroot/$pod => $htmldir/$html\n" if $verbose;
 599    Pod::Html::pod2html(
 600        "--htmldir=$htmldir",
 601        "--htmlroot=$htmlroot",
 602        "--podpath=".join(":", @podpath),
 603        "--podroot=$podroot", "--netscape",
 604        "--header",
 605        ($doindex ? "--index" : "--noindex"),
 606        "--" . ($recurse ? "" : "no") . "recurse",
 607        ($#libpods >= 0) ? "--libpods=" . join(":", @libpods) : "",
 608        "--infile=$podroot/$pod", "--outfile=$htmldir/$html");
 609    die "$0: error running $pod2html: $!\n" if $?;
 610}
 611
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.