xref: /openbsd-src/gnu/usr.bin/perl/ext/Pod-Html/lib/Pod/Html.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
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_">&nbsp;$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