#!/usr/bin/perl # # podweb This script translates POD into HTML. # # See the POD for details on usage. # Or, run this script on itself and see the HTML. # # Revision History # 1.0 Initial revision. 130626 # 1.1 Added doctype line at file start. 150527 # 1.2 Added a more complete doctype line. 180102 # 1.3 Added licensing info. 180531 # # Written by Wayne Morrison, 130626; posted 130707. # # Copyright 2013 Wayne Morrison # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # use strict; use Getopt::Long qw(:config no_ignore_case_always); use Pod::Parser; # # Version information. # my $NAME = "podweb"; my $VERS = "$NAME version: 1.3"; ############################################################################ # # Options fields. # my %opts = (); # Options. # # Command line arguments. # my @opts = ( 'headmatch|hm', # numbers matches =head numbers. 'mapfile=s', # User's pod->html map file. 'nodefaults', # Don't use any defaults. 'noheader', # Don't include header HTML. 'notrailer', # Don't include trailing HTML. 'openli', # Don't close =item's. 'title=s', # HTML title line. 'table', # Print the optioned translation table. # # Replacement HTML commands for pod commands. # 'pod=s', 'cut=s', 'head1|h1=s', 'head2|h2=s', 'head3|h3=s', 'head4|h4=s', 'over=s', 'item=s', 'back=s', # # Replacement HTML commands for pod interior sequences. # 'B|bold=s', 'C|code=s', 'F|file=s', 'I|italicize=s', 'X|index=s', 'Z|zerospace=s', 'stdout', # Write output to stdout. 'help', # Give a help message. 'Version', # Display the program version. ); my $headmatch = 0; # Header numbering flag. my $noheader = 0; # No-header-HTML flag. my $notrailer = 0; # No-trailer-HTML flag. my $openli = 0; # Open-list flag. my $stdout = 0; # Output to stdout. my $titlestr = "INSERT TITLE LINE"; # Title line. ############################################################################ my @htmllines = (); # HTML lines translated from pod. my $RCFILE = '.podwebrc'; # Default rc file. ############################################################################ package poddy; our @ISA = qw(Pod::Parser); # # Opening HTML tags for the pod commands and internal sequences. # # Most of the commands have a closing command given in %cmdend. However, # the =over and =back commands are paired and are both given here. If # these are updated from the default, modify them here. # my %cmdstart = ( 'pod' => '', 'cut' => '', 'head1' => '

', 'head2' => '

', 'head3' => '

', 'head4' => '
', 'over' => '', 'item' => '
  • ', 'b' => '', 'c' => "\"", 'f' => '', 'i' => '', 'x' => '', 'z' => ' --->', ); # # Default paragraph separator. # my $parsep = "

    \n"; # # Default verbatim wrappers. # my $verbstart = '

    ';
    my $verbend   = '
    '; #---------------------------------------------------------------------- # Routine: poddy::command() # # Purpose: Translate a pod =command. # # Handled: # =pod # =cut # =head1 # =head2 # =head3 # =head4 # =over # =back # =item # # Not handled yet: # =begin # =end # =for # sub command { my $parser = shift; # Parser object. my $cmd = shift; # Command to handle. my $para = shift; # Command's argument. my $linenum = shift; # Line number of command. my $expansion; # Parsed expansion of paragraph. my $cmdstart; # Start of command. my $cmdend; # Start of command. # print "command: <$cmd>\t<$para>\n"; # # Parse the command's paragraph. # $expansion = $parser->interpolate($para, $linenum); # print "\t\t\t<$expansion>\n"; # # Slice off trailing whitespace (newlines especially) from the # paragraph. # $expansion =~ s/\s*$//s; # # Slice off trailing whitespace (newlines especially) from the # paragraph. # $cmdstart = $cmdstart{lc($cmd)}; $cmdend = $cmdend{lc($cmd)}; if($cmd =~ /pod/i) { # # Don't do anything special right now. # } elsif($cmd =~ /head1/i) { push @htmllines, "$cmdstart$expansion$cmdend\n\n"; } elsif($cmd =~ /head2/i) { push @htmllines, "$cmdstart$expansion$cmdend\n\n"; } elsif($cmd =~ /head3/i) { push @htmllines, "$cmdstart$expansion$cmdend\n\n"; } elsif($cmd =~ /head4/i) { push @htmllines, "$cmdstart$expansion$cmdend\n\n"; } elsif($cmd =~ /over/i) { push @htmllines, "$cmdstart"; } elsif($cmd =~ /back/i) { push @htmllines, "$cmdstart\n"; } elsif($cmd =~ /item/i) { if($openli) { push @htmllines, "$cmdstart$expansion"; } else { push @htmllines, "$cmdstart$expansion$cmdend"; } } elsif($cmd =~ /cut/i) { # # Don't do anything special right now. # } elsif(($cmd =~ /begin/i) || ($cmd =~ /for/i) || ($cmd =~ /end/i) ) { # # Don't do anything special right now. # # When these commands are handled, $expansion will # hold the name of the special section. # } } #---------------------------------------------------------------------- # Routine: poddy::textblock() # sub textblock { my $parser = shift; # Parser object. my $para = shift; # Text block. my $linenum = shift; # Line number of text. my $expansion; # Parsed expansion of text block. $expansion = $parser->interpolate($para, $linenum); # print "textblock: <$paragraph>\t<$expansion>\n"; # push @htmllines, "$expansion$parsep\n"; push @htmllines, "$expansion$parsep"; } #---------------------------------------------------------------------- # Routine: poddy::interior_sequence() # # Purpose: Translate a pod character command. # # Handled: # B bolds # C double-quotes # F underlines # E # E < # E > # E / # E | # I italicizes # L double-quotes # S text w/ nonbreaking spaces # X deletes # Z<> deletes # sub interior_sequence { my $parser = shift; # Parser object. my $seqcmd = shift; # Sequence command. my $seqarg = shift; # Sequence command's argument. my $cmdstart; # Start of command. my $cmdend; # Start of command. # print "interior: <$seqcmd>\t$seqarg\n"; # # Slice off trailing whitespace (newlines especially) from the # paragraph. # $cmdstart = $cmdstart{lc($seqcmd)}; $cmdend = $cmdend{lc($seqcmd)}; if($seqcmd eq 'B') { return("$cmdstart$seqarg$cmdend"); } elsif($seqcmd eq 'C') { return("\"$seqarg\""); } elsif($seqcmd eq 'E') { if($seqarg eq 'lt') { return('<'); } elsif($seqarg eq 'gt') { return('>'); } elsif($seqarg eq 'sol') { return('/'); } elsif($seqarg eq 'verbar') { return('|'); } } elsif($seqcmd eq 'F') { return("$cmdstart$seqarg$cmdend"); } elsif($seqcmd eq 'I') { return("$cmdstart$seqarg$cmdend"); } elsif($seqcmd eq 'L') { return("$seqarg"); } elsif($seqcmd eq 'S') { $seqarg =~ s/\s/\ /g; return($seqarg); } elsif($seqcmd eq 'X') { return("$cmdstart $seqarg $cmdend"); } elsif($seqcmd eq 'Z') { return("$cmdstart $seqarg $cmdend"); } } #---------------------------------------------------------------------- # Routine: poddy::verbatim() # sub verbatim { my $parser = shift; # Parser object. my $para = shift; # Verbatim text. my $linenum = shift; # Line number of text. # print "verbatim: <$paragraph>\t$linenum\n"; if(! defined($opts{'nodefaults'})) { $para =~ s//>/g; } push @htmllines, "$verbstart$para$verbend"; } ############################################################################ package main; main(); exit(0); ############################################################################ #----------------------------------------------------------------------------- # Routine: main() # sub main { my $defmap; # POD-parsing object. my $parser; # POD-parsing object. $| = 1; # # Read an rc file if the user has one. # $defmap = glob("~/$RCFILE"); if(-e $defmap) { readmap($defmap); } # # Munch on the options and arguments. # optsandargs(); # # Get a new parser object. # $parser = new poddy; # # Convert each file's pod to its own HTML file. # foreach my $fn (@ARGV) { # # Start with a fresh HTML buffer. # @htmllines = (); # # Parse this file. # $parser->parse_from_file($fn); # # Add boilerplate to the start and end of the translated pod. # addhdr(); addtail(); # # Send the translated pod to the correct place. # writeout($fn); } } #---------------------------------------------------------------------- # Routine: optsandargs() # # Purpose: Parse the command line for options and arguments. # sub optsandargs { # # Parse the options. # GetOptions(\%opts,@opts) || usage(); # # Check for some immediate-action options. # usage() if(defined($opts{'help'})); version() if(defined($opts{'Version'})); # # Handle the -nodefaults option. We'll empty out all the default # values in our starting and ending hashes. A few other default # formatters will also be cleared. # if(defined($opts{'nodefaults'})) { foreach my $key (keys(%cmdstart)) { $cmdstart{$key} = ''; } foreach my $key (keys(%cmdend)) { $cmdend{$key} = ''; } $parsep = ''; $titlestr = ''; $verbstart = ''; $verbend = ''; } # # Handle the -mapfile option. # if(defined($opts{'mapfile'})) { readmap($opts{'mapfile'}); } # # Handle a few special options. # $titlestr = $opts{'title'} if(defined($opts{'title'})); $openli = 1 if(defined($opts{'openli'})); $noheader = 1 if(defined($opts{'noheader'})); $notrailer = 1 if(defined($opts{'notrailer'})); $stdout = 1 if(defined($opts{'stdout'})); # # Handle to -headmatch option. # if(defined($opts{'headmatch'})) { $cmdstart{'head1'} = '

    '; $cmdstart{'head2'} = '

    '; $cmdstart{'head3'} = '

    '; $cmdstart{'head4'} = '

    '; $cmdend{'head1'} = '

    '; $cmdend{'head2'} = '
  • '; $cmdend{'head3'} = ''; $cmdend{'head4'} = ''; } # # Set the command-line options corresponding to the keys # of the command-start table. # foreach my $key (keys(%cmdstart)) { doopt($key,$opts{$key}) if(defined($opts{$key})); } # # If asked, we'll print the translation table and exit. # dumptable() if(defined($opts{'table'})); } #---------------------------------------------------------------------- # Routine: readmap() # # Purpose: Read a map file. # sub readmap { my $mapfn = shift; # Map file to read. # # Open the map file. # if(open(MF, "< $mapfn") == 0) { print STDERR "unable to open mapfile \"$mapfn\"\n"; exit(3); } # # Read and handle each entry. # while() { my $ln = $_; # Current line. my $key; # Line's option name. # # Skip blank and comment lines. # chomp $ln; $ln =~ s/#.*$//g; next if($ln =~ /^\s*$/); # # Split line into pieces and (ahem) take care of business. # $ln =~ s/^(\S+)\s*//; $key = $1; doopt($key,$ln); } close(MF); } #---------------------------------------------------------------------- # Routine: doopt() # # Purpose: Handle an option line. # sub doopt { my $key = shift; # Option name. my $arg = shift; # Option value. my $prefix = ''; # First half of option value. my $suffix = ''; # Second half of option value. # # Handle a few special-case options. # if(($key eq 'headmatch') || ($key eq 'openli')) { $opts{$key} = 1; return; } elsif($key eq 'title') { $titlestr = $arg; return; } # # Give a warning and return if the option isn't known. # if(! defined($cmdstart{$key})) { print STDERR "unknown option: $key\n"; return; } # # If the option value doesn't have any commas, we'll append one. # This means the option value will be the prefix. # $arg .= ',' if($arg !~ /,/); # # Get rid of any trailing comments and whitespace. # $arg =~ s/\s*#.*$//; $arg =~ s/\s+$//; # # Break the option argument into its two pieces. # $arg =~ /(.*),(.*)/; $prefix = $1; $suffix = $2; # # Give a warning and return if the option isn't known. # if(($prefix eq '') && ($suffix eq '')) { print STDERR "$key has no values\n"; return; } # # Add the argument's prefix to the cmdstart hash. If the # prefix is '-', then the user wants to zap it. # if($prefix ne '') { if($prefix eq '-') { $cmdstart{$key} = ''; } else { $cmdstart{$key} = $prefix; } } # # Add the argument's suffix to the cmdend hash. If the # suffix is '-', then the user wants to zap it. # if($suffix ne '') { if($suffix eq '-') { $cmdend{$key} = ''; } else { $cmdend{$key} = $suffix; } } } #---------------------------------------------------------------------- # Routine: addhdr() # # Purpose: Add an HTML header section. # sub addhdr { return if($noheader); unshift @htmllines, "\n"; unshift @htmllines, "
    \n"; unshift @htmllines, "\n"; unshift @htmllines, "\n"; unshift @htmllines, "\n"; unshift @htmllines, "\n"; unshift @htmllines, "\n"; unshift @htmllines, $titlestr; unshift @htmllines, ""; unshift @htmllines, "<head>\n"; unshift @htmllines, "<html>\n"; unshift @htmllines, "\<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"\>\n"; } #---------------------------------------------------------------------- # Routine: addtail() # # Purpose: Add a tail section to the buffer. # sub addtail { return if($notrailer); push @htmllines, "</body>\n"; push @htmllines, "</html>\n"; } #---------------------------------------------------------------------- # Routine: writeout() # # Purpose: Send the translated pod to either a file or stdout. # sub writeout { my $fn = shift; if($stdout) { # # Print the translated pod. # foreach my $ln (@htmllines) { print "$ln\n"; } } else { my $outfn; # Output filename. # # Ensure our output file doesn't exist. # $outfn = "$fn.html"; if(-e $outfn) { print STDERR "unable to write translated pod; output file ($outfn) already exists\n"; return; } # # Ensure we can create our output file. # if(open(OUT,"> $outfn") == 0) { print STDERR "unable to create output file ($outfn)\n"; return; } # # Print the translated pod. # foreach my $ln (@htmllines) { print OUT "$ln\n"; } close(OUT); } } #---------------------------------------------------------------------- # Routine: dumptable() # # Purpose: Display the translation table. # sub dumptable { my @keys = ( 'pod', 'cut', 'head1', 'head2', 'head3', 'head4', 'over', 'back', 'item', 'B', 'C', 'F', 'I', 'X', 'Z', ); foreach my $key (@keys) { printf("%-10s %-10s %-10s\n",$key,$cmdstart{$key},$cmdend{$key}); } print "\n"; print "title string: $titlestr\n"; print "openli flag: $openli\n"; exit(0); } #---------------------------------------------------------------------- # Routine: version() # # Purpose: Print the version number(s) and exit. # sub version { print STDERR "$VERS\n"; exit(0); } #---------------------------------------------------------------------- # Routine: usage() # # Purpose: Give usage message and exit. # sub usage { my $ustr; # Usage string. $ustr = "usage: podweb [options] <files> options for remapping HTML commands for pod commands -pod start of a document section -cut end of a document section -head1 | -h1 heading -head2 | -h2 heading -head3 | -h3 heading -head4 | -h4 heading -over start of list section -back end of list section -item list item options for remapping HTML commands pod interior sequences: -B | -bold bold text -C | -code code lines -F | -file filenames -I | -italicize italicize text -X | -index index entries -Z | -zerospace zero-width characters miscellaneous translation-control options for: -headmatch|hm <H> numbers matches =head numbers -mapfile user's POD->HTML map file -nodefaults don't use any defaults -noheader don't include header HTML -notrailer don't include trailing HTML -openli don't close =item's elements -title HTML title line miscellaneous options: -table print the option translation table -help -Version\n"; print STDERR $ustr; exit(0); } 1; ############################################################################## =pod =head1 NAME B<podweb> - convert POD into HTML =head1 SYNOPSIS podweb [options] <file1 ... fileN> =head1 DESCRIPTION B<podweb> converts POD in a Perl file into HTML and saves the translated POD to an output file. The default translations will produce a nice, readable HTML version of the POD (by the author's aesthetics). Some or all of the translations can be modified to the user's tastes; see the MAPPINGS section for information about adjusting the translations. B<podweb> has a standard translation format, which is described in the TRANSLATIONS section below. These defaults can be modified by the user with command-line options and a map file. After translating all the POD in a file, a boilerplate set of starting and ending HTML is added to the POD. Other than the title, which may be modified with the I<-title> option, this is the same regardless of file. After creating an HTML file with B<podweb>, you would be well-advised to verify that the resulting file looks the way you want it to look. Aesthetics are a very personal thing, and you should ensure you like the result. By default, the translated POD is written to a new file named by appending "B<.html>" to the translated file's name. For example, running "B<podweb foo"> will have the translated output written to B<foo.html>. The output file must not exist and must be in a directory in which B<podweb> can write. If the I<-stdout> option is given, the translated POD will be written to standard output. The POD parsing is performed by the I<Pod::Parser(3pm)> module. There are other POD/HTML converters, such as B<pod2html>. None met my needs or formatting preferences, so B<podweb> came to be. =head1 TRANSLATIONS There are two types of pod commands that are translated. The B<=commands> are those pod commands that start with an equals sign. The pod-character commands are those single-character commands that are surrounded by angle brackets. The default POD-to-HTML translations are provided int the DEFAULT MAPPINGS section. The B<=commands> are translated in this way: =pod nothing =cut nothing =head1 adds a <h3> header section =head2 adds a <h4> header section =head3 adds a <h5> header section =head4 adds a <h5> header section =over starts an itemized list with a <ul> tag =back closes an itemized list with a </ul> tag =item adds a list entry with a <li> tag The pod-character commands are translated in this manner: B<text> bolds the enclosed text C<code> puts the enclosed code in double quotes E<esc> four escape values are defined E<lt> < E<gt> > E<sol> / E<verbar> | F<file> underlines the enclosed filename I<text> italicizes the enclosed text L<link> puts the enclosed text in an <a> tag S<text> does nothing to the enclosed text X<idx> enclosed the text in comments Z<> enclosed the text in comments =head1 MAPPINGS The mappings used by B<podweb> define how various POD commands and internal sequences are translated to HTML tags. Many HTML tags are used in pairs, one tag to initiate a format and another to terminate it. B<podweb> mappings allow you to specify the set of initial tags and the set of terminal tags. Multiple HTML tags can be included in a mapping. The basic format for a B<podweb> mapping is: initial-tags,terminal-tags The I<initial-tags> portion of the mapping will be inserted before any relevant text; the I<terminal-tags> portion will be inserted after the text. The two mapping parts are separated by a comma. For example, this mapping can be used with the I<-head1> option to define how B<=head1> POD commands are translated: -head1 '<center><h2>,</h2></center>' This particular mapping will translate B<=head1> commands to use the HTML B<h2> section header, and center the header line. The text from the B<=head1> command will be placed between the two parts of the mapping. The quotes are not required for the mapping, but they are highly likely to be needed when specified on a command line. Either the I<initial-tags> or the I<terminal-tags> part of the mapping may be left blank. This will cause B<podweb> to use the default value for that mapping piece. If the one of these is given as a hyphen, then the nothing will be used for that part of the mapping at all. These two mappings show how this may be used: -item '<li>,-' -over '<ol start=42>,' The first mapping will not add a B<E<lt>/liE<gt>> tag at the end of the B<=item> line. The second mapping will make B<=over> lists be ordered lists starting with number 42. The default mappings will be modified by the B<.podmwebrc> file, a I<-mapfile> option mapfile, and command-line options, in that order. The final set of mappings may get a little confusing when the defaults, map files, and command-line options are swirled together. The I<-table> option will display the mappings that will be used, but it will not translate a file. =head1 MAP FILES For the sake of convenience, a map file may be set up to hold frequently used mappings. If the B<.podwebrc> file is in the user's home directory, mappings will be read from that file. The I<-mapfile> option may be used to specify an alternate map file. Map files consist of a set of lines that correspond to the B<podweb> command line options. However, the initial dash needed by options is not used with map file entries. Comments are started with a pound sign ('#') and are ignored by B<podweb>. The pound sign should not be used in mappings. Blank lines are also ignored. The following is an example of a map file: headmatch openli title big hunk of docs item <li>,- over <ol start=42>, back </ol>, =head1 DEFAULT MAPPINGS The following are the default mappings for most POD commands: POD HTML opening tag HTML closing tag pod (nothing) (nothing) cut (nothing) (nothing) head1 <h3> </h3> head2 <h4> </h4> head3 <h5> </h5> head4 <h5> </h5> over <ul> (nothing) back </ul> (nothing) item <li> </li> B <b> </b> C " " F <u> </u> L <a href={link}>{link}</a> (nothing) I <i> </i> S <i> </i> X <!-- --> Z <!--- ---> When the B<SE<lt>E<gt>> command is given, enclosed blanks are replaced with HTML's B<&Z<>nbsp;> tag for non-breaking spaces. When POD escape commands are given, the actual character is inserted in place of the escape command. The following POD escape commands are recognized: POD Replacement Character E<lt> < E<gt> > E<sol> / E<verbar> | The following are default values for options that do not have a direct HTML analog: headmatch: off nodefaults: off noheader: off notrailer: off openli flag: off title string: INSERT TITLE LINE =head1 OPTIONS B<podweb> takes the several types of options. The mapping options modify either POD commands or POD internal sequences, and are described in their own sections. There is a small set of options that affect translations, and these are described in the "General Mapping Options" section. A final set of options are described in the "Miscellaneous Options" section. =head2 General Mapping Options =over 4 =item I<-headmatch | -hm> This option cases E<lt>HE<gt> numbers to match the I<=head> numbers. B<podweb>'s default mappings do not match directly; this overrides the default values. =item I<-nodefaults> No defaults will be used. =item I<-noheader> The header HTML will not be prepended to the translated file. =item I<-notrailer> The trailer HTML will not be prepended to the translated file. =item I<-openli> Don't close I<=item> translations. The actual effect will depend on the item translation and the browser. In some cases, the whole of the item's text will be included on the initial item line. =item I<-title title-string> HTML title line. This string replaces the default title string in the standard boilerplate header. =back =head2 Command Mapping Options =over 4 =item I<-pod mapping> The mapping modifies how the I<=pod> command is translated. =item I<-cut mapping> The mapping modifies how the I<=cut> command is translated. =item I<-head1 | -h1 mapping> The mapping modifies how the I<=head1> command is translated. =item I<-head2 | -h2 mapping> The mapping modifies how the I<=head2> command is translated. =item I<-head3 | -h3 mapping> The mapping modifies how the I<=head3> command is translated. =item I<-head4 | -h4 mapping> The mapping modifies how the I<=head4> command is translated. =item I<-over mapping> The mapping modifies how the I<=over> command is translated. If this option is used to specify a different list tag than B<ol>, the I<=back> command must be set to mirror this change. =item I<-item mapping> The mapping modifies how the I<=item> command is translated. =item I<-back mapping> The mapping modifies how the I<=back> command is translated. =back =head2 Interior Sequence Mapping Options =over 4 =item I<-B mapping> The mapping modifies how the BE<lt>E<gt> sequence is translated. =item I<-C mapping> The mapping modifies how the CE<lt>E<gt> sequence is translated. =item I<-F mapping> The mapping modifies how the FE<lt>E<gt> sequence is translated. =item I<-I mapping> The mapping modifies how the IE<lt>E<gt> sequence is translated. =item I<-X mapping> The mapping modifies how the XE<lt>E<gt> sequence is translated. =item I<-Z mapping> The mapping modifies how the ZE<lt>E<gt> sequence is translated. =back =head2 Miscellaneous Options =over 4 =item I<-mapfile file> User's POD->HTML map file. This file will be read and its translation maps applied before anything else happens. Other command-line mappings may be used to modify the translation maps from the map file. =item I<-stdout> Print the translated file to standard output, instead of to a file. =item I<-table> Print the translation mapping table, as modified by an optional map file and command-line options. =item I<-Version> Display the version information for B<podweb>. =item I<-help> Display a help message. =back =head1 LIMITATIONS B<podweb> has the following limitations: - The =begin, =for, and =end commands are not handled. - The L<link> format cannot be changed without editting podweb. - The S<text> format cannot be changed without editting podweb. It is hoped that these limitations will be fixed eventually. =head1 AUTHOR Wayne Morrison, wayne@waynemorrison.com =head1 LICENSE Copyright 2013 Wayne Morrison Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =head1 SEE ALSO perlpod(1), pod2html(1) Pod::Parser(3pm) =cut