1898184e3Ssthenpackage Pod::Html; 2898184e3Ssthenuse strict; 3eac174f2Safresh1use Exporter 'import'; 4898184e3Ssthen 5*3d61058aSafresh1our $VERSION = 1.35; 6eac174f2Safresh1$VERSION = eval $VERSION; 7e0680481Safresh1our @EXPORT = qw(pod2html); 8898184e3Ssthen 9898184e3Ssthenuse Config; 10898184e3Ssthenuse Cwd; 11898184e3Ssthenuse File::Basename; 12898184e3Ssthenuse File::Spec; 13898184e3Ssthenuse Pod::Simple::Search; 149f11ffb7Safresh1use Pod::Simple::SimpleTree (); 15eac174f2Safresh1use Pod::Html::Util qw( 16eac174f2Safresh1 html_escape 17eac174f2Safresh1 process_command_line 18eac174f2Safresh1 trim_leading_whitespace 19eac174f2Safresh1 unixify 20eac174f2Safresh1 usage 21eac174f2Safresh1 htmlify 22eac174f2Safresh1 anchorify 23eac174f2Safresh1 relativize_url 24eac174f2Safresh1); 25b8851fccSafresh1use locale; # make \w work right in non-ASCII lands 26898184e3Ssthen 27898184e3Ssthen=head1 NAME 28898184e3Ssthen 29898184e3SsthenPod::Html - module to convert pod files to HTML 30898184e3Ssthen 31898184e3Ssthen=head1 SYNOPSIS 32898184e3Ssthen 33898184e3Ssthen use Pod::Html; 34898184e3Ssthen pod2html([options]); 35898184e3Ssthen 36898184e3Ssthen=head1 DESCRIPTION 37898184e3Ssthen 38898184e3SsthenConverts files from pod format (see L<perlpod>) to HTML format. It 39898184e3Ssthencan automatically generate indexes and cross-references, and it keeps 40898184e3Ssthena cache of things it knows how to cross-reference. 41898184e3Ssthen 42898184e3Ssthen=head1 FUNCTIONS 43898184e3Ssthen 44898184e3Ssthen=head2 pod2html 45898184e3Ssthen 46898184e3Ssthen pod2html("pod2html", 47898184e3Ssthen "--podpath=lib:ext:pod:vms", 48898184e3Ssthen "--podroot=/usr/src/perl", 49898184e3Ssthen "--htmlroot=/perl/nmanual", 50898184e3Ssthen "--recurse", 51898184e3Ssthen "--infile=foo.pod", 52898184e3Ssthen "--outfile=/perl/nmanual/foo.html"); 53898184e3Ssthen 54898184e3Ssthenpod2html takes the following arguments: 55898184e3Ssthen 56898184e3Ssthen=over 4 57898184e3Ssthen 58898184e3Ssthen=item backlink 59898184e3Ssthen 60898184e3Ssthen --backlink 61898184e3Ssthen 62898184e3SsthenTurns every C<head1> heading into a link back to the top of the page. 63898184e3SsthenBy default, no backlinks are generated. 64898184e3Ssthen 65898184e3Ssthen=item cachedir 66898184e3Ssthen 67898184e3Ssthen --cachedir=name 68898184e3Ssthen 69898184e3SsthenCreates the directory cache in the given directory. 70898184e3Ssthen 71898184e3Ssthen=item css 72898184e3Ssthen 73898184e3Ssthen --css=stylesheet 74898184e3Ssthen 75898184e3SsthenSpecify the URL of a cascading style sheet. Also disables all HTML/CSS 76898184e3SsthenC<style> attributes that are output by default (to avoid conflicts). 77898184e3Ssthen 78898184e3Ssthen=item flush 79898184e3Ssthen 80898184e3Ssthen --flush 81898184e3Ssthen 82898184e3SsthenFlushes the directory cache. 83898184e3Ssthen 84898184e3Ssthen=item header 85898184e3Ssthen 86898184e3Ssthen --header 87898184e3Ssthen --noheader 88898184e3Ssthen 89898184e3SsthenCreates header and footer blocks containing the text of the C<NAME> 90898184e3Ssthensection. By default, no headers are generated. 91898184e3Ssthen 92898184e3Ssthen=item help 93898184e3Ssthen 94898184e3Ssthen --help 95898184e3Ssthen 96898184e3SsthenDisplays the usage message. 97898184e3Ssthen 98898184e3Ssthen=item htmldir 99898184e3Ssthen 100898184e3Ssthen --htmldir=name 101898184e3Ssthen 102898184e3SsthenSets the directory to which all cross references in the resulting 103898184e3Ssthenhtml file will be relative. Not passing this causes all links to be 104898184e3Ssthenabsolute since this is the value that tells Pod::Html the root of the 105898184e3Ssthendocumentation tree. 106898184e3Ssthen 107898184e3SsthenDo not use this and --htmlroot in the same call to pod2html; they are 108898184e3Ssthenmutually exclusive. 109898184e3Ssthen 110898184e3Ssthen=item htmlroot 111898184e3Ssthen 112898184e3Ssthen --htmlroot=name 113898184e3Ssthen 114898184e3SsthenSets the base URL for the HTML files. When cross-references are made, 115898184e3Ssthenthe HTML root is prepended to the URL. 116898184e3Ssthen 117898184e3SsthenDo not use this if relative links are desired: use --htmldir instead. 118898184e3Ssthen 119898184e3SsthenDo not pass both this and --htmldir to pod2html; they are mutually 120898184e3Ssthenexclusive. 121898184e3Ssthen 122898184e3Ssthen=item index 123898184e3Ssthen 124898184e3Ssthen --index 125898184e3Ssthen --noindex 126898184e3Ssthen 127898184e3SsthenGenerate an index at the top of the HTML file. This is the default 128898184e3Ssthenbehaviour. 129898184e3Ssthen 130898184e3Ssthen=item infile 131898184e3Ssthen 132898184e3Ssthen --infile=name 133898184e3Ssthen 134898184e3SsthenSpecify the pod file to convert. Input is taken from STDIN if no 135898184e3Sstheninfile is specified. 136898184e3Ssthen 137898184e3Ssthen=item outfile 138898184e3Ssthen 139898184e3Ssthen --outfile=name 140898184e3Ssthen 141898184e3SsthenSpecify the HTML file to create. Output goes to STDOUT if no outfile 142898184e3Ssthenis specified. 143898184e3Ssthen 144898184e3Ssthen=item poderrors 145898184e3Ssthen 146898184e3Ssthen --poderrors 147898184e3Ssthen --nopoderrors 148898184e3Ssthen 149898184e3SsthenInclude a "POD ERRORS" section in the outfile if there were any POD 150898184e3Ssthenerrors in the infile. This section is included by default. 151898184e3Ssthen 152898184e3Ssthen=item podpath 153898184e3Ssthen 154898184e3Ssthen --podpath=name:...:name 155898184e3Ssthen 156898184e3SsthenSpecify which subdirectories of the podroot contain pod files whose 157898184e3SsthenHTML converted forms can be linked to in cross references. 158898184e3Ssthen 159898184e3Ssthen=item podroot 160898184e3Ssthen 161898184e3Ssthen --podroot=name 162898184e3Ssthen 163898184e3SsthenSpecify the base directory for finding library pods. Default is the 164898184e3Ssthencurrent working directory. 165898184e3Ssthen 166898184e3Ssthen=item quiet 167898184e3Ssthen 168898184e3Ssthen --quiet 169898184e3Ssthen --noquiet 170898184e3Ssthen 171898184e3SsthenDon't display I<mostly harmless> warning messages. These messages 172898184e3Ssthenwill be displayed by default. But this is not the same as C<verbose> 173898184e3Ssthenmode. 174898184e3Ssthen 175898184e3Ssthen=item recurse 176898184e3Ssthen 177898184e3Ssthen --recurse 178898184e3Ssthen --norecurse 179898184e3Ssthen 180898184e3SsthenRecurse into subdirectories specified in podpath (default behaviour). 181898184e3Ssthen 182898184e3Ssthen=item title 183898184e3Ssthen 184898184e3Ssthen --title=title 185898184e3Ssthen 186898184e3SsthenSpecify the title of the resulting HTML file. 187898184e3Ssthen 188898184e3Ssthen=item verbose 189898184e3Ssthen 190898184e3Ssthen --verbose 191898184e3Ssthen --noverbose 192898184e3Ssthen 193898184e3SsthenDisplay progress messages. By default, they won't be displayed. 194898184e3Ssthen 195898184e3Ssthen=back 196898184e3Ssthen 197e0680481Safresh1=head2 Formerly Exported Auxiliary Functions 198898184e3Ssthen 199eac174f2Safresh1Prior to perl-5.36, the following three functions were exported by 200eac174f2Safresh1F<Pod::Html>, either by default or on request: 201898184e3Ssthen 202eac174f2Safresh1=over 4 203898184e3Ssthen 204eac174f2Safresh1=item * C<htmlify()> (by default) 205898184e3Ssthen 206eac174f2Safresh1=item * C<anchorify()> (upon request) 207898184e3Ssthen 208eac174f2Safresh1=item * C<relativize_url()> (upon request) 209eac174f2Safresh1 210eac174f2Safresh1=back 211eac174f2Safresh1 212eac174f2Safresh1The definition and documentation of these functions have been moved to 213eac174f2Safresh1F<Pod::Html::Util>, viewable via C<perldoc Pod::Html::Util>. 214eac174f2Safresh1 215e0680481Safresh1Beginning with perl-5.38 these functions must be explicitly imported from 216e0680481Safresh1F<Pod::Html::Util>. Please modify your code as needed. 217898184e3Ssthen 218898184e3Ssthen=head1 ENVIRONMENT 219898184e3Ssthen 220898184e3SsthenUses C<$Config{pod2html}> to setup default options. 221898184e3Ssthen 222898184e3Ssthen=head1 AUTHOR 223898184e3Ssthen 224898184e3SsthenMarc Green, E<lt>marcgreen@cpan.orgE<gt>. 225898184e3Ssthen 226898184e3SsthenOriginal version by Tom Christiansen, E<lt>tchrist@perl.comE<gt>. 227898184e3Ssthen 228898184e3Ssthen=head1 SEE ALSO 229898184e3Ssthen 230898184e3SsthenL<perlpod> 231898184e3Ssthen 232898184e3Ssthen=head1 COPYRIGHT 233898184e3Ssthen 234898184e3SsthenThis program is distributed under the Artistic License. 235898184e3Ssthen 236898184e3Ssthen=cut 237898184e3Ssthen 238eac174f2Safresh1sub new { 239eac174f2Safresh1 my $class = shift; 240eac174f2Safresh1 return bless {}, $class; 241898184e3Ssthen} 242898184e3Ssthen 243898184e3Ssthensub pod2html { 244898184e3Ssthen local(@ARGV) = @_; 245898184e3Ssthen local $_; 246898184e3Ssthen 247eac174f2Safresh1 my $self = Pod::Html->new(); 248eac174f2Safresh1 $self->init_globals(); 249eac174f2Safresh1 250eac174f2Safresh1 my $opts = process_command_line; 251eac174f2Safresh1 $self->process_options($opts); 252eac174f2Safresh1 253eac174f2Safresh1 $self->refine_globals(); 254eac174f2Safresh1 255eac174f2Safresh1 # load or generate/cache %Pages 256eac174f2Safresh1 unless ($self->get_cache()) { 257eac174f2Safresh1 # generate %Pages 258eac174f2Safresh1 #%Pages = $self->generate_cache(\%Pages); 259eac174f2Safresh1 $self->generate_cache($self->{Pages}); 260eac174f2Safresh1 } 261eac174f2Safresh1 my $input = $self->identify_input(); 262eac174f2Safresh1 my $podtree = $self->parse_input_for_podtree($input); 263eac174f2Safresh1 $self->set_Title_from_podtree($podtree); 264eac174f2Safresh1 265eac174f2Safresh1 # set options for the HTML generator 266eac174f2Safresh1 my $parser = Pod::Simple::XHTML::LocalPodLinks->new(); 267eac174f2Safresh1 $parser->codes_in_verbatim(0); 268eac174f2Safresh1 $parser->anchor_items(1); # the old Pod::Html always did 269eac174f2Safresh1 $parser->backlink($self->{Backlink}); # linkify =head1 directives 270eac174f2Safresh1 $parser->force_title($self->{Title}); 271eac174f2Safresh1 $parser->htmldir($self->{Htmldir}); 272eac174f2Safresh1 $parser->htmlfileurl($self->{Htmlfileurl}); 273eac174f2Safresh1 $parser->htmlroot($self->{Htmlroot}); 274eac174f2Safresh1 $parser->index($self->{Doindex}); 275eac174f2Safresh1 $parser->output_string(\$self->{output}); # written to file later 276eac174f2Safresh1 #$parser->pages(\%Pages); 277eac174f2Safresh1 $parser->pages($self->{Pages}); 278eac174f2Safresh1 $parser->quiet($self->{Quiet}); 279eac174f2Safresh1 $parser->verbose($self->{Verbose}); 280eac174f2Safresh1 281eac174f2Safresh1 $parser = $self->refine_parser($parser); 282eac174f2Safresh1 $self->feed_tree_to_parser($parser, $podtree); 283eac174f2Safresh1 $self->write_file(); 284eac174f2Safresh1} 285eac174f2Safresh1 286eac174f2Safresh1sub init_globals { 287eac174f2Safresh1 my $self = shift; 288eac174f2Safresh1 $self->{Cachedir} = "."; # The directory to which directory caches 289eac174f2Safresh1 # will be written. 290eac174f2Safresh1 291eac174f2Safresh1 $self->{Dircache} = "pod2htmd.tmp"; 292eac174f2Safresh1 293eac174f2Safresh1 $self->{Htmlroot} = "/"; # http-server base directory from which all 294eac174f2Safresh1 # relative paths in $podpath stem. 295eac174f2Safresh1 $self->{Htmldir} = ""; # The directory to which the html pages 296eac174f2Safresh1 # will (eventually) be written. 297eac174f2Safresh1 $self->{Htmlfile} = ""; # write to stdout by default 298eac174f2Safresh1 $self->{Htmlfileurl} = ""; # The url that other files would use to 299eac174f2Safresh1 # refer to this file. This is only used 300eac174f2Safresh1 # to make relative urls that point to 301eac174f2Safresh1 # other files. 302eac174f2Safresh1 303eac174f2Safresh1 $self->{Poderrors} = 1; 304eac174f2Safresh1 $self->{Podfile} = ""; # read from stdin by default 305eac174f2Safresh1 $self->{Podpath} = []; # list of directories containing library pods. 306eac174f2Safresh1 $self->{Podroot} = $self->{Curdir} = File::Spec->curdir; 307eac174f2Safresh1 # filesystem base directory from which all 308eac174f2Safresh1 # relative paths in $podpath stem. 309eac174f2Safresh1 $self->{Css} = ''; # Cascading style sheet 310eac174f2Safresh1 $self->{Recurse} = 1; # recurse on subdirectories in $podpath. 311eac174f2Safresh1 $self->{Quiet} = 0; # not quiet by default 312eac174f2Safresh1 $self->{Verbose} = 0; # not verbose by default 313eac174f2Safresh1 $self->{Doindex} = 1; # non-zero if we should generate an index 314eac174f2Safresh1 $self->{Backlink} = 0; # no backlinks added by default 315eac174f2Safresh1 $self->{Header} = 0; # produce block header/footer 316eac174f2Safresh1 $self->{Title} = undef; # title to give the pod(s) 317eac174f2Safresh1 $self->{Saved_Cache_Key} = ''; 318eac174f2Safresh1 $self->{Pages} = {}; 319eac174f2Safresh1 return $self; 320eac174f2Safresh1} 321eac174f2Safresh1 322eac174f2Safresh1sub process_options { 323eac174f2Safresh1 my ($self, $opts) = @_; 324eac174f2Safresh1 325eac174f2Safresh1 $self->{Podpath} = (defined $opts->{podpath}) 326eac174f2Safresh1 ? [ split(":", $opts->{podpath}) ] 327eac174f2Safresh1 : []; 328eac174f2Safresh1 329eac174f2Safresh1 $self->{Backlink} = $opts->{backlink} if defined $opts->{backlink}; 330eac174f2Safresh1 $self->{Cachedir} = unixify($opts->{cachedir}) if defined $opts->{cachedir}; 331eac174f2Safresh1 $self->{Css} = $opts->{css} if defined $opts->{css}; 332eac174f2Safresh1 $self->{Header} = $opts->{header} if defined $opts->{header}; 333eac174f2Safresh1 $self->{Htmldir} = unixify($opts->{htmldir}) if defined $opts->{htmldir}; 334eac174f2Safresh1 $self->{Htmlroot} = unixify($opts->{htmlroot}) if defined $opts->{htmlroot}; 335eac174f2Safresh1 $self->{Doindex} = $opts->{index} if defined $opts->{index}; 336eac174f2Safresh1 $self->{Podfile} = unixify($opts->{infile}) if defined $opts->{infile}; 337eac174f2Safresh1 $self->{Htmlfile} = unixify($opts->{outfile}) if defined $opts->{outfile}; 338eac174f2Safresh1 $self->{Poderrors} = $opts->{poderrors} if defined $opts->{poderrors}; 339eac174f2Safresh1 $self->{Podroot} = unixify($opts->{podroot}) if defined $opts->{podroot}; 340eac174f2Safresh1 $self->{Quiet} = $opts->{quiet} if defined $opts->{quiet}; 341eac174f2Safresh1 $self->{Recurse} = $opts->{recurse} if defined $opts->{recurse}; 342eac174f2Safresh1 $self->{Title} = $opts->{title} if defined $opts->{title}; 343eac174f2Safresh1 $self->{Verbose} = $opts->{verbose} if defined $opts->{verbose}; 344eac174f2Safresh1 345eac174f2Safresh1 warn "Flushing directory caches\n" 346eac174f2Safresh1 if $opts->{verbose} && defined $opts->{flush}; 347eac174f2Safresh1 $self->{Dircache} = "$self->{Cachedir}/pod2htmd.tmp"; 348eac174f2Safresh1 if (defined $opts->{flush}) { 349eac174f2Safresh1 1 while unlink($self->{Dircache}); 350eac174f2Safresh1 } 351eac174f2Safresh1 return $self; 352eac174f2Safresh1} 353eac174f2Safresh1 354eac174f2Safresh1sub refine_globals { 355eac174f2Safresh1 my $self = shift; 356898184e3Ssthen 357898184e3Ssthen # prevent '//' in urls 358eac174f2Safresh1 $self->{Htmlroot} = "" if $self->{Htmlroot} eq "/"; 359eac174f2Safresh1 $self->{Htmldir} =~ s#/\z##; 360898184e3Ssthen 361eac174f2Safresh1 if ( $self->{Htmlroot} eq '' 362eac174f2Safresh1 && defined( $self->{Htmldir} ) 363eac174f2Safresh1 && $self->{Htmldir} ne '' 364eac174f2Safresh1 && substr( $self->{Htmlfile}, 0, length( $self->{Htmldir} ) ) eq $self->{Htmldir} 365898184e3Ssthen ) { 366898184e3Ssthen # Set the 'base' url for this file, so that we can use it 367898184e3Ssthen # as the location from which to calculate relative links 368898184e3Ssthen # to other files. If this is '', then absolute links will 369898184e3Ssthen # be used throughout. 370eac174f2Safresh1 #$self->{Htmlfileurl} = "$self->{Htmldir}/" . substr( $self->{Htmlfile}, length( $self->{Htmldir} ) + 1); 371eac174f2Safresh1 # Is the above not just "$self->{Htmlfileurl} = $self->{Htmlfile}"? 372eac174f2Safresh1 $self->{Htmlfileurl} = unixify($self->{Htmlfile}); 373eac174f2Safresh1 } 374eac174f2Safresh1 return $self; 375898184e3Ssthen} 376898184e3Ssthen 377eac174f2Safresh1sub generate_cache { 378eac174f2Safresh1 my $self = shift; 379898184e3Ssthen my $pwd = getcwd(); 380eac174f2Safresh1 chdir($self->{Podroot}) || 381eac174f2Safresh1 die "$0: error changing to directory $self->{Podroot}: $!\n"; 382898184e3Ssthen 383898184e3Ssthen # find all pod modules/pages in podpath, store in %Pages 384eac174f2Safresh1 # - inc(0): do not prepend directories in @INC to search list; 385eac174f2Safresh1 # limit search to those in @{$self->{Podpath}} 386eac174f2Safresh1 # - verbose: report (via 'warn') what search is doing 387eac174f2Safresh1 # - laborious: to allow '.' in dirnames (e.g., /usr/share/perl/5.14.1) 388eac174f2Safresh1 # - recurse: go into subdirectories 389eac174f2Safresh1 # - survey: search for POD files in PodPath 390eac174f2Safresh1 my ($name2path, $path2name) = 391eac174f2Safresh1 Pod::Simple::Search->new->inc(0)->verbose($self->{Verbose})->laborious(1) 392eac174f2Safresh1 ->recurse($self->{Recurse})->survey(@{$self->{Podpath}}); 393eac174f2Safresh1 # remove Podroot and extension from each file 394eac174f2Safresh1 for my $k (keys %{$name2path}) { 395eac174f2Safresh1 $self->{Pages}{$k} = _transform($self, $name2path->{$k}); 396eac174f2Safresh1 } 397898184e3Ssthen 398898184e3Ssthen chdir($pwd) || die "$0: error changing to directory $pwd: $!\n"; 399898184e3Ssthen 400898184e3Ssthen # cache the directory list for later use 401eac174f2Safresh1 warn "caching directories for later use\n" if $self->{Verbose}; 402eac174f2Safresh1 open my $cache, '>', $self->{Dircache} 403eac174f2Safresh1 or die "$0: error open $self->{Dircache} for writing: $!\n"; 404898184e3Ssthen 405eac174f2Safresh1 print $cache join(":", @{$self->{Podpath}}) . "\n$self->{Podroot}\n"; 406eac174f2Safresh1 my $_updirs_only = ($self->{Podroot} =~ /\.\./) && !($self->{Podroot} =~ /[^\.\\\/]/); 407eac174f2Safresh1 foreach my $key (keys %{$self->{Pages}}) { 408898184e3Ssthen if($_updirs_only) { 409eac174f2Safresh1 my $_dirlevel = $self->{Podroot}; 410898184e3Ssthen while($_dirlevel =~ /\.\./) { 411898184e3Ssthen $_dirlevel =~ s/\.\.//; 412eac174f2Safresh1 # Assume $Pagesref->{$key} has '/' separators (html dir separators). 413eac174f2Safresh1 $self->{Pages}->{$key} =~ s/^[\w\s\-\.]+\///; 414898184e3Ssthen } 415898184e3Ssthen } 416eac174f2Safresh1 print $cache "$key $self->{Pages}->{$key}\n"; 417eac174f2Safresh1 } 418eac174f2Safresh1 close $cache or die "error closing $self->{Dircache}: $!"; 419898184e3Ssthen} 420898184e3Ssthen 421eac174f2Safresh1sub _transform { 422eac174f2Safresh1 my ($self, $v) = @_; 423eac174f2Safresh1 $v = $self->{Podroot} eq File::Spec->curdir 424eac174f2Safresh1 ? File::Spec->abs2rel($v) 425eac174f2Safresh1 : File::Spec->abs2rel($v, 426eac174f2Safresh1 File::Spec->canonpath($self->{Podroot})); 427eac174f2Safresh1 428eac174f2Safresh1 # Convert path to unix style path 429eac174f2Safresh1 $v = unixify($v); 430eac174f2Safresh1 431eac174f2Safresh1 my ($file, $dir) = fileparse($v, qr/\.[^.]*/); # strip .ext 432eac174f2Safresh1 return $dir.$file; 433898184e3Ssthen} 434898184e3Ssthen 435eac174f2Safresh1sub get_cache { 436eac174f2Safresh1 my $self = shift; 437eac174f2Safresh1 438eac174f2Safresh1 # A first-level cache: 439eac174f2Safresh1 # Don't bother reading the cache files if they still apply 440eac174f2Safresh1 # and haven't changed since we last read them. 441eac174f2Safresh1 442eac174f2Safresh1 my $this_cache_key = $self->cache_key(); 443eac174f2Safresh1 return 1 if $self->{Saved_Cache_Key} and $this_cache_key eq $self->{Saved_Cache_Key}; 444eac174f2Safresh1 $self->{Saved_Cache_Key} = $this_cache_key; 445eac174f2Safresh1 446eac174f2Safresh1 # load the cache of %Pages if possible. $tests will be 447eac174f2Safresh1 # non-zero if successful. 448eac174f2Safresh1 my $tests = 0; 449eac174f2Safresh1 if (-f $self->{Dircache}) { 450eac174f2Safresh1 warn "scanning for directory cache\n" if $self->{Verbose}; 451eac174f2Safresh1 $tests = $self->load_cache(); 452eac174f2Safresh1 } 453eac174f2Safresh1 454eac174f2Safresh1 return $tests; 455eac174f2Safresh1} 456eac174f2Safresh1 457eac174f2Safresh1sub cache_key { 458eac174f2Safresh1 my $self = shift; 459eac174f2Safresh1 return join('!', 460eac174f2Safresh1 $self->{Dircache}, 461eac174f2Safresh1 $self->{Recurse}, 462eac174f2Safresh1 @{$self->{Podpath}}, 463eac174f2Safresh1 $self->{Podroot}, 464eac174f2Safresh1 stat($self->{Dircache}), 465eac174f2Safresh1 ); 466eac174f2Safresh1} 467eac174f2Safresh1 468eac174f2Safresh1# 469eac174f2Safresh1# load_cache - tries to find if the cache stored in $dircache is a valid 470eac174f2Safresh1# cache of %Pages. if so, it loads them and returns a non-zero value. 471eac174f2Safresh1# 472eac174f2Safresh1sub load_cache { 473eac174f2Safresh1 my $self = shift; 474eac174f2Safresh1 my $tests = 0; 475eac174f2Safresh1 local $_; 476eac174f2Safresh1 477eac174f2Safresh1 warn "scanning for directory cache\n" if $self->{Verbose}; 478eac174f2Safresh1 open(my $cachefh, '<', $self->{Dircache}) || 479eac174f2Safresh1 die "$0: error opening $self->{Dircache} for reading: $!\n"; 480eac174f2Safresh1 $/ = "\n"; 481eac174f2Safresh1 482eac174f2Safresh1 # is it the same podpath? 483eac174f2Safresh1 $_ = <$cachefh>; 484eac174f2Safresh1 chomp($_); 485eac174f2Safresh1 $tests++ if (join(":", @{$self->{Podpath}}) eq $_); 486eac174f2Safresh1 487eac174f2Safresh1 # is it the same podroot? 488eac174f2Safresh1 $_ = <$cachefh>; 489eac174f2Safresh1 chomp($_); 490eac174f2Safresh1 $tests++ if ($self->{Podroot} eq $_); 491eac174f2Safresh1 492eac174f2Safresh1 # load the cache if its good 493eac174f2Safresh1 if ($tests != 2) { 494eac174f2Safresh1 close($cachefh); 495eac174f2Safresh1 return 0; 496eac174f2Safresh1 } 497eac174f2Safresh1 498eac174f2Safresh1 warn "loading directory cache\n" if $self->{Verbose}; 499eac174f2Safresh1 while (<$cachefh>) { 500eac174f2Safresh1 /(.*?) (.*)$/; 501eac174f2Safresh1 $self->{Pages}->{$1} = $2; 502eac174f2Safresh1 } 503eac174f2Safresh1 504eac174f2Safresh1 close($cachefh); 505eac174f2Safresh1 return 1; 506eac174f2Safresh1} 507eac174f2Safresh1 508eac174f2Safresh1sub identify_input { 509eac174f2Safresh1 my $self = shift; 5109f11ffb7Safresh1 my $input; 5119f11ffb7Safresh1 unless (@ARGV && $ARGV[0]) { 512eac174f2Safresh1 if ($self->{Podfile} and $self->{Podfile} ne '-') { 513eac174f2Safresh1 $input = $self->{Podfile}; 5149f11ffb7Safresh1 } else { 5159f11ffb7Safresh1 $input = '-'; # XXX: make a test case for this 5169f11ffb7Safresh1 } 5179f11ffb7Safresh1 } else { 518eac174f2Safresh1 $self->{Podfile} = $ARGV[0]; 5199f11ffb7Safresh1 $input = *ARGV; 5209f11ffb7Safresh1 } 521eac174f2Safresh1 return $input; 522eac174f2Safresh1} 5239f11ffb7Safresh1 524eac174f2Safresh1sub parse_input_for_podtree { 525eac174f2Safresh1 my ($self, $input) = @_; 5269f11ffb7Safresh1 # set options for input parser 527eac174f2Safresh1 my $input_parser = Pod::Simple::SimpleTree->new; 528eac174f2Safresh1 # Normalize whitespace indenting 529eac174f2Safresh1 $input_parser->strip_verbatim_indent(\&trim_leading_whitespace); 5309f11ffb7Safresh1 531eac174f2Safresh1 $input_parser->codes_in_verbatim(0); 532eac174f2Safresh1 $input_parser->accept_targets(qw(html HTML)); 533eac174f2Safresh1 $input_parser->no_errata_section(!$self->{Poderrors}); # note the inverse 5349f11ffb7Safresh1 535eac174f2Safresh1 warn "Converting input file $self->{Podfile}\n" if $self->{Verbose}; 536eac174f2Safresh1 my $podtree = $input_parser->parse_file($input)->root; 537eac174f2Safresh1 return $podtree; 538eac174f2Safresh1} 539eac174f2Safresh1 540eac174f2Safresh1sub set_Title_from_podtree { 541eac174f2Safresh1 my ($self, $podtree) = @_; 542eac174f2Safresh1 unless(defined $self->{Title}) { 5439f11ffb7Safresh1 if($podtree->[0] eq "Document" && ref($podtree->[2]) eq "ARRAY" && 5449f11ffb7Safresh1 $podtree->[2]->[0] eq "head1" && @{$podtree->[2]} == 3 && 5459f11ffb7Safresh1 ref($podtree->[2]->[2]) eq "" && $podtree->[2]->[2] eq "NAME" && 5469f11ffb7Safresh1 ref($podtree->[3]) eq "ARRAY" && $podtree->[3]->[0] eq "Para" && 5479f11ffb7Safresh1 @{$podtree->[3]} >= 3 && 5489f11ffb7Safresh1 !(grep { ref($_) ne "" } 5499f11ffb7Safresh1 @{$podtree->[3]}[2..$#{$podtree->[3]}]) && 5509f11ffb7Safresh1 (@$podtree == 4 || 5519f11ffb7Safresh1 (ref($podtree->[4]) eq "ARRAY" && 5529f11ffb7Safresh1 $podtree->[4]->[0] eq "head1"))) { 553eac174f2Safresh1 $self->{Title} = join("", @{$podtree->[3]}[2..$#{$podtree->[3]}]); 5549f11ffb7Safresh1 } 5559f11ffb7Safresh1 } 5569f11ffb7Safresh1 557eac174f2Safresh1 $self->{Title} //= ""; 558eac174f2Safresh1 $self->{Title} = html_escape($self->{Title}); 559eac174f2Safresh1 return $self; 560eac174f2Safresh1} 5619f11ffb7Safresh1 562eac174f2Safresh1sub refine_parser { 563eac174f2Safresh1 my ($self, $parser) = @_; 564898184e3Ssthen # We need to add this ourselves because we use our own header, not 565898184e3Ssthen # ::XHTML's header. We need to set $parser->backlink to linkify 566898184e3Ssthen # the =head1 directives 567eac174f2Safresh1 my $bodyid = $self->{Backlink} ? ' id="_podtop_"' : ''; 568898184e3Ssthen 569898184e3Ssthen my $csslink = ''; 5706fb12b70Safresh1 my $tdstyle = ' style="background-color: #cccccc; color: #000"'; 571898184e3Ssthen 572eac174f2Safresh1 if ($self->{Css}) { 573eac174f2Safresh1 $csslink = qq(\n<link rel="stylesheet" href="$self->{Css}" type="text/css" />); 574898184e3Ssthen $csslink =~ s,\\,/,g; 575898184e3Ssthen $csslink =~ s,(/.):,$1|,; 576898184e3Ssthen $tdstyle= ''; 577898184e3Ssthen } 578898184e3Ssthen 579898184e3Ssthen # header/footer block 580eac174f2Safresh1 my $block = $self->{Header} ? <<END_OF_BLOCK : ''; 581898184e3Ssthen<table border="0" width="100%" cellspacing="0" cellpadding="3"> 582898184e3Ssthen<tr><td class="_podblock_"$tdstyle valign="middle"> 583eac174f2Safresh1<big><strong><span class="_podblock_"> $self->{Title}</span></strong></big> 584898184e3Ssthen</td></tr> 585898184e3Ssthen</table> 586898184e3SsthenEND_OF_BLOCK 587898184e3Ssthen 588898184e3Ssthen # create own header/footer because of --header 589898184e3Ssthen $parser->html_header(<<"HTMLHEAD"); 590898184e3Ssthen<?xml version="1.0" ?> 591898184e3Ssthen<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 592898184e3Ssthen<html xmlns="http://www.w3.org/1999/xhtml"> 593898184e3Ssthen<head> 594eac174f2Safresh1<title>$self->{Title}</title>$csslink 595898184e3Ssthen<meta http-equiv="content-type" content="text/html; charset=utf-8" /> 596898184e3Ssthen<link rev="made" href="mailto:$Config{perladmin}" /> 597898184e3Ssthen</head> 598898184e3Ssthen 5996fb12b70Safresh1<body$bodyid> 600898184e3Ssthen$block 601898184e3SsthenHTMLHEAD 602898184e3Ssthen 603898184e3Ssthen $parser->html_footer(<<"HTMLFOOT"); 604898184e3Ssthen$block 605898184e3Ssthen</body> 606898184e3Ssthen 607898184e3Ssthen</html> 608898184e3SsthenHTMLFOOT 609eac174f2Safresh1 return $parser; 610eac174f2Safresh1} 611898184e3Ssthen 612eac174f2Safresh1# This sub duplicates the guts of Pod::Simple::FromTree. We could have 613eac174f2Safresh1# used that module, except that it would have been a non-core dependency. 614eac174f2Safresh1sub feed_tree_to_parser { 615eac174f2Safresh1 my($self, $parser, $tree) = @_; 616eac174f2Safresh1 if(ref($tree) eq "") { 617eac174f2Safresh1 $parser->_handle_text($tree); 618eac174f2Safresh1 } elsif(!($tree->[0] eq "X" && $parser->nix_X_codes)) { 619eac174f2Safresh1 $parser->_handle_element_start($tree->[0], $tree->[1]); 620eac174f2Safresh1 $self->feed_tree_to_parser($parser, $_) foreach @{$tree}[2..$#$tree]; 621eac174f2Safresh1 $parser->_handle_element_end($tree->[0]); 622eac174f2Safresh1 } 623eac174f2Safresh1} 624898184e3Ssthen 625eac174f2Safresh1sub write_file { 626eac174f2Safresh1 my $self = shift; 627eac174f2Safresh1 $self->{Htmlfile} = "-" unless $self->{Htmlfile}; # stdout 628898184e3Ssthen my $fhout; 629eac174f2Safresh1 if($self->{Htmlfile} and $self->{Htmlfile} ne '-') { 630eac174f2Safresh1 open $fhout, ">", $self->{Htmlfile} 631eac174f2Safresh1 or die "$0: cannot open $self->{Htmlfile} file for output: $!\n"; 632898184e3Ssthen } else { 633898184e3Ssthen open $fhout, ">-"; 634898184e3Ssthen } 63591f110e0Safresh1 binmode $fhout, ":utf8"; 636eac174f2Safresh1 print $fhout $self->{output}; 637eac174f2Safresh1 close $fhout or die "Failed to close $self->{Htmlfile}: $!"; 638eac174f2Safresh1 chmod 0644, $self->{Htmlfile} unless $self->{Htmlfile} eq '-'; 639898184e3Ssthen} 640898184e3Ssthen 641898184e3Ssthenpackage Pod::Simple::XHTML::LocalPodLinks; 642898184e3Ssthenuse strict; 643898184e3Ssthenuse warnings; 6446fb12b70Safresh1use parent 'Pod::Simple::XHTML'; 645898184e3Ssthen 646898184e3Ssthenuse File::Spec; 647898184e3Ssthenuse File::Spec::Unix; 648898184e3Ssthen 649898184e3Ssthen__PACKAGE__->_accessorize( 650898184e3Ssthen 'htmldir', 651898184e3Ssthen 'htmlfileurl', 652898184e3Ssthen 'htmlroot', 653898184e3Ssthen 'pages', # Page name => relative/path/to/page from root POD dir 654898184e3Ssthen 'quiet', 655898184e3Ssthen 'verbose', 656898184e3Ssthen); 657898184e3Ssthen 658898184e3Ssthensub resolve_pod_page_link { 659898184e3Ssthen my ($self, $to, $section) = @_; 660898184e3Ssthen 661898184e3Ssthen return undef unless defined $to || defined $section; 662898184e3Ssthen if (defined $section) { 663898184e3Ssthen $section = '#' . $self->idify($section, 1); 664898184e3Ssthen return $section unless defined $to; 665898184e3Ssthen } else { 666898184e3Ssthen $section = ''; 667898184e3Ssthen } 668898184e3Ssthen 669898184e3Ssthen my $path; # path to $to according to %Pages 670898184e3Ssthen unless (exists $self->pages->{$to}) { 671898184e3Ssthen # Try to find a POD that ends with $to and use that. 672898184e3Ssthen # e.g., given L<XHTML>, if there is no $Podpath/XHTML in %Pages, 673898184e3Ssthen # look for $Podpath/*/XHTML in %Pages, with * being any path, 674898184e3Ssthen # as a substitute (e.g., $Podpath/Pod/Simple/XHTML) 675898184e3Ssthen my @matches; 676898184e3Ssthen foreach my $modname (keys %{$self->pages}) { 677898184e3Ssthen push @matches, $modname if $modname =~ /::\Q$to\E\z/; 678898184e3Ssthen } 679898184e3Ssthen 68056d68f1eSafresh1 # make it look like a path instead of a namespace 68156d68f1eSafresh1 my $modloc = File::Spec->catfile(split(/::/, $to)); 68256d68f1eSafresh1 683898184e3Ssthen if ($#matches == -1) { 68456d68f1eSafresh1 warn "Cannot find file \"$modloc.*\" directly under podpath, " . 68556d68f1eSafresh1 "cannot find suitable replacement: link remains unresolved.\n" 68656d68f1eSafresh1 if $self->verbose; 687898184e3Ssthen return ''; 688898184e3Ssthen } elsif ($#matches == 0) { 689898184e3Ssthen $path = $self->pages->{$matches[0]}; 69056d68f1eSafresh1 my $matchloc = File::Spec->catfile(split(/::/, $path)); 69156d68f1eSafresh1 warn "Cannot find file \"$modloc.*\" directly under podpath, but ". 69256d68f1eSafresh1 "I did find \"$matchloc.*\", so I'll assume that is what you ". 69356d68f1eSafresh1 "meant to link to.\n" 69456d68f1eSafresh1 if $self->verbose; 695898184e3Ssthen } else { 696898184e3Ssthen # Use [-1] so newer (higher numbered) perl PODs are used 69756d68f1eSafresh1 # XXX currently, @matches isn't sorted so this is not true 698898184e3Ssthen $path = $self->pages->{$matches[-1]}; 69956d68f1eSafresh1 my $matchloc = File::Spec->catfile(split(/::/, $path)); 70056d68f1eSafresh1 warn "Cannot find file \"$modloc.*\" directly under podpath, but ". 70156d68f1eSafresh1 "I did find \"$matchloc.*\" (among others), so I'll use that " . 70256d68f1eSafresh1 "to resolve the link.\n" if $self->verbose; 703898184e3Ssthen } 704898184e3Ssthen } else { 705898184e3Ssthen $path = $self->pages->{$to}; 706898184e3Ssthen } 707898184e3Ssthen 708eac174f2Safresh1 my $url = File::Spec::Unix->catfile(Pod::Html::Util::unixify($self->htmlroot), 709898184e3Ssthen $path); 710898184e3Ssthen 711898184e3Ssthen if ($self->htmlfileurl ne '') { 712898184e3Ssthen # then $self->htmlroot eq '' (by definition of htmlfileurl) so 713898184e3Ssthen # $self->htmldir needs to be prepended to link to get the absolute path 714898184e3Ssthen # that will be relativized 715eac174f2Safresh1 $url = Pod::Html::Util::relativize_url( 716eac174f2Safresh1 File::Spec::Unix->catdir(Pod::Html::Util::unixify($self->htmldir), $url), 717898184e3Ssthen $self->htmlfileurl # already unixified 718898184e3Ssthen ); 719898184e3Ssthen } 720898184e3Ssthen 721898184e3Ssthen return $url . ".html$section"; 722898184e3Ssthen} 723898184e3Ssthen 724898184e3Ssthen1; 725