#! /usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if 0;    # not running under some shell

use 5.8.0;
use strict;
use warnings;

use Carp;
use Config;
use English;
use File::Basename;
use File::Find;
use File::Path;
use File::Slurp;
use File::Spec;
use File::Spec::Unix;
use GDBM_File;
use Getopt::Long;
use HTML::EasyTags;
use IO::File;
use MIME::Base64 qw( decode_base64 );
use Pod::HtmlEasy ( 0.8 );
use Readonly;
use Storable qw( store retrieve );

# Static configuration
Readonly my $debug     =>  0;
Readonly my $DOT       =>  q{.};
Readonly my $EMPTY     =>  q{};
Readonly my $me        =>  basename($PROGRAM_NAME);
Readonly my $page_cols =>  3;
Readonly my @suffixes  => qw{ \.pm \.pod \.pl };
Readonly my $title     =>  q{Perl Documentation};
Readonly my $user      =>  q{root@localhost};
Readonly my $VERSION   =>  q{0.4};

# Variables that may be set from the command line
my $dumpdb;
my $help;
my $scratch;
my $verbose;

# Longest first, otherwise alpha
my @prefixes = sort { length $b <=> length $a or $a cmp $b } @INC;
pop(@prefixes) if $prefixes[-1] eq $DOT;

# For the source directory, take the shortest path in @INC and
# eliminate the last directory component.  With luck, this will
# give you /usr/lib/perl5 (or equivalent) for a standard installation.
my $sourcedir = dirname( $prefixes[-1] );
my $targetdir = q{/usr/local/doc/HTML/Perl};
my @addpods   = qw{ /usr/local/doc/POD };      # Adds on to @INC

if ($debug) {

    # To debug in a smaller environment, use these.
    # Note that if you should use something like
    #    /usr/lib/perl5/vendor_perl/5.8.8/Regexp
    # in order to get a small-sample "live" test, the links will be
    # CPAN searches as the last directory will be lost, resulting in
    # failure of the inverted hash lookup in on_L().
    $targetdir = qq{$ENV{HOME}/Perl/pod2_test_output};
    $sourcedir = qq{$ENV{HOME}/Perl/pod2_test_input};
    @addpods   = ();
    @prefixes  = ($sourcedir);
}

# Make a compiled regex.
Readonly my $prefixer => '\A' . join( '/|\A', @prefixes, @addpods ) . '/';
Readonly my $prefixre => qr{$prefixer};

# Persistant hashes that track the state of PODs converted to HTML.
# %html_track:
#   key:   path to original (.pod, .pm or .pl) file
#   value: [ path to the .html file, last mod time of POD ]
# %nopod_track:
#   key:   path to original .pm  or .pl file
#   value: the last-modified time of the .pm  or .pl file
#          that was found to have no POD.
# %index_track:
#   key:   tag for the index (Foo::Bar)
#   value: ARRAY of [ path to the .html file, last mod time of POD ]
# See insert_latest() for a specific example.
my ( %html_track, %nopod_track, %index_track );
my ( $html_track_ref, $nopod_track_ref, $index_track_ref ) =
   ( \%html_track, \%nopod_track, \%index_track );

# %pod_convert:
#   key:   path to the POD file to convert
#   value: path to the HTML file that will receive the conversion
my ( %pod_convert );

# Files accessed in the course of index generation

# Index tracking and nopod tracking files
Readonly my $html_track  => "$targetdir/.html_track";
Readonly my $index_track => "$targetdir/.index_track";
Readonly my $nopod_track => "$targetdir/.nopod_track";
# Default URL for referenced uninstalled PODs
Readonly my $CPAN        => q{search.cpan.org};
# HTML index for converted PODs
Readonly my $index_file  => "$targetdir/index.html";
# Stylesheet for generated HTML
Readonly my $css         => "$targetdir/.doc.css";
# Graphic to use for "back to top" link
Readonly my $uparrow     => "$targetdir/.up.png";

# Generate a css file.
# Data for the css file is found at the end of this file
sub WriteCSS {
    my $file = shift;
    local $INPUT_RECORD_SEPARATOR = $EMPTY;
    my $css_data                  = <DATA>;
    write_file( $file, \$css_data );
}

# Encoded base64 representation of the uparrow PNG.
Readonly my $uparrow_data => q {
iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAQMAAAABGAcJAAAABlBMVEUAZpn///9z47MeAAAAAnRS
TlP/AOW3MEoAAAABYktHRAH/Ai3eAAAAJ0lEQVR42mNgYGCo/8NQ94ehpoahwoahQIYhgYchxQfE
RUZANQwMABzCDJ1R7eOQAAAAAElFTkSuQmCC
};

# Generate the uparrow png file.
sub WriteUpArrow {
    my $file = shift;
    write_file( $file, { binmode => ':raw' },
    		decode_base64($uparrow_data) );
}

# Get the last modified time of the file arg.
sub mtime {
    return ( stat(shift) )[9];
}

# Execute the Storable module's retrieve() to load the persistent hashes.
sub DoRetrieve {
    return if defined $scratch;		# Hashes are not loaded.
    return unless -e $html_track;	# Hash files are not found.

    print "Retrieving $html_track\n" if defined $verbose;
    $html_track_ref = retrieve( $html_track )
        or die "Unable to retrieve $html_track - $!\n";

    print "Retrieving $index_track\n" if defined $verbose;
    $index_track_ref = retrieve( $index_track )
        or die "Unable to retrieve $index_track - $!\n";

    print "Retrieving $nopod_track\n\n" if defined $verbose;
    $nopod_track_ref = retrieve( $nopod_track )
        or die "Unable to retrieve $nopod_track - $!\n";
}

# Execute the Storable module's store() to save the persistent hashes.
sub DoStore() {
    store( $html_track_ref,  $html_track  );
    store( $index_track_ref, $index_track );
    store( $nopod_track_ref, $nopod_track );
}

# Build HTML for the header/trailer that goes at the top and bottom of 
# generated HTML pages.  The "_top" is a target for the up arrows.
sub build_header {
    my $name = shift;
    my $header = <<"_HEAD_";

<div class=hf>
&nbsp;$name</head>
</div>

<a name="_top"></a>
_HEAD_
    return $header;
}

# Maintain the array of index targets with the latest date as element [0].
# If you wish to maintain the array sorted by date, use this:
#  return [ sort { $b->[1] <=> $a->[1] } @$arrayref, $htmlfile ]
# Here's a sample of the hash.  Note the nested ARRAYs.
# HASH(0x930e668)
#  'Item_error_Pod_Readme' => ARRAY(0x930f3ac)
#     0  ARRAY(0x930f3c4)
#        0  '/home/geoff/Perl/pod2_test_output/Item_error_Pod_Readme.html'
#        1  1151020294
#     1  ARRAY(0x930f3f4)
#        0  '/home/geoff/Perl/pod2_test_output2/Item_error_Pod_Readme.html'
#        1  1150299856
#  'OLEwriter' => ARRAY(0x930f424)
#     0  ARRAY(0x930f43c)
#        0  '/home/geoff/Perl/pod2_test_output/OLEwriter.html'
#        1  1150299856

sub insert_latest {
    my ( $arrayref, $htmlfile ) = @_;
    unless ( defined $arrayref ) {
	# First time for this htmlfile
	return [ $htmlfile ];
    }
    # Multiple links for a particular tag.
    # The $htmlfile (ref to ARRAY) goes at the head of the list if
    # its date is later than the one currently there, and at the
    # back of the list otherwise.  We're interested only in the head
    # of the list for link generation when this tag is referenced.
    $arrayref->[0][1] > $htmlfile->[1] ?
	push(@{$arrayref}, $htmlfile) : unshift(@{$arrayref}, $htmlfile);
    return $arrayref;
}

sub Usage {
    print <<"_USAGE_";
    $me $VERSION
    		  [-dumpdb]	 Dump persistent files and exit.
		  [-help]	 Print this and exit.
		  [-scratch]     Rebuild the persistent files,
		  		 which re-creates all HTML.
		  [-verbose]	 Babble.
		  [path ...]	 Single-file POD conversion.

    $me $VERSION converts POD (.pm, .pl or .pod) files to HTML,
    and indexes them. The state of the HTML is tracked, and re-conversion
    is avoided if unnecessary.
    
    If path(s) are specified on the command line, indexing is skipped,
    and conversion takes place directly.  /foo/bar/test.pm => ./test.pm.html
_USAGE_
    exit(0);
}

# Subroutines which replace the subs internal to pod2html 
# and associated global variables.

# These globals carry results from the internal processing of HTML.
# $NAME_seen:
#   records the fact that we've seen a NAME directive. If that
#   directive has additional text, that's saved. Otherwise, we wait
#   for the _next_ text block and save that. The result is saved in
#   $NAME_text.
# $NAME_text:
#   used for header/footer blocks in the HTML page.
# $HTML_file:
#   the file currently being generated, and is used for
#   links within the page.
my ( $NAME_seen, $NAME_text, $HTML_file );

# We want to use the content of the NAME directive as title, but we
# won't have that data until after the title's been generated, so we
# use a placeholder that should be easy to find.
Readonly my $title_tag => "__xxxx__";

sub on_head1 {
    my ( $this, $txt, $a_name ) = @_;
    if ( $txt =~ m{\ANAME} ) {
	$NAME_seen = 1;
	# Some PODs, pod/* in particular, get a space-padded line here
	my ($txt) = m{\ANAME\s+(.*)\s+\z}; 
	$NAME_text = $txt if defined $txt and length $txt;
    }
    return qq{<h1><a href='#_top'
    	      title='click to go to top of document' 
              name='$a_name'>$txt<img src='$uparrow'
	      alt='^'></a></h1>\n};
}

sub on_textblock {
    my ( $this, $txt ) = @_;
    $NAME_text = $txt if defined $NAME_seen and not defined $NAME_text and length $txt;
    return "<p>$txt</p>\n";
}

# Some PODs have a leading space in the NAME text block, so we get here.

sub on_verbatim {
    my ( $this, $txt ) = @_;

    # Multiple blank lines are parsed as verbatim tesx by Pod::Parser
    # And will show up as empty <pre> blocks, which is mucho nasty
    $txt =~ s{(^[\r\n])*(^[\r\n])\z}{}mg;
    return '' unless length $txt;
    $NAME_text = $txt if defined $NAME_seen and not defined $NAME_text;

    return "<pre>$txt</pre>\n";
}

sub on_L {
    my ( $this, $L, $text, $page, $section, $type ) = @_;
    if ( $type eq 'pod' ) {
	return qq{<i><a href='http://(undefined)'>(undefined)</a></i>} unless defined $text;
        $page = $EMPTY unless defined $page;

        if ( defined $section ) {
            $section =~ s/\s+/-/g;
            return exists $index_track_ref->{$page} ?
                qq{<i><a href='file://$HTML_file$page#$section'>$text</a></i>} :
	        qq{<i><a href='http://$CPAN/perldoc?$page#$section'>$text</a></i>};
        }

	# $text is expected to contain Foo::Bar.
	# Returning a link to an HTML file in the conversion directory
	my $link = $index_track_ref->{$text};
	return defined $link ?
	    qq{<i><a href='file://$link->[0][0]'>$text</a></i>} :
	    # Returning a search command that will (hopefully) locate POD for
	    # a module that is not installed
	    qq{<i><a href='http://$CPAN/perldoc?$page'>$text</a></i>};
    }

    # OK, not a POD.  Try something else
    if ( $type eq 'man' ) {
        return "<i>man $text</i>";
    }

    if ( $type eq 'url' ) {
        return "<a href='$page' target='_blank'>$text</a>";
    }

    print
        "Pod::HtmlEasy asked to process link type $type, ",
	"but that's not supported.";
    return $EMPTY;
}

my $podhtml = Pod::HtmlEasy->new(
    on_head1     => \&on_head1,
    on_textblock => \&on_textblock,
    on_verbatim  => \&on_verbatim,
    on_L         => \&on_L,
);

# This a "wanted" sub for File::Find.
# It accepts files with extension .pod or with extension .pm or .pl and
# which have a line that begins with "=\w+" and saves them in the
# html_track hash. If they've been modified since last conversion,
# or never converted, the file is stuffed into the pod_convert hash.
# If a POD file is removed, it's entry will linger in the persistent files.

sub ListPods {

    my $podfile = $File::Find::name;
    my ( $name, $path, $suffix ) = fileparse( $podfile, @suffixes );
    return unless length $suffix;
    print "$podfile\n" if defined $verbose;

    # Source podfile is assumed to exist. Otherwise, how did we get here?
    # $podmtime is the last-modified time of the .pm or .pod we're
    # considering.
    my $podmtime = mtime($podfile);

    # Check if there is anything to do.
    my $ftime = $nopod_track_ref->{$podfile};
    if ( defined $ftime and ( $ftime >= $podmtime ) ) {

        # The podfile was previously examined and had no POD and
        # it has't been modified since.
        print "  in .nopod_track\n" if defined $verbose;
        return;
    }

    # Now let's see if the podfile was previously converted and
    # whether its been modified since then.
    my $track = $html_track_ref->{$podfile};
    if ( defined $track ) {

        # We've converted this POD before.
        my ( $htmlfile, $ftime ) = @$track;
        if ( -e $htmlfile and ( $ftime >= $podmtime ) ) {

            # The podfile was previously converted and it hasn't
            # been modified since, and the HTML version is still around.
            print "  already converted \n" if defined $verbose;
            return;
        }
    }

    # .pod files should have POD; with .pm or .pl its problematic.
    # So, check the .pm or .pl file to see if there are any lines
    # beginning with "="
    if ( $suffix ne '.pod' ) {

        # Get the source and check
        # This is not foolproof.  But its cheap.
        unless ( grep( m/^=\w+/m, ${ read_file( $podfile,
					        scalar_ref => 1 ) } ) )
        {
            print "  no POD\n" if defined $verbose;
            $nopod_track_ref->{$podfile} = $podmtime;
            return;
        }
    }

    # We have a POD to convert. The time is last-modified of POD.
    # %html_track has the state of the index database.
    # %pod_convert has the PODs that are to be converted this run.
    $path =~ s{$sourcedir}{$targetdir};
    my $htmlfile = "$path$name.html";
    $html_track_ref->{$podfile}   = [ $htmlfile, $podmtime ];
    $pod_convert{$htmlfile}       = $podfile;
    print "  to be converted\n" if defined $verbose;
}

# Convert a POD file to HTML

sub ConvertPOD {
    my ( $pod_file, $html_file ) = @_;

    # $html_file is global as its needed by on_L()
    $HTML_file = $html_file;

    # These represent per-POD-file state
    undef $NAME_text;
    undef $NAME_seen;

    # Make an HTML file from the POD
    my ( $name, $path ) = fileparse($HTML_file);
    mkpath($path) unless -d $path;
    print "$pod_file =>\n  $HTML_file\n"; # if defined $verbose;

    my $html = $podhtml->pod2html(
        $pod_file,
        css   => $css,
        title => $title_tag,
    );

    # Now post-process the generated HTML to fix the title
    # and add header/footer
    my @html = split( "\n", $html );
    unless ( defined $NAME_text ) {
	($NAME_text, $path ) = fileparse($pod_file);
    }
    my $heading = build_header($NAME_text, $name);

    # This should be near the beginning of the array.
    foreach my $html_line (@html) {
        $html_line =~ m{$title_tag} and do {
            $html_line =~ s/$title_tag/$NAME_text/;
	    last;
        };
    }
    # And this as well.
    foreach my $html_line (@html) {
        $html_line =~ m{<body} and do {
            $html_line .= $heading;
	    last;
        };
    }
    # And this should be near the end of the list.
    foreach my $html_line (reverse @html) {
        $html_line =~ m{</body} and do {
            $html_line .= $heading;
	    last;
        };
    }
    $html = join( "\n", @html ) . "\n";
    write_file( $HTML_file, \$html );
}

# The index is re-generated without regard to whether
# the HTML files were changed.

sub BuildIndex {

    my $fh = new IO::File( $index_file, '>' )
        or die "$me: Can't open $index_file: $!\n";
    my $html = HTML::EasyTags->new();
    $html->groups_by_default(1);

    # Do the HTML document header
    $fh->print(
        $html->start_html(
            $title,
            [   $html->link(
                    rel  => 'stylesheet',
                    href => $css,
                    type => 'text/css'
                ),
                $html->link(
                    rev  => 'made',
                    href => "mailto:$user"
                ),
            ],
        ),
        $html->font_start( size => 5 ),
        $html->strong( $html->center($title) ),
        $html->font_end,
        $html->hr,
        $html->p,
        $html->table_start
    );

    # Get set up to dump %index_track as links to the docs
    my @tags  = sort { uc $a cmp uc $b } keys %$index_track_ref;
    my $items = @tags;
    my $nrows = int( $items / $page_cols );
    my $item  = 0;
    my $row   = 0;

    # Create a <tr> for each row (reading across) of <td>s
    while ( $row < $nrows ) {
        $fh->print( $html->tr_start );
        foreach my $tag ( @tags[ $item .. $item + $page_cols - 1 ] ) {
            $fh->print(
                $html->td(
                    [ $html->a( href => $index_track_ref->{$tag}[0][0],
		    		text => $tag ) ]
                )
            );
        }
        $fh->print( $html->tr_end );
        $row++;
        $item += $page_cols;
    }

    # Last row might be not be full
    if ( $item < $items ) {
        $fh->print( $html->tr_start );
        foreach my $tag ( @tags[ $item .. $items - 1 ] ) {
            $fh->print(
                $html->td(
                    [ $html->a( href => $index_track_ref->{$tag}[0][0],
		    		text => $tag ) ]
                )
            );
        }
        $fh->print( $html->tr_end );
    }

    $fh->print( $html->table_end, $html->end_html );
    $fh->close();
}

# Here's where it all begins

Usage()
    unless GetOptions(
    'dumpdb'   => \$dumpdb,
    'help'     => \$help,
    'scratch'  => \$scratch,
    'verbose'  => \$verbose,
    );
Usage() if defined $help;

$SIG{INT} = sub {
    # Persistant hashes not updated.
    exit(1);
};

select STDOUT;
$OUTPUT_AUTOFLUSH = 1;

DoRetrieve();
if ( defined $dumpdb ) {

    die "Can't combine -scratch and -dumpdb!\n" if defined $scratch;

    print "$html_track\n";
    for ( sort keys %$html_track_ref ) {
        print "  $_ =>\n    $html_track_ref->{$_}->[0]\n    ",
	      scalar( localtime($html_track_ref->{$_}->[1]) ), "\n";
    }

    my @index_refs;
    print "\n$index_track\n";
    for ( sort keys %$index_track_ref ) {
        print "  $_ =>\n";
	push(@index_refs, $_) if int(@{$index_track_ref->{$_}}) > 1;
	foreach my $ary_ref ( @{$index_track_ref->{$_}} ) {
	    print "    $ary_ref->[0]\n    ",
		  scalar( localtime($ary_ref->[1]) ), "\n";
	}
    }

    print "\n$nopod_track\n";
    for ( sort keys %$nopod_track_ref ) {
        print "  $_ =>\n    ",
	      scalar( localtime($nopod_track_ref->{$_}) ), "\n";
    }

    if ( @index_refs ) {
	print "\nMultiple index references\n";
	foreach ( @index_refs ) {
	    print "  $_ =>\n";
	    foreach my $ary_ref ( @{$index_track_ref->{$_}} ) {
		print "    $ary_ref->[0]\n    ",
		      scalar( localtime($ary_ref->[1]) ), "\n";
	    }
	}
    }

    exit(0);
}

if (@ARGV) {
    $verbose = 1;
    foreach my $pod_file (@ARGV) {
        unless ( -e $pod_file ) {
            print "No file $pod_file\n";
            next;
        }

        my ( $name, $path, $suffix )
            = fileparse( $pod_file, @suffixes );
        ConvertPOD( $pod_file, "$name.html" );
    }
    exit(0);
}

# Generate a hash (in %pod_convert) of qualifying .pm, .pl and .pod files
# and keep up-to-date the hash of the HTML file state (%html_track)

print "Indexing .pm, .pl and .pod\n\n" if defined $verbose;
find( \&ListPods, $sourcedir, @addpods );

# Update the %index_track hash taking into account that there may be more
# than one path that maps to a particular name. This is used to find 
# the HTML for linking.

print "\nProcessing HTML paths\n\n" if defined $verbose;
foreach my $podfile ( values %pod_convert ) {

    # Extract the name of the POD
    my $htmlfile = $html_track_ref->{$podfile};
    # "." marks the beginning of the suffix
    my ($tag) = $podfile =~ m{$prefixre(.*)\.}; 

    unless ( defined $tag ) {
        # Match didn't work.  Try Plan B.
        my ( $hf, $ht ) = @$htmlfile;
        print "Tag match failure for\n  $hf\n";
        my ( $name, $path, $suffix ) = fileparse( $hf, '\.html' );
        $tag = $name;
    }

    # PODs in the pod/ directory of the standard distribution are referred
    # to without the pod:: prefix, so let's just dump it.
    $tag =~ s{^pod/}{};

    # Convert the POD path (Foo/Bar) to a POD name (Foo::Bar)
    $tag =~ s{/}{::}g;

    print "$podfile =>\n  $htmlfile->[0]\n" if defined $verbose;
    # Insert the new reference into the %index_track entry.
    $index_track_ref->{$tag} = insert_latest( $index_track_ref->{$tag},
    					      $htmlfile );
}

print "\nGenerating HTML\n\n" if defined $verbose;
foreach my $html_file ( keys %pod_convert ) {
    ConvertPOD( $pod_convert{$html_file}, $html_file );
}

# Make an index of the HTML files
BuildIndex();

# Save data
DoStore();

# Write supporting files
WriteCSS($css);
WriteUpArrow($uparrow);

=head1 NAME 

pod2indexed_html - Convert POD files to HTML, create an index page

=head1 SYNOPSIS

pod2indexed_html [-dumpdb] [-help] [-scratch] [-verbose] [POD file ...]

=head1 DESCRIPTION

pod2indexed_html locates all the PODs in your distribution, converts
them to 
HTML and makes an index page. Links are rendered so as to refer 
o the appropriate pages.

The principal advantage of pod2indexed_html is that it uses a persistent
database of module creation times
so that once its been run for the first time, subsequent
executions are relatively quick, depending of course
on what has been changed.
pod2indexed_html notices both new modules and updates.

The generated HTML index is flat, organized to look like a module hierarchy.
For example,
F</some/path/DBI/Const/GetInfo/ANSI.pm> goes to
F</another/path/DBI/Cons/GetInfo/ANSI.html>,
and is indexed under F<Const::GetInfo::Ansi>.

HTML display is controled by a stylesheet, F<.doc.css>
in the document root directory.

=head1 OPTIONS

      -dumpdb	Dump .html_track, .index_track and .nopod_track, then exit.
	        This also provides a list of all links having the same tag.
      -help 	Print this and exit.
      -scratch	Rebuild the persistent files, which re-creates all HTML.
      -verbose	Babble.

=head1 ENVIRONMENT

$ENV{HOME} is used in debug mode.

=head1 PREREQUISITES

This script requires the following modules:
L<Carp>,
L<Config>,
L<English>,
L<File::Basename>,
L<File::Find>,
L<File::Path>,
L<File::Slurp>,
L<File::Spec>,
L<File::Spec::Unix>,
L<GDBM_File>,
L<Getopt::Long>,
L<HTML::EasyTags>,
L<IO::File>,
L<MIME::Base64>,
L<Pod::HtmlEasy>,
L<Readonly>,
L<Storable>

It also requires Perl 5.8.0, but should run under earlier version with only
minor modifications.  Required modules willing, of course.

=head1 CONFIGURATION

Therpersisten
user environments.

=over

=item @addpods

Defaults to F</usr/local/doc/POD>.
This is a list of paths to search for POD files, in addition to @INC.

=item $CPAN

Defaults to "search.cpan.org". This is the URL to be used to search for
modules that are mentioned in a POD, but have not been installed locally.

=item $sourcedir

Defaults to the shortest path in @INC.  Usually, that's
F</usr/lib/perl5>

=item $targetdir

Defaults to F</usr/local/doc/HTML/Perl>

=item $user

Mailto for the creator of the index page. Defaults to "root@localhost".

=item $title

Defaults to "Perl Documentation"

=back

=head1 DIAGNOSTICS

=over 4

=item C<-verbose> 

This will produce all sorts of (allegedly) helpful info. 

=item Tag references more than one POD.

Somtimes, one index entry will map to more than one HTML file.
For example, both
F</usr/local/doc/HTML/Perl/vendor_perl/5.8.8/IPC/Run/Timer.html>
and
F</usr/local/doc/HTML/Perl/site_perl/5.8.8/IPC/Run/Timer.html>
would be pointed to by
F<IPC::Run::Timer>.
Obviously, this is not going to work very well. The situation is resolved by
choosing the most recently modified POD file. To see what's going on, run with
-dumpdb and look for the section titled "Multiple index references" at the end.

=item Unable to open F<file> - I<error>

Failure to open (or create) the database files.

=item Tag match failure for F<some/path>

Failure of the regular expression matching that strips @INC paths.

=item Can't combine -dumpdb and -scratch.

Don't combine C<-dumpdb> and C<-scratch>, as this will delete the database
before dumping it.

=item Pod::HtmlEasy asked to process link of type I<type>, but that's not supported.

HTML conversion has missed a particular link type.
Supported types are: URL, manpage, URI.

=back

=head1 FILES

Assuming the you are using the default target directory.

=over 4

=item F</usr/local/doc/HTML/Perl/.html_track>

Tracks the HTML file by the corresponding POD (.pm or .pod) file.

 /usr/local/doc/HTML/Perl/.html_track
  /usr/lib/perl5/5.8.8/AnyDBM_File.pm =>
    /usr/local/doc/HTML/Perl/5.8.8/AnyDBM_File.html
    Sun Jun  4 16:45:28 2006
  /usr/lib/perl5/5.8.8/Attribute/Handlers.pm =>
    /usr/local/doc/HTML/Perl/5.8.8/Attribute/Handlers.html
    Sun Jun  4 16:45:28 2006
  /usr/lib/perl5/5.8.8/AutoLoader.pm =>
    /usr/local/doc/HTML/Perl/5.8.8/AutoLoader.html
    Sun Jun  4 16:45:28 2006
...

=item F</usr/local/doc/HTML/Perl/.index_track>

Tracks the HTML index entry by the file tag and HTML file.

 /usr/local/doc/HTML/Perl/.index_track
  APR =>
    /usr/local/doc/HTML/Perl/vendor_perl/5.8.8/i386-linux-thread-multi/APR.html
    Sat Feb 11 23:29:50 2006
  APR::Base64 =>
    /usr/local/doc/HTML/Perl/vendor_perl/5.8.8/i386-linux-thread-multi/APR/Base64.html
    Sat Feb 11 23:29:57 2006
  APR::Brigade =>
    /usr/local/doc/HTML/Perl/vendor_perl/5.8.8/i386-linux-thread-multi/APR/Brigade.html
    Sat Feb 11 23:29:34 2006
...

There are some entries that point to multiple HTML files 
(found at the end out the output), for example:

  Archive::Extract =>
    /usr/local/doc/HTML/Perl/vendor_perl/5.8.8/Archive/Extract.html
    Thu Jan 19 04:53:02 2006
    /usr/local/doc/HTML/Perl/site_perl/5.8.8/Archive/Extract.html
    Thu Jan 19 04:53:02 2006
...

=item F</usr/local/doc/HTML/Perl/.nopod_track>

Tracks those .pm files that were not found to have POD.

 /usr/local/doc/HTML/Perl/.nopod_track
  /usr/lib/perl5/5.8.8/CGI/eg/make_links.pl =>
    Sun Jun  4 16:45:28 2006
  /usr/lib/perl5/5.8.8/CPAN/Config.pm =>
    Mon Jun 19 17:02:39 2006
...

=item F</usr/local/doc/HTML/Perl/.doc.css>

The style sheet that's generated to go with the HTML.
The source for this file is found in the script,
at the end of the file.

=item F</usr/local/doc/HTML/Perl/.up.png>

The "back to the top" arrow gliph.
The source for this file is found in the script.

=back

=head1 AUTHOR

Geoffrey Leach <geoff@hughes.net>

=head1 VERSION

0.4

=head1 COPYRIGHT AND LICENSE

Copyright 2006 by Geoffrey Leach

This script is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.

=pod SCRIPT CATEGORIES

CPAN/Administrative

=cut

__END__
BODY {
    background: white;
    color: black;
    font-family: arial,sans-serif;
    margin: 0;
    padding: 1ex;
}
TABLE {
    border-collapse: collapse;
    border-spacing: 0;
    border-width: 0;
    color: inherit;
}
IMG { border: 0; }
FORM { margin: 0; }
input { margin: 2px; }
A.fred {
    text-decoration: none;
}
A:link, A:visited {
    background: transparent;
    color: #006699;
}
TD {
    margin: 0;
    padding: 0;
}
DIV {
    border-width: 0;
}
DT {
    margin-top: 1em;
}
TH {
    background: #bbbbbb;
    color: inherit;
    padding: 0.4ex 1ex;
    text-align: left;
}
TH A:link, TH A:visited {
    background: transparent;
    color: black;
}
A.m:link, A.m:visited {
    background: #006699;
    color: white;
    font: bold 10pt Arial,Helvetica,sans-serif;
    text-decoration: none;
}
A.o:link, A.o:visited {
    background: #006699;
    color: #ccffcc;
    font: bold 10pt Arial,Helvetica,sans-serif;
    text-decoration: none;
}
A.o:hover {
    background: transparent;
    color: #ff6600;
    text-decoration: underline;
}
A.m:hover {
    background: transparent;
    color: #ff6600;
    text-decoration: underline;
}
table.dlsip     {
    background: #dddddd;
    border: 0.4ex solid #dddddd;
}
.pod PRE     {
    background: #eeeeee;
    border: 1px solid #888888;
    color: black;
    padding: 1em;
    white-space: pre;
}
.HF     {
    background: #eeeeee;
    border: 1px solid #888888;
    color: black;
    margin: 1ex 0;
    padding: 0.5ex 1ex;
}
.pod H1      {
    background: transparent;
    color: #006699;
    font-size: large;
}
.pod H2      {
    background: transparent;
    color: #006699;
    font-size: medium;
}
.pod IMG     {
    vertical-align: top;
}
.pod .toc A  {
    text-decoration: none;
}
.pod .toc LI {
    line-height: 1.2em;
    list-style-type: none;
}
