File Coverage

blib/lib/Pod/L10N/Html.pm
Criterion Covered Total %
statement 331 348 95.1
branch 118 150 78.6
condition 40 71 56.3
subroutine 38 38 100.0
pod 1 16 6.2
total 528 623 84.7


line stmt bran cond sub pod time code
1             package Pod::L10N::Html;
2 21     21   2605461 use strict;
  21         64  
  21         902  
3 21     21   117 use Exporter 'import';
  21         137  
  21         1596  
4              
5             our $VERSION = '1.10';
6             $VERSION = eval $VERSION;
7             our @EXPORT = qw(pod2htmll10n);
8              
9 21     21   126 use Config;
  21         59  
  21         1114  
10 21     21   122 use Cwd;
  21         84  
  21         1576  
11 21     21   135 use File::Basename;
  21         49  
  21         2051  
12 21     21   137 use File::Spec;
  21         68  
  21         733  
13 21     21   14242 use Pod::Simple::Search;
  21         176578  
  21         858  
14 21     21   12507 use Pod::Simple::SimpleTree ();
  21         1105487  
  21         1059  
15 21         2649 use Pod::L10N::Html::Util qw(
16             html_escape
17             process_command_line
18             trim_leading_whitespace
19             unixify
20             usage
21             htmlify
22             anchorify
23             relativize_url
24 21     21   13606 );
  21         81  
25 21     21   158 use locale; # make \w work right in non-ASCII lands
  21         43  
  21         127  
26              
27 21     21   13360 use Pod::L10N::Model;
  21         25372  
  21         94715  
28              
29             =head1 NAME
30              
31             Pod::L10N::Html - module to convert pod files to HTML with L10N
32              
33             =head1 SYNOPSIS
34              
35             use Pod::L10N::Html;
36             pod2htmll10n([options]);
37              
38             =head1 DESCRIPTION
39              
40             Converts files from pod format (see L) to HTML format.
41              
42             Its API is fully compatible with L.
43              
44             If input files support L extended format,
45             Pod::L10N::Html do some more works to print translated text pretty well.
46              
47             =head1 ADDITIONAL FEATURES
48              
49             Additional features from L 1.33 are:
50              
51             =over
52              
53             =item *
54              
55             Support L extended format.
56              
57             =back
58              
59             =head1 FUNCTIONS
60              
61             =head2 pod2htmll10n
62              
63             pod2htmll10n("pod2htmll10n",
64             "--podpath=lib:ext:pod:vms",
65             "--podroot=/usr/src/perl",
66             "--htmlroot=/perl/nmanual",
67             "--recurse",
68             "--infile=foo.pod",
69             "--outfile=/perl/nmanual/foo.html");
70              
71             See L for details.
72              
73             =head1 ENVIRONMENT
74              
75             Uses C<$Config{pod2html}> to setup default options.
76              
77             =head1 AUTHOR
78              
79             C is based on L Version 1.33 written by
80             Marc Green, Emarcgreen@cpan.orgE.
81              
82             Modification to C is written by SHIRAKATA Kentaro,
83             Eargrath@cpan.orgE.
84              
85             =head1 SEE ALSO
86              
87             L, L, L
88              
89             =head1 COPYRIGHT
90              
91             This program is distributed under the Artistic License.
92              
93             =cut
94              
95             sub new {
96 31     31 0 190 my $class = shift;
97 31         186 return bless {}, $class;
98             }
99              
100             sub pod2htmll10n {
101 31     31 1 730499 local(@ARGV) = @_;
102 31         131 local $_;
103              
104 31         832 my $self = Pod::L10N::Html->new();
105 31         320 $self->init_globals();
106              
107 31         318 my $opts = process_command_line;
108 29         319 $self->process_options($opts);
109              
110 29         356 $self->refine_globals();
111              
112             # load or generate/cache %Pages
113 29 100       277 unless ($self->get_cache()) {
114             # generate %Pages
115             #%Pages = $self->generate_cache(\%Pages);
116 27         174 $self->generate_cache($self->{Pages});
117             }
118 29         466 my $input = $self->identify_input();
119              
120 29         344 my ($lcontent, $lencoding) = arrange($self->{Podfile});
121 29 100       159 if(!defined $lencoding){
122 26         163 $lencoding = 'utf-8';
123             }
124 29         285 $self->{Lcontent} = $lcontent;
125 29         154 $self->{Lencoding} = $lencoding;
126              
127 29         222 my $podtree = $self->parse_input_for_podtree($input);
128 29         241 $self->set_Title_from_podtree($podtree);
129              
130             # set options for the HTML generator
131 29         1209 my $parser = Pod::L10N::Html::LocalPodLinks->new();
132 29         6935 $parser->codes_in_verbatim(0);
133 29         430 $parser->anchor_items(1); # the old Pod::Html always did
134 29         431 $parser->backlink($self->{Backlink}); # linkify =head1 directives
135 29         422 $parser->force_title($self->{Title});
136 29         355 $parser->htmldir($self->{Htmldir});
137 29         454 $parser->htmlfileurl($self->{Htmlfileurl});
138 29         384 $parser->htmlroot($self->{Htmlroot});
139 29         575 $parser->index($self->{Doindex});
140             # still need as parse twice
141 29         372 $parser->no_errata_section(!$self->{Poderrors}); # note the inverse
142 29         476 $parser->output_string(\$self->{output}); # written to file later
143             #$parser->pages(\%Pages);
144 29         3346 $parser->pages($self->{Pages});
145 29         434 $parser->quiet($self->{Quiet});
146 29         341 $parser->verbose($self->{Verbose});
147              
148             # $parser->html_charset('UTF-8');
149 29         411 $parser->html_encode_chars('&<>">');
150             # $parser->html_header_tags('');
151              
152 29         339 $parser = $self->refine_parser($parser);
153 29         164 $self->feed_tree_to_parser($parser, $podtree);
154 29         9677 $self->write_file();
155             }
156              
157             sub init_globals {
158 31     31 0 132 my $self = shift;
159 31         666 $self->{Cachedir} = "."; # The directory to which directory caches
160             # will be written.
161              
162 31         183 $self->{Dircache} = "pod2htmd.tmp";
163              
164 31         389 $self->{Htmlroot} = "/"; # http-server base directory from which all
165             # relative paths in $podpath stem.
166 31         194 $self->{Htmldir} = ""; # The directory to which the html pages
167             # will (eventually) be written.
168 31         199 $self->{Htmlfile} = ""; # write to stdout by default
169 31         149 $self->{Htmlfileurl} = ""; # The url that other files would use to
170             # refer to this file. This is only used
171             # to make relative urls that point to
172             # other files.
173              
174 31         230 $self->{Poderrors} = 1;
175 31         170 $self->{Podfile} = ""; # read from stdin by default
176 31         137 $self->{Podpath} = []; # list of directories containing library pods.
177 31         407 $self->{Podroot} = $self->{Curdir} = File::Spec->curdir;
178             # filesystem base directory from which all
179             # relative paths in $podpath stem.
180 31         121 $self->{Css} = ''; # Cascading style sheet
181 31         134 $self->{Recurse} = 1; # recurse on subdirectories in $podpath.
182 31         112 $self->{Quiet} = 0; # not quiet by default
183 31         325 $self->{Verbose} = 0; # not verbose by default
184 31         99 $self->{Doindex} = 1; # non-zero if we should generate an index
185 31         147 $self->{Backlink} = 0; # no backlinks added by default
186 31         233 $self->{Header} = 0; # produce block header/footer
187 31         137 $self->{Title} = undef; # title to give the pod(s)
188 31         102 $self->{Saved_Cache_Key} = '';
189 31         455 $self->{Pages} = {};
190 31         96 return $self;
191             }
192              
193             sub process_options {
194 29     29 0 93 my ($self, $opts) = @_;
195              
196             $self->{Podpath} = (defined $opts->{podpath})
197 29 100       280 ? [ split(":", $opts->{podpath}) ]
198             : [];
199              
200 29 100       172 $self->{Backlink} = $opts->{backlink} if defined $opts->{backlink};
201 29 100       127 $self->{Cachedir} = unixify($opts->{cachedir}) if defined $opts->{cachedir};
202 29 100       138 $self->{Css} = $opts->{css} if defined $opts->{css};
203 29 100       134 $self->{Header} = $opts->{header} if defined $opts->{header};
204 29 100       139 $self->{Htmldir} = unixify($opts->{htmldir}) if defined $opts->{htmldir};
205 29 100       137 $self->{Htmlroot} = unixify($opts->{htmlroot}) if defined $opts->{htmlroot};
206 29 100       114 $self->{Doindex} = $opts->{index} if defined $opts->{index};
207 29 50       184 $self->{Podfile} = unixify($opts->{infile}) if defined $opts->{infile};
208 29 50       165 $self->{Htmlfile} = unixify($opts->{outfile}) if defined $opts->{outfile};
209 29 100       127 $self->{Poderrors} = $opts->{poderrors} if defined $opts->{poderrors};
210 29 100       158 $self->{Podroot} = unixify($opts->{podroot}) if defined $opts->{podroot};
211 29 100       141 $self->{Quiet} = $opts->{quiet} if defined $opts->{quiet};
212 29 100       111 $self->{Recurse} = $opts->{recurse} if defined $opts->{recurse};
213 29 100       131 $self->{Title} = $opts->{title} if defined $opts->{title};
214 29 100       100 $self->{Verbose} = $opts->{verbose} if defined $opts->{verbose};
215              
216             warn "Flushing directory caches\n"
217 29 50 66     123 if $opts->{verbose} && defined $opts->{flush};
218 29         95 $self->{Dircache} = "$self->{Cachedir}/pod2htmd.tmp";
219 29 50       96 if (defined $opts->{flush}) {
220 0         0 1 while unlink($self->{Dircache});
221             }
222 29         71 return $self;
223             }
224              
225             sub refine_globals {
226 29     29 0 68 my $self = shift;
227              
228             # prevent '//' in urls
229 29 100       183 $self->{Htmlroot} = "" if $self->{Htmlroot} eq "/";
230 29         188 $self->{Htmldir} =~ s#/\z##;
231              
232 29 100 66     514 if ( $self->{Htmlroot} eq ''
      100        
      100        
233             && defined( $self->{Htmldir} )
234             && $self->{Htmldir} ne ''
235             && substr( $self->{Htmlfile}, 0, length( $self->{Htmldir} ) ) eq $self->{Htmldir}
236             ) {
237             # Set the 'base' url for this file, so that we can use it
238             # as the location from which to calculate relative links
239             # to other files. If this is '', then absolute links will
240             # be used throughout.
241             #$self->{Htmlfileurl} = "$self->{Htmldir}/" . substr( $self->{Htmlfile}, length( $self->{Htmldir} ) + 1);
242             # Is the above not just "$self->{Htmlfileurl} = $self->{Htmlfile}"?
243 11         81 $self->{Htmlfileurl} = unixify($self->{Htmlfile});
244             }
245 29         64 return $self;
246             }
247              
248             sub generate_cache {
249 27     27 0 59 my $self = shift;
250 27         322 my $pwd = getcwd();
251 27 50       370 chdir($self->{Podroot}) ||
252             die "$0: error changing to directory $self->{Podroot}: $!\n";
253              
254             # find all pod modules/pages in podpath, store in %Pages
255             # - inc(0): do not prepend directories in @INC to search list;
256             # limit search to those in @{$self->{Podpath}}
257             # - verbose: report (via 'warn') what search is doing
258             # - laborious: to allow '.' in dirnames (e.g., /usr/share/perl/5.14.1)
259             # - recurse: go into subdirectories
260             # - survey: search for POD files in PodPath
261             my ($name2path, $path2name) =
262             Pod::Simple::Search->new->inc(0)->verbose($self->{Verbose})->laborious(1)
263 27         722 ->recurse($self->{Recurse})->survey(@{$self->{Podpath}});
  27         5735  
264             # remove Podroot and extension from each file
265 27         317667 for my $k (keys %{$name2path}) {
  27         359  
266 354         1173 $self->{Pages}{$k} = _transform($self, $name2path->{$k});
267             }
268              
269 27 50       721 chdir($pwd) || die "$0: error changing to directory $pwd: $!\n";
270              
271             # cache the directory list for later use
272 27 100       245 warn "caching directories for later use\n" if $self->{Verbose};
273             open my $cache, '>', $self->{Dircache}
274 27 50       7841 or die "$0: error open $self->{Dircache} for writing: $!\n";
275              
276 27         142 print $cache join(":", @{$self->{Podpath}}) . "\n$self->{Podroot}\n";
  27         361  
277 27   33     373 my $_updirs_only = ($self->{Podroot} =~ /\.\./) && !($self->{Podroot} =~ /[^\.\\\/]/);
278 27         106 foreach my $key (keys %{$self->{Pages}}) {
  27         242  
279 354 50       701 if($_updirs_only) {
280 0         0 my $_dirlevel = $self->{Podroot};
281 0         0 while($_dirlevel =~ /\.\./) {
282 0         0 $_dirlevel =~ s/\.\.//;
283             # Assume $Pagesref->{$key} has '/' separators (html dir separators).
284 0         0 $self->{Pages}->{$key} =~ s/^[\w\s\-\.]+\///;
285             }
286             }
287 354         729 print $cache "$key $self->{Pages}->{$key}\n";
288             }
289 27 50       2323 close $cache or die "error closing $self->{Dircache}: $!";
290             }
291              
292             sub _transform {
293 354     354   739 my ($self, $v) = @_;
294             $v = $self->{Podroot} eq File::Spec->curdir
295             ? File::Spec->abs2rel($v)
296             : File::Spec->abs2rel($v,
297 354 100       21465 File::Spec->canonpath($self->{Podroot}));
298              
299             # Convert path to unix style path
300 354         1688 $v = unixify($v);
301              
302 354         9452 my ($file, $dir) = fileparse($v, qr/\.[^.]*/); # strip .ext
303 354         1971 return $dir.$file;
304             }
305              
306             sub get_cache {
307 29     29 0 73 my $self = shift;
308              
309             # A first-level cache:
310             # Don't bother reading the cache files if they still apply
311             # and haven't changed since we last read them.
312              
313 29         156 my $this_cache_key = $self->cache_key();
314 29 50 33     251 return 1 if $self->{Saved_Cache_Key} and $this_cache_key eq $self->{Saved_Cache_Key};
315 29         245 $self->{Saved_Cache_Key} = $this_cache_key;
316              
317             # load the cache of %Pages if possible. $tests will be
318             # non-zero if successful.
319 29         162 my $tests = 0;
320 29 100       367 if (-f $self->{Dircache}) {
321 2 50       9 warn "scanning for directory cache\n" if $self->{Verbose};
322 2         10 $tests = $self->load_cache();
323             }
324              
325 29         139 return $tests;
326             }
327              
328             sub cache_key {
329 29     29 0 59 my $self = shift;
330             return join('!',
331             $self->{Dircache},
332             $self->{Recurse},
333 29         5223 @{$self->{Podpath}},
334             $self->{Podroot},
335 29         87 stat($self->{Dircache}),
336             );
337             }
338              
339             #
340             # load_cache - tries to find if the cache stored in $dircache is a valid
341             # cache of %Pages. if so, it loads them and returns a non-zero value.
342             #
343             sub load_cache {
344 2     2 0 6 my $self = shift;
345 2         3 my $tests = 0;
346 2         5 local $_;
347              
348 2 50       7 warn "scanning for directory cache\n" if $self->{Verbose};
349 2 50       105 open(my $cachefh, '<', $self->{Dircache}) ||
350             die "$0: error opening $self->{Dircache} for reading: $!\n";
351 2         12 $/ = "\n";
352              
353             # is it the same podpath?
354 2         84 $_ = <$cachefh>;
355 2         7 chomp($_);
356 2 50       5 $tests++ if (join(":", @{$self->{Podpath}}) eq $_);
  2         12  
357              
358             # is it the same podroot?
359 2         7 $_ = <$cachefh>;
360 2         4 chomp($_);
361 2 50       8 $tests++ if ($self->{Podroot} eq $_);
362              
363             # load the cache if its good
364 2 50       7 if ($tests != 2) {
365 0         0 close($cachefh);
366 0         0 return 0;
367             }
368              
369 2 50       7 warn "loading directory cache\n" if $self->{Verbose};
370 2         19 while (<$cachefh>) {
371 0         0 /(.*?) (.*)$/;
372 0         0 $self->{Pages}->{$1} = $2;
373             }
374              
375 2         26 close($cachefh);
376 2         41 return 1;
377             }
378              
379             sub identify_input {
380 29     29 0 92 my $self = shift;
381 29         83 my $input;
382 29 50 33     231 unless (@ARGV && $ARGV[0]) {
383 29 50 33     673 if ($self->{Podfile} and $self->{Podfile} ne '-') {
384 29         117 $input = $self->{Podfile};
385             } else {
386 0         0 $input = '-'; # XXX: make a test case for this
387             }
388             } else {
389 0         0 $self->{Podfile} = $ARGV[0];
390 0         0 $input = *ARGV;
391             }
392 29         115 return $input;
393             }
394              
395             sub parse_input_for_podtree {
396 29     29 0 108 my ($self, $input) = @_;
397             # set options for input parser
398 29         4885 my $input_parser = Pod::Simple::SimpleTree->new;
399             # Normalize whitespace indenting
400 29         4754 $input_parser->strip_verbatim_indent(\&trim_leading_whitespace);
401              
402 29         482 $input_parser->codes_in_verbatim(0);
403 29         414 $input_parser->accept_targets(qw(html HTML));
404 29         1775 $input_parser->no_errata_section(!$self->{Poderrors}); # note the inverse
405              
406 29 100       331 warn "Converting input file $self->{Podfile}\n" if $self->{Verbose};
407 29         333 my $podtree = $input_parser->parse_string_document($self->{Lcontent})->root;
408 29         221902 return $podtree;
409             }
410              
411             sub set_Title_from_podtree {
412 29     29 0 96 my ($self, $podtree) = @_;
413 29 100       170 unless(defined $self->{Title}) {
414 22 100 33     494 if($podtree->[0] eq "Document" && ref($podtree->[2]) eq "ARRAY" &&
      33        
      33        
      33        
      66        
      66        
      66        
      33        
      33        
      100        
      66        
415 22         454 $podtree->[2]->[0] eq "head1" && @{$podtree->[2]} == 3 &&
416             ref($podtree->[2]->[2]) eq "" && $podtree->[2]->[2] eq "NAME" &&
417             ref($podtree->[3]) eq "ARRAY" && $podtree->[3]->[0] eq "Para" &&
418 21         169 @{$podtree->[3]} >= 3 &&
419 23         367 !(grep { ref($_) ne "" }
420 21         140 @{$podtree->[3]}[2..$#{$podtree->[3]}]) &&
  21         94  
421             (@$podtree == 4 ||
422             (ref($podtree->[4]) eq "ARRAY" &&
423             $podtree->[4]->[0] eq "head1"))) {
424 19         58 $self->{Title} = join("", @{$podtree->[3]}[2..$#{$podtree->[3]}]);
  19         89  
  19         50  
425             }
426             }
427              
428 29   100     158 $self->{Title} //= "";
429 29         277 $self->{Title} = html_escape($self->{Title});
430 29         67 return $self;
431             }
432              
433             sub refine_parser {
434 29     29 0 123 my ($self, $parser) = @_;
435             # We need to add this ourselves because we use our own header, not
436             # ::XHTML's header. We need to set $parser->backlink to linkify
437             # the =head1 directives
438 29 100       201 my $bodyid = $self->{Backlink} ? ' id="_podtop_"' : '';
439              
440 29         74 my $csslink = '';
441 29         94 my $tdstyle = ' style="background-color: #cccccc; color: #000"';
442              
443 29 100       118 if ($self->{Css}) {
444 4         12 $csslink = qq(\n);
445 4         14 $csslink =~ s,\\,/,g;
446 4         28 $csslink =~ s,(/.):,$1|,;
447             #/ # (for VSCode highlighter)
448 4         27 $tdstyle= '';
449             }
450              
451             # header/footer block
452 29 100       168 my $block = $self->{Header} ? <
453            
454            
455              $self->{Title}
456            
457            
458             END_OF_BLOCK
459              
460             # create own header/footer because of --header
461 29         3687 $parser->html_header(<<"HTMLHEAD");
462            
463            
464            
465            
466             $self->{Title}$csslink
467            
468            
469            
470              
471            
472             $block
473             HTMLHEAD
474              
475 29         470 $parser->html_footer(<<"HTMLFOOT");
476             $block
477            
478              
479            
480             HTMLFOOT
481 29         218 return $parser;
482             }
483              
484             # This sub duplicates the guts of Pod::Simple::FromTree. We could have
485             # used that module, except that it would have been a non-core dependency.
486             sub feed_tree_to_parser {
487 1192     1192 0 25415 my($self, $parser, $tree) = @_;
488 1192 100 66     3866 if(ref($tree) eq "") {
    100          
489 652         1985 $parser->_handle_text($tree);
490             } elsif(!($tree->[0] eq "X" && $parser->nix_X_codes)) {
491 528         2016 $parser->_handle_element_start($tree->[0], $tree->[1]);
492 528         14952 $self->feed_tree_to_parser($parser, $_) foreach @{$tree}[2..$#$tree];
  528         2005  
493 528         30751 $parser->_handle_element_end($tree->[0]);
494             }
495             }
496              
497             sub write_file {
498 29     29 0 109 my $self = shift;
499 29 50       145 $self->{Htmlfile} = "-" unless $self->{Htmlfile}; # stdout
500 29         105 my $fhout;
501 29 50 33     315 if($self->{Htmlfile} and $self->{Htmlfile} ne '-') {
502             open $fhout, ">", $self->{Htmlfile}
503 29 50       6271 or die "$0: cannot open $self->{Htmlfile} file for output: $!\n";
504             } else {
505 0         0 open $fhout, ">-";
506             }
507 29     20   1825 binmode $fhout, ":encoding($self->{Lencoding})";
  20         29140  
  20         434  
  20         1324  
508 29         37725 print $fhout $self->{output};
509 29 50       2324 close $fhout or die "Failed to close $self->{Htmlfile}: $!";
510 29 50       7309 chmod 0644, $self->{Htmlfile} unless $self->{Htmlfile} eq '-';
511             }
512              
513             sub arrange {
514 29     29 0 96 my $fn = shift;
515 29         179 my $base;
516             my $ret;
517 29         0 my $encoding;
518              
519 29         349 $base = Pod::L10N::Model::decode_file($fn);
520              
521 29         11107 for (@$base){
522 362         2770 my($o, $t) = @$_;
523 362 100       1140 if($o =~ /^=encoding (.+)/){
524 3         15 $encoding = $1;
525 3         8 $ret .= $o . "\n\n";
526 3         8 next;
527             }
528 359 100       790 if($o =~ /^=/){
529 143 100       285 if(defined $t){
530 4         29 $t =~ /\((.+)\)/;
531 4         34 $ret .= $o . '@@@@@@@@@@' . $1;
532             } else {
533 139         264 $ret .= $o;
534             }
535             } else {
536 216 50       340 if(defined $t){
537 0         0 $ret .= $t;
538             } else {
539 216         374 $ret .= $o;
540             }
541             }
542 359         595 $ret .= "\n\n";
543             }
544              
545 29         240 return ($ret, $encoding);
546             }
547              
548             package Pod::L10N::Html::LocalPodLinks;
549 21     21   193 use strict;
  21         53  
  21         905  
550 21     21   213 use warnings;
  21         74  
  21         1666  
551 21     21   11739 use parent 'Pod::Simple::XHTML';
  21         6880  
  21         198  
552              
553 21     21   1732 use File::Spec;
  21         44  
  21         603  
554 21     21   102 use File::Spec::Unix;
  21         36  
  21         35380  
555              
556             __PACKAGE__->_accessorize(
557             'htmldir',
558             'htmlfileurl',
559             'htmlroot',
560             'pages', # Page name => relative/path/to/page from root POD dir
561             'quiet',
562             'verbose',
563             );
564              
565             sub idify {
566 163     163   325 my ($self, $t, $not_unique) = @_;
567 163         374 for ($t) {
568 163         640 s/<[^>]+>//g; # Strip HTML.
569 163         1542 s/&[^;]+;//g; # Strip entities.
570 163         1592 s/^\s+//; s/\s+$//; # Strip white space.
  163         1441  
571 163         1532 s/^([^a-zA-Z]+)$/pod$1/; # Prepend "pod" if no valid chars.
572 163         1318 s/^[^a-zA-Z]+//; # First char must be a letter.
573 163         1631 s/[^-a-zA-Z0-9_:.]+/-/g; # All other chars must be valid.
574 163         2199 s/[-:.]+$//; # Strip trailing punctuation.
575             }
576 163 100       873 return $t if $not_unique;
577 85         279 my $i = '';
578 85         596 $i++ while $self->{ids}{"$t$i"}++;
579 85         561 return "$t$i";
580             }
581              
582             sub resolve_pod_page_link {
583 123     123   2080 my ($self, $to, $section) = @_;
584              
585 123 50 66     486 return undef unless defined $to || defined $section;
586 123 100       271 if (defined $section) {
587 78         177 $section = '#' . $self->idify($section, 1);
588 78 100       541 return $section unless defined $to;
589             } else {
590 45         101 $section = '';
591             }
592              
593 76         152 my $path; # path to $to according to %Pages
594 76 100       263 unless (exists $self->pages->{$to}) {
595             # Try to find a POD that ends with $to and use that.
596             # e.g., given L, if there is no $Podpath/XHTML in %Pages,
597             # look for $Podpath/*/XHTML in %Pages, with * being any path,
598             # as a substitute (e.g., $Podpath/Pod/Simple/XHTML)
599 33         891 my @matches;
600 33         117 foreach my $modname (keys %{$self->pages}) {
  33         135  
601 574 100       10266 push @matches, $modname if $modname =~ /::\Q$to\E\z/;
602             }
603              
604             # make it look like a path instead of a namespace
605 33         587 my $modloc = File::Spec->catfile(split(/::/, $to));
606              
607 33 100       980 if ($#matches == -1) {
    50          
608 25 100       136 warn "Cannot find file \"$modloc.*\" directly under podpath, " .
609             "cannot find suitable replacement: link remains unresolved.\n"
610             if $self->verbose;
611 25         356 return '';
612             } elsif ($#matches == 0) {
613 8         61 $path = $self->pages->{$matches[0]};
614 8         96 my $matchloc = File::Spec->catfile(split(/::/, $path));
615 8 50       31 warn "Cannot find file \"$modloc.*\" directly under podpath, but ".
616             "I did find \"$matchloc.*\", so I'll assume that is what you ".
617             "meant to link to.\n"
618             if $self->verbose;
619             } else {
620             # Use [-1] so newer (higher numbered) perl PODs are used
621             # XXX currently, @matches isn't sorted so this is not true
622 0         0 $path = $self->pages->{$matches[-1]};
623 0         0 my $matchloc = File::Spec->catfile(split(/::/, $path));
624 0 0       0 warn "Cannot find file \"$modloc.*\" directly under podpath, but ".
625             "I did find \"$matchloc.*\" (among others), so I'll use that " .
626             "to resolve the link.\n" if $self->verbose;
627             }
628             } else {
629 43         1222 $path = $self->pages->{$to};
630             }
631              
632 51         1119 my $url = File::Spec::Unix->catfile(Pod::L10N::Html::Util::unixify($self->htmlroot),
633             $path);
634              
635 51 100       176 if ($self->htmlfileurl ne '') {
636             # then $self->htmlroot eq '' (by definition of htmlfileurl) so
637             # $self->htmldir needs to be prepended to link to get the absolute path
638             # that will be relativized
639 25         231 $url = Pod::L10N::Html::Util::relativize_url(
640             File::Spec::Unix->catdir(Pod::L10N::Html::Util::unixify($self->htmldir), $url),
641             $self->htmlfileurl # already unixified
642             );
643             }
644              
645 51         465 return $url . ".html$section";
646             }
647              
648             sub _end_head {
649 69     69   1485 my $h = delete $_[0]{in_head};
650              
651 69         383 my $add = $_[0]->html_h_level;
652 69 50       633 $add = 1 unless defined $add;
653 69         167 $h += $add - 1;
654              
655 69         465 my ($orig, $trans) = split /@@@@@@@@@@/, $_[0]{scratch};
656 69 100       207 if(!defined $trans){
657 67         134 $trans = $orig;
658             }
659 69         291 my $id = $_[0]->idify($orig);
660 69         184 my $text = $trans;
661 69 100 66     259 $_[0]{'scratch'} = $_[0]->backlink && ($h - $add == 0)
662             # backlinks enabled && =head1
663             ? qq{$text}
664             : qq{$text};
665 69         985 $_[0]->emit;
666 69         5168 push @{ $_[0]{'to_index'} }, [$h, $id, $text];
  69         729  
667             }
668              
669             sub end_item_text {
670 16     16   257 my ($orig, $trans) = split /@@@@@@@@@@/, $_[0]{scratch};
671 16 100       80 if(!defined $trans){
672 14         34 $trans = $orig;
673             }
674              
675             # idify and anchor =item content if wanted
676 16 50       95 my $dt_id = $_[0]{'anchor_items'}
677             ? ' id="'. $_[0]->idify($orig) .'"'
678             : '';
679              
680             # reset scratch
681 16         36 my $text = $trans;
682 16         44 $_[0]{'scratch'} = '';
683              
684 16 100       139 if ($_[0]{'in_dd'}[ $_[0]{'dl_level'} ]) {
685 6         11 $_[0]{'scratch'} = "\n";
686 6         12 $_[0]{'in_dd'}[ $_[0]{'dl_level'} ] = 0;
687             }
688              
689 16         98 $_[0]{'scratch'} .= qq{$text\n
};
690 16         44 $_[0]{'in_dd'}[ $_[0]{'dl_level'} ] = 1;
691 16         76 $_[0]->emit;
692             }
693              
694             1;