use
vars
qw($VERSION @ISA @EXPORT @EXPORT_OK)
;
$VERSION
= 1.15_02;
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw(pod2html htmlify)
;
@EXPORT_OK
=
qw(anchorify)
;
my
$Cachedir
;
my
$Dircache
;
my
(
$Htmlroot
,
$Htmldir
,
$Htmlfile
,
$Htmlfileurl
);
my
(
$Podfile
,
@Podpath
,
$Podroot
);
my
$Poderrors
;
my
$Css
;
my
$Recurse
;
my
$Quiet
;
my
$Verbose
;
my
$Doindex
;
my
$Backlink
;
my
(
$Title
,
$Header
);
my
%Pages
= ();
my
$Curdir
= File::Spec->curdir;
init_globals();
sub
init_globals {
$Cachedir
=
"."
;
$Dircache
=
"pod2htmd.tmp"
;
$Htmlroot
=
"/"
;
$Htmldir
=
""
;
$Htmlfile
=
""
;
$Htmlfileurl
=
""
;
$Poderrors
= 1;
$Podfile
=
""
;
@Podpath
= ();
$Podroot
=
$Curdir
;
$Css
=
''
;
$Recurse
= 1;
$Quiet
= 0;
$Verbose
= 0;
$Doindex
= 1;
$Backlink
= 0;
$Header
= 0;
$Title
=
''
;
}
sub
pod2html {
local
(
@ARGV
) =
@_
;
local
$_
;
init_globals();
parse_command_line();
$Htmlroot
=
""
if
$Htmlroot
eq
"/"
;
$Htmldir
=~ s
if
(
$Htmlroot
eq
''
&&
defined
(
$Htmldir
)
&&
$Htmldir
ne
''
&&
substr
(
$Htmlfile
, 0,
length
(
$Htmldir
) ) eq
$Htmldir
) {
$Htmlfileurl
= Pod::Html::_unixify(
$Htmlfile
);
}
unless
(get_cache(
$Dircache
, \
@Podpath
,
$Podroot
,
$Recurse
)) {
my
$pwd
= getcwd();
chdir
(
$Podroot
) ||
die
"$0: error changing to directory $Podroot: $!\n"
;
Pod::Simple::Search->new->inc(0)->verbose(
$Verbose
)->laborious(1)
->callback(\
&_save_page
)->recurse(
$Recurse
)->survey(
@Podpath
);
chdir
(
$pwd
) ||
die
"$0: error changing to directory $pwd: $!\n"
;
warn
"caching directories for later use\n"
if
$Verbose
;
open
my
$cache
,
'>'
,
$Dircache
or
die
"$0: error open $Dircache for writing: $!\n"
;
print
$cache
join
(
":"
,
@Podpath
) .
"\n$Podroot\n"
;
my
$_updirs_only
= (
$Podroot
=~ /\.\./) && !(
$Podroot
=~ /[^\.\\\/]/);
foreach
my
$key
(
keys
%Pages
) {
if
(
$_updirs_only
) {
my
$_dirlevel
=
$Podroot
;
while
(
$_dirlevel
=~ /\.\./) {
$_dirlevel
=~ s/\.\.//;
$Pages
{
$key
} =~ s/^[\w\s\-\.]+\///;
}
}
print
$cache
"$key $Pages{$key}\n"
;
}
close
$cache
or
die
"error closing $Dircache: $!"
;
}
my
$parser
= Pod::Simple::XHTML::LocalPodLinks->new();
$parser
->codes_in_verbatim(0);
$parser
->anchor_items(1);
$parser
->backlink(
$Backlink
);
$parser
->htmldir(
$Htmldir
);
$parser
->htmlfileurl(
$Htmlfileurl
);
$parser
->htmlroot(
$Htmlroot
);
$parser
->
index
(
$Doindex
);
$parser
->no_errata_section(!
$Poderrors
);
$parser
->output_string(\
my
$output
);
$parser
->pages(\
%Pages
);
$parser
->quiet(
$Quiet
);
$parser
->verbose(
$Verbose
);
$Title
= html_escape(
$Title
);
my
$bodyid
=
$Backlink
?
' id="_podtop_"'
:
''
;
my
$csslink
=
''
;
my
$bodystyle
=
' style="background-color: white"'
;
my
$tdstyle
=
' style="background-color: #cccccc"'
;
if
(
$Css
) {
$csslink
=
qq(\n<link rel="stylesheet" href="$Css" type="text/css" />)
;
$csslink
=~ s,\\,/,g;
$csslink
=~ s,(/.):,$1|,;
$bodystyle
=
''
;
$tdstyle
=
''
;
}
my
$block
=
$Header
?
<<END_OF_BLOCK : '';
<table border="0" width="100%" cellspacing="0" cellpadding="3">
<tr><td class="_podblock_"$tdstyle valign="middle">
<big><strong><span class="_podblock_"> $Title</span></strong></big>
</td></tr>
</table>
END_OF_BLOCK
$parser
->html_header(
<<"HTMLHEAD");
<?xml version="1.0" ?>
<head>
<title>$Title</title>$csslink
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rev="made" href="mailto:$Config{perladmin}" />
</head>
<body$bodyid$bodystyle>
$block
HTMLHEAD
$parser
->html_footer(
<<"HTMLFOOT");
$block
</body>
</html>
HTMLFOOT
my
$input
;
unless
(
@ARGV
&&
$ARGV
[0]) {
if
(
$Podfile
and
$Podfile
ne
'-'
) {
$input
=
$Podfile
;
}
else
{
$input
=
'-'
;
}
}
else
{
$Podfile
=
$ARGV
[0];
$input
=
*ARGV
;
}
warn
"Converting input file $Podfile\n"
if
$Verbose
;
$parser
->parse_file(
$input
);
$Htmlfile
=
"-"
unless
$Htmlfile
;
my
$fhout
;
if
(
$Htmlfile
and
$Htmlfile
ne
'-'
) {
open
$fhout
,
">"
,
$Htmlfile
or
die
"$0: cannot open $Htmlfile file for output: $!\n"
;
}
else
{
open
$fhout
,
">-"
;
}
print
$fhout
$output
;
close
$fhout
or
die
"Failed to close $Htmlfile: $!"
;
chmod
0644,
$Htmlfile
unless
$Htmlfile
eq
'-'
;
}
sub
usage {
my
$podfile
=
shift
;
warn
"$0: $podfile: @_\n"
if
@_
;
die
<<END_OF_USAGE;
Usage: $0 --help --htmlroot=<name> --infile=<name> --outfile=<name>
--podpath=<name>:...:<name> --podroot=<name> --cachedir=<name>
--recurse --verbose --index --norecurse --noindex
--[no]backlink - turn =head1 directives into links pointing to the top of
the page (off by default).
--cachedir - directory for the directory cache files.
--css - stylesheet URL
--flush - flushes the directory cache.
--[no]header - produce block header/footer (default is no headers).
--help - prints this message.
--htmldir - directory for resulting HTML files.
--htmlroot - http-server base directory from which all relative paths
in podpath stem (default is /).
--[no]index - generate an index at the top of the resulting html
(default behaviour).
--infile - filename for the pod to convert (input taken from stdin
by default).
--outfile - filename for the resulting html file (output sent to
stdout by default).
--[no]poderrors - include a POD ERRORS section in the output if there were
any POD errors in the input (default behavior).
--podpath - colon-separated list of directories containing library
pods (empty by default).
--podroot - filesystem base directory from which all relative paths
in podpath stem (default is .).
--[no]quiet - suppress some benign warning messages (default is off).
--[no]recurse - recurse on those subdirectories listed in podpath
(default behaviour).
--title - title that will appear in resulting html file.
--[no]verbose - self-explanatory (off by default).
END_OF_USAGE
}
sub
parse_command_line {
my
(
$opt_backlink
,
$opt_cachedir
,
$opt_css
,
$opt_flush
,
$opt_header
,
$opt_help
,
$opt_htmldir
,
$opt_htmlroot
,
$opt_index
,
$opt_infile
,
$opt_outfile
,
$opt_poderrors
,
$opt_podpath
,
$opt_podroot
,
$opt_quiet
,
$opt_recurse
,
$opt_title
,
$opt_verbose
,
$opt_libpods
);
unshift
@ARGV
,
split
' '
,
$Config
{pod2html}
if
$Config
{pod2html};
my
$result
= GetOptions(
'backlink!'
=> \
$opt_backlink
,
'cachedir=s'
=> \
$opt_cachedir
,
'css=s'
=> \
$opt_css
,
'flush'
=> \
$opt_flush
,
'help'
=> \
$opt_help
,
'header!'
=> \
$opt_header
,
'htmldir=s'
=> \
$opt_htmldir
,
'htmlroot=s'
=> \
$opt_htmlroot
,
'index!'
=> \
$opt_index
,
'infile=s'
=> \
$opt_infile
,
'libpods=s'
=> \
$opt_libpods
,
'outfile=s'
=> \
$opt_outfile
,
'poderrors!'
=> \
$opt_poderrors
,
'podpath=s'
=> \
$opt_podpath
,
'podroot=s'
=> \
$opt_podroot
,
'quiet!'
=> \
$opt_quiet
,
'recurse!'
=> \
$opt_recurse
,
'title=s'
=> \
$opt_title
,
'verbose!'
=> \
$opt_verbose
,
);
usage(
"-"
,
"invalid parameters"
)
if
not
$result
;
usage(
"-"
)
if
defined
$opt_help
;
$opt_help
=
""
;
@Podpath
=
split
(
":"
,
$opt_podpath
)
if
defined
$opt_podpath
;
warn
"--libpods is no longer supported"
if
defined
$opt_libpods
;
$Backlink
=
$opt_backlink
if
defined
$opt_backlink
;
$Cachedir
= _unixify(
$opt_cachedir
)
if
defined
$opt_cachedir
;
$Css
=
$opt_css
if
defined
$opt_css
;
$Header
=
$opt_header
if
defined
$opt_header
;
$Htmldir
= _unixify(
$opt_htmldir
)
if
defined
$opt_htmldir
;
$Htmlroot
= _unixify(
$opt_htmlroot
)
if
defined
$opt_htmlroot
;
$Doindex
=
$opt_index
if
defined
$opt_index
;
$Podfile
= _unixify(
$opt_infile
)
if
defined
$opt_infile
;
$Htmlfile
= _unixify(
$opt_outfile
)
if
defined
$opt_outfile
;
$Poderrors
=
$opt_poderrors
if
defined
$opt_poderrors
;
$Podroot
= _unixify(
$opt_podroot
)
if
defined
$opt_podroot
;
$Quiet
=
$opt_quiet
if
defined
$opt_quiet
;
$Recurse
=
$opt_recurse
if
defined
$opt_recurse
;
$Title
=
$opt_title
if
defined
$opt_title
;
$Verbose
=
$opt_verbose
if
defined
$opt_verbose
;
warn
"Flushing directory caches\n"
if
$opt_verbose
&&
defined
$opt_flush
;
$Dircache
=
"$Cachedir/pod2htmd.tmp"
;
if
(
defined
$opt_flush
) {
1
while
unlink
(
$Dircache
);
}
}
my
$Saved_Cache_Key
;
sub
get_cache {
my
(
$dircache
,
$podpath
,
$podroot
,
$recurse
) =
@_
;
my
@cache_key_args
=
@_
;
my
$this_cache_key
= cache_key(
@cache_key_args
);
return
1
if
$Saved_Cache_Key
and
$this_cache_key
eq
$Saved_Cache_Key
;
$Saved_Cache_Key
=
$this_cache_key
;
my
$tests
= 0;
if
(-f
$dircache
) {
warn
"scanning for directory cache\n"
if
$Verbose
;
$tests
= load_cache(
$dircache
,
$podpath
,
$podroot
);
}
return
$tests
;
}
sub
cache_key {
my
(
$dircache
,
$podpath
,
$podroot
,
$recurse
) =
@_
;
return
join
(
'!'
,
$dircache
,
$recurse
,
@$podpath
,
$podroot
,
stat
(
$dircache
));
}
sub
load_cache {
my
(
$dircache
,
$podpath
,
$podroot
) =
@_
;
my
$tests
= 0;
local
$_
;
warn
"scanning for directory cache\n"
if
$Verbose
;
open
(
my
$cachefh
,
'<'
,
$dircache
) ||
die
"$0: error opening $dircache for reading: $!\n"
;
$/ =
"\n"
;
$_
= <
$cachefh
>;
chomp
(
$_
);
$tests
++
if
(
join
(
":"
,
@$podpath
) eq
$_
);
$_
= <
$cachefh
>;
chomp
(
$_
);
$tests
++
if
(
$podroot
eq
$_
);
if
(
$tests
!= 2) {
close
(
$cachefh
);
return
0;
}
warn
"loading directory cache\n"
if
$Verbose
;
while
(<
$cachefh
>) {
/(.*?) (.*)$/;
$Pages
{$1} = $2;
}
close
(
$cachefh
);
return
1;
}
sub
html_escape {
my
$rest
=
$_
[0];
$rest
=~ s/&/
&
;/g;
$rest
=~ s/</
<
;/g;
$rest
=~ s/>/
>
;/g;
$rest
=~ s/"/
"
;/g;
return
$rest
;
}
sub
htmlify {
my
(
$heading
) =
@_
;
$heading
=~ s/(\s+)/ /g;
$heading
=~ s/\s+\Z//;
$heading
=~ s/\A\s+//;
$heading
=~ s/["?]//g;
$heading
=
lc
(
$heading
);
return
$heading
;
}
sub
anchorify {
my
(
$anchor
) =
@_
;
$anchor
= htmlify(
$anchor
);
$anchor
=~ s/\W/_/g;
return
$anchor
;
}
sub
_save_page {
my
(
$modspec
,
$modname
) =
@_
;
$modspec
=
$Podroot
eq File::Spec->curdir
? File::Spec->abs2rel(
$modspec
)
: File::Spec->abs2rel(
$modspec
,
File::Spec->canonpath(
$Podroot
));
$modspec
= Pod::Html::_unixify(
$modspec
);
my
(
$file
,
$dir
) = fileparse(
$modspec
,
qr/\.[^.]*/
);
$Pages
{
$modname
} =
$dir
.
$file
;
}
sub
_unixify {
my
$full_path
=
shift
;
return
''
unless
$full_path
;
return
$full_path
if
$full_path
eq
'/'
;
my
(
$vol
,
$dirs
,
$file
) = File::Spec->splitpath(
$full_path
);
my
@dirs
=
$dirs
eq File::Spec->curdir()
? (File::Spec::Unix->curdir())
: File::Spec->splitdir(
$dirs
);
if
(
defined
(
$vol
) &&
$vol
) {
$vol
=~ s/:$//
if
$^O eq
'VMS'
;
$vol
=
uc
$vol
if
$^O eq
'MSWin32'
;
if
(
$dirs
[0] ) {
unshift
@dirs
,
$vol
;
}
else
{
$dirs
[0] =
$vol
;
}
}
unshift
@dirs
,
''
if
File::Spec->file_name_is_absolute(
$full_path
);
return
$file
unless
scalar
(
@dirs
);
$full_path
= File::Spec::Unix->catfile(File::Spec::Unix->catdir(
@dirs
),
$file
);
$full_path
=~ s|^\/||
if
$^O eq
'MSWin32'
;
return
$full_path
;
}
__PACKAGE__->_accessorize(
'htmldir'
,
'htmlfileurl'
,
'htmlroot'
,
'pages'
,
'quiet'
,
'verbose'
,
);
sub
resolve_pod_page_link {
my
(
$self
,
$to
,
$section
) =
@_
;
return
undef
unless
defined
$to
||
defined
$section
;
if
(
defined
$section
) {
$section
=
'#'
.
$self
->idify(
$section
, 1);
return
$section
unless
defined
$to
;
}
else
{
$section
=
''
;
}
my
$path
;
unless
(
exists
$self
->pages->{
$to
}) {
my
@matches
;
foreach
my
$modname
(
keys
%{
$self
->pages}) {
push
@matches
,
$modname
if
$modname
=~ /::\Q
$to
\E\z/;
}
if
(
$#matches
== -1) {
warn
"Cannot find \"$to\" in podpath: "
.
"cannot find suitable replacement path, cannot resolve link\n"
unless
$self
->quiet;
return
''
;
}
elsif
(
$#matches
== 0) {
warn
"Cannot find \"$to\" in podpath: "
.
"using $matches[0] as replacement path to $to\n"
unless
$self
->quiet;
$path
=
$self
->pages->{
$matches
[0]};
}
else
{
warn
"Cannot find \"$to\" in podpath: "
.
"more than one possible replacement path to $to, "
.
"using $matches[-1]\n"
unless
$self
->quiet;
$path
=
$self
->pages->{
$matches
[-1]};
}
}
else
{
$path
=
$self
->pages->{
$to
};
}
my
$url
= File::Spec::Unix->catfile(Pod::Html::_unixify(
$self
->htmlroot),
$path
);
if
(
$self
->htmlfileurl ne
''
) {
$url
= relativize_url(
File::Spec::Unix->catdir(Pod::Html::_unixify(
$self
->htmldir),
$url
),
$self
->htmlfileurl
);
}
return
$url
.
".html$section"
;
}
sub
relativize_url {
my
(
$dest
,
$source
) =
@_
;
my
(
$dest_volume
,
$dest_directory
,
$dest_file
) =
File::Spec::Unix->splitpath(
$dest
);
$dest
= File::Spec::Unix->catpath(
$dest_volume
,
$dest_directory
,
''
);
my
(
$source_volume
,
$source_directory
,
$source_file
) =
File::Spec::Unix->splitpath(
$source
);
$source
= File::Spec::Unix->catpath(
$source_volume
,
$source_directory
,
''
);
my
$rel_path
=
''
;
if
(
$dest
ne
''
) {
$rel_path
= File::Spec::Unix->abs2rel(
$dest
,
$source
);
}
if
(
$rel_path
ne
''
&&
substr
(
$rel_path
, -1 ) ne
'/'
) {
$rel_path
.=
"/$dest_file"
;
}
else
{
$rel_path
.=
"$dest_file"
;
}
return
$rel_path
;
}
1;