File indexing completed on 2024-05-05 05:44:47

0001 #!/usr/bin/perl
0002 #
0003 # Code adapted from Pod::HtmlEasy for kio_perldoc.  See CPAN for the real deal.
0004 # Integrated and crap^H^H^H^H unnecessary dependencies removed by Michael Pyne
0005 # <michael.pyne@kdemail.net>
0006 #
0007 # Copyright 2007, 2008 Michael Pyne.
0008 # Original source is licensed to be distributed under the same terms as Perl
0009 # itself.  All changes by Michael Pyne retain this license.
0010 #
0011 # Based on Pod::HtmlEasy by M. P. Graciliano and Geoffrey Leach.
0012 # http://search.cpan.org/~gleach/Pod-HtmlEasy/
0013 
0014 #############################################################################
0015 ## Name:        TieHandler.pm
0016 ## Purpose:     Pod::HtmlEasy::TieHandler
0017 ## Author:      Graciliano M. P.
0018 ## Modified by: Geoffrey Leach
0019 ## Created:     2/14/2007
0020 ## Copyright:   (c) 2004 Graciliano M. P.
0021 ## Licence:     This program is free software; you can redistribute it and/or
0022 ##              modify it under the same terms as Perl itself
0023 #############################################################################
0024 
0025 # The function of this package is to provide a print function that is
0026 # tied to a filehandle which is then passed as the output file to
0027 # Pod::Parser. Note that only PRINT() and CLOSE() are defined.
0028 # PRINT() accumulates output in an anon array, which is then referenced
0029 # by the defining function.
0030 
0031 package Pod::HtmlEasy::TieHandler;
0032 
0033 use strict;
0034 use warnings;
0035 
0036 our $VERSION = 0.02;
0037 
0038 sub TIEHANDLE {
0039     my $class  = shift;
0040     my $scalar = shift;
0041 
0042     return bless $scalar, $class;
0043 }
0044 
0045 sub PRINT {
0046     my $this = shift;
0047 
0048     push @{$this}, @_;
0049     return 1;
0050 }
0051 
0052 sub FILENO { return 1; }
0053 sub CLOSE  { return 1; }
0054 
0055 #######
0056 # END #
0057 #######
0058 
0059 1;
0060 
0061 #############################################################################
0062 ## Name:        Parser.pm
0063 ## Purpose:     Pod::HtmlEasy::Parser
0064 ## Author:      Graciliano M. P.
0065 ## Modified by: Geoffrey Leach
0066 ## Created:     11/01/2004
0067 ## Updated:     2007-02-25
0068 ## Copyright:   (c) 2004 Graciliano M. P.
0069 ## Licence:     This program is free software; you can redistribute it and/or
0070 ##              modify it under the same terms as Perl itself
0071 #############################################################################
0072 
0073 package Pod::HtmlEasy::Parser;
0074 
0075 use base qw{ Pod::Parser };
0076 
0077 use Carp;
0078 use English qw{ -no_match_vars };
0079 use feature "switch";
0080 
0081 use strict;
0082 use warnings;
0083 no warnings 'experimental::smartmatch';
0084 
0085 our $VERSION = 0.03;
0086 
0087 our $EMPTY = q{};
0088 our $NL    = qq{\n};
0089 our $NUL   = qq{\0};
0090 our $SPACE = q{ };
0091 
0092 # Set to 1 for URIs like /functions/foo to fixup links in evt_on_L
0093 our $fnPage = 0;
0094 
0095 ########
0096 # VARS #
0097 ########
0098 
0099 my $MAIL_RE = qr{
0100          (         # grab all of this
0101          [\w-]+    # some word chars with '-' included   foo
0102          \0?       # possible NUL escape
0103          \@        # literal '@'                         @
0104          [\w\\-]+  # another word                        bar
0105          (?:       # non-grabbing pattern
0106           \.       # literal '.'                        .
0107           [\w\-\.]+# that word stuff                    stuff
0108           \.       # another literal '.'                .
0109           [\w\-]+  # another word                       and
0110           |        # or
0111           \.       # literal '.'                        .   
0112           [\w\-]+  # word                               nonsense
0113           |        # or empty?
0114          )        # end of non-grab
0115          )        # end of grab
0116         }smx;    # [6062]
0117 
0118 # Treatment of embedded HTML-significant characters and embedded URIs.
0119 
0120 # There are some characters (%html_entities below) which may in some
0121 # circumstances be interpreted by a browser, and you probably don't want that
0122 # Consequently, they are replaced by names defined by the W3C UNICODE spec,
0123 # http://www.w3.org/TR/MathML2/bycodes.html, bracketed by '&' and ';'
0124 # Thus, '>' becomes '&lt;' This is handled by _encode_entities()
0125 # There's a "gotchya" in this process. As we are generating HTML,
0126 # the encoding needs to take place _before_ any HTML is generated.
0127 
0128 # If the HTML appears garbled, and UNICODE entities appear where they
0129 # shouldn't, this encoding has happened to late at some point.
0130 
0131 # This is all further complicated by the fact that the POD formatting
0132 # codes syntax uses some of the same characters, as in "L<...>", for example,
0133 # and we can't expand those first, because some of them generate
0134 # HTML. This is resolved by tagging the characters that we want
0135 # to distinguish from HTML with ASCII NUL ('\0', $NUL). Thus, '$lt;' becomes
0136 # '\0&amp;' in _encode_entities().  Generated HTML is also handled
0137 # this way by _nul_escape(). After all processing of the  POD formatting
0138 # codes are processed, this is reversed by _remove _nul_escapes().
0139 
0140 # Then there's the issue of embedded URIs. URIs are also generated
0141 # by the processing of L<...>, and can show up _inside L<...>, we
0142 # delay processing of embedded URIs until after all of the POD
0143 # formatting codes is complete. URIs that result from that processing
0144 # are tagged (you guessed it!) with a NUL character, but not preceeding
0145 # the generated URI, but after the first character. These NULs are removed
0146 # by _remove _nul_escapes()
0147 
0148 my %html_entities = (
0149     q{&} => q{amp},
0150     q{>} => q{gt},
0151     q{<} => q{lt},
0152     q{"} => q{quot},
0153 );
0154 
0155 my $HTML_ENTITIES_RE = '[' . join ('', keys %html_entities) . ']';
0156 $HTML_ENTITIES_RE = qr{$HTML_ENTITIES_RE};
0157 
0158 #################
0159 # _NUL_ESCAPE   #
0160 #################
0161 
0162 # Escape HTML-significant characters with ASCII NUL to differentiate them
0163 # from the same characters that get converted to entity names
0164 
0165 sub _nul_escape {
0166     my $txt_ref = shift;
0167 
0168     ${$txt_ref} =~ s{($HTML_ENTITIES_RE)}{$NUL$1}gsm;
0169     return;
0170 }
0171 
0172 #######################
0173 # _REMOVE_NUL_ESCAPSE #
0174 #######################
0175 
0176 sub _remove_nul_escapes {
0177     my $txt_ref = shift;
0178 
0179     ${$txt_ref} =~ s{$NUL}{}gsm;
0180     return;
0181 }
0182 
0183 ####################
0184 # _ENCODE_ENTITIES #
0185 ####################
0186 
0187 sub _encode_entities {
0188     my ( $txt_ref ) = @_;
0189 
0190     return unless $$txt_ref;
0191 
0192     foreach my $chr ( keys %html_entities ) {
0193 
0194         my $re = qr{(?<!$NUL)$chr};
0195         my $replacement = '&' . $html_entities{$chr} . ';';
0196         ${$txt_ref} =~ s{$re}{$NUL$replacement}gsm;
0197     }
0198 
0199     return;
0200 }
0201 
0202 sub setFunctionPage {
0203     my $this = shift;
0204     our $fnPage = shift;
0205 }
0206 
0207 ###########
0208 # COMMAND #
0209 ###########
0210 
0211 # Overrides command() provided by base class in Pod::Parser
0212 sub command {
0213     my ( $parser, $command, $paragraph, $line_num, $pod ) = @_;
0214 
0215     if ( defined $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER} ) {
0216         _verbatim($parser);
0217     }    # [6062]
0218 
0219     my $expansion = $parser->interpolate( $paragraph, $line_num );
0220 
0221     $expansion =~ s{^\s*}{}gsm;    # delete surrounding whitespace
0222     $expansion =~ s{\s*$}{}gsm;    # delete surrounding whitespace
0223 
0224     # Encoding puts in a NUL; we're finished with the text, so remove them
0225     _encode_entities( \$expansion );
0226     _remove_nul_escapes( \$expansion );
0227 
0228 # Create the index tag
0229 # a_name has the text of the expansion _without_ anything between '<' and '>',
0230 # which amounts to the HTML formatting codes, which are not processed by
0231 # the name directive.
0232     my $a_name = $expansion;
0233     $a_name =~ s{<.*?>}{}gsm;
0234 
0235     $a_name =~ /</g;
0236     my $pos = pos($a_name);
0237     my $start = $pos;
0238     my $count = 0;
0239     $count++ if defined $pos;
0240 
0241     while(defined $pos and $count != 0)
0242     {
0243         my $match = $a_name =~ /\G[^<>]*([<>])/g;
0244 
0245         last unless $match;
0246         $pos = pos($a_name);
0247 
0248         $count++ if $1 eq '<';
0249         $count-- if $1 eq '>';
0250     }
0251 
0252     if (defined $pos and defined $start) {
0253         $pos = $pos - $start + 1;
0254         $start = $start - 1;
0255         $a_name =~ s/^(.{$start}).{$pos}/$1/;
0256     }
0257 
0258     my $html;
0259     given ($command) {
0260         when ("head1") {
0261             _add_tree_point( $parser, $expansion, 1 );
0262             $html = $parser->{POD_HTMLEASY}
0263                 ->{ON_HEAD1}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0264         }
0265         when ("head2") {
0266             _add_tree_point( $parser, $expansion, 2 );
0267             $html = $parser->{POD_HTMLEASY}
0268                 ->{ON_HEAD2}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0269         }
0270         when ("head3") {
0271             _add_tree_point( $parser, $expansion, 3 );
0272             $html = $parser->{POD_HTMLEASY}
0273                 ->{ON_HEAD3}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0274         }
0275         when ("head4") {
0276             _add_tree_point( $parser, $expansion, 4 );
0277             $html = $parser->{POD_HTMLEASY}
0278                 ->{ON_HEAD4}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0279         }
0280         when ("begin") {
0281             _add_tree_point( $parser, $expansion, 4 );
0282             $html = $parser->{POD_HTMLEASY}
0283                 ->{ON_BEGIN}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0284         }
0285         when ("end") {
0286             $html = $parser->{POD_HTMLEASY}
0287                 ->{ON_END}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0288         }
0289         when ("over") {
0290             if ( $parser->{INDEX_ITEM} ) {
0291                 $parser->{INDEX_ITEM_LEVEL}++;
0292             }
0293             $html = $parser->{POD_HTMLEASY}
0294                 ->{ON_OVER}( $parser->{POD_HTMLEASY}, $expansion );
0295         }
0296         when ("item") {
0297             if ( $parser->{INDEX_ITEM} ) {
0298                 _add_tree_point( $parser, $expansion,
0299                     ( 3 + ( $parser->{INDEX_ITEM_LEVEL} || 1 ) ) );
0300             }
0301             $html = $parser->{POD_HTMLEASY}
0302                 ->{ON_ITEM}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0303         }
0304         when ("back") {
0305             if ( $parser->{INDEX_ITEM} ) {
0306                 $parser->{INDEX_ITEM_LEVEL}--;
0307             }
0308             $html = $parser->{POD_HTMLEASY}
0309                 ->{ON_BACK}( $parser->{POD_HTMLEASY}, $expansion );
0310         }
0311         when ("for") {
0312             $html = $parser->{POD_HTMLEASY}
0313                 ->{ON_FOR}( $parser->{POD_HTMLEASY}, $expansion, $a_name );
0314         }
0315         when ("include") {
0316             my $file = $parser->{POD_HTMLEASY}
0317                 ->{ON_INCLUDE}( $parser->{POD_HTMLEASY}, $expansion );
0318             if (   -e $file
0319                 && -r _ ) # _ is the last checked filehandle.
0320             {
0321                 $parser->{POD_HTMLEASY}->parse_include($file);
0322             }
0323         }
0324         default {
0325             if ( defined $parser->{POD_HTMLEASY}->{qq{ON_\U$command\E}} ) {
0326                 $html = $parser->{POD_HTMLEASY}
0327                     ->{qq{ON_\U$command\E}}( $parser->{POD_HTMLEASY},
0328                     $expansion );
0329             }
0330             elsif ( $command !~ /^(?:pod|cut)$/imx ) {
0331                 $html = qq{<pre>=$command $expansion</pre>};
0332             }
0333             else { $html = $EMPTY; }
0334         }
0335     };
0336 
0337     if ( $html ne $EMPTY ) {
0338         print { $parser->output_handle() } $html;
0339     }    # [6062]
0340 
0341     return;
0342 }
0343 
0344 ############
0345 # VERBATIM #
0346 ############
0347 
0348 # Overrides verbatim() provided by base class in Pod::Parser
0349 sub verbatim {
0350     my ( $parser, $paragraph, $line_num ) = @_;
0351 
0352     if ( exists $parser->{POD_HTMLEASY}->{IN_BEGIN} ) { return; }
0353     $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER} .= $paragraph;
0354 
0355     return;
0356 }
0357 
0358 sub _verbatim {
0359     my ($parser) = @_;
0360 
0361     if ( exists $parser->{POD_HTMLEASY}->{IN_BEGIN} ) { return; }
0362     my $expansion = $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER};
0363     $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER} = $EMPTY;
0364 
0365     _encode_entities( \$expansion );
0366 
0367     my $html = $parser->{POD_HTMLEASY}
0368         ->{ON_VERBATIM}( $parser->{POD_HTMLEASY}, $expansion );
0369 
0370     # And remove any NUL escapes
0371     _remove_nul_escapes( \$html );
0372 
0373     if ( $html ne $EMPTY ) {
0374         print { $parser->output_handle() } $html;
0375     }    # [6062]
0376 
0377     return;
0378 }
0379 
0380 #############
0381 # TEXTBLOCK #
0382 #############
0383 
0384 # Overrides textblock() provided by base class in Pod::Parser
0385 sub textblock {
0386     my ( $parser, $paragraph, $line_num ) = @_;
0387 
0388     if ( exists $parser->{POD_HTMLEASY}->{IN_BEGIN} ) { return; }
0389     if ( defined $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER} ) {
0390         _verbatim($parser);
0391     }    # [6062]
0392 
0393     my $expansion = $parser->interpolate( $paragraph, $line_num );
0394 
0395     $expansion =~ s{^\s+}{}gsmx;
0396     $expansion =~ s{\s+$}{}gsmx;
0397 
0398     # Encode HTML-specific characters before adding any HTML (eg <p>)
0399     _encode_entities( \$expansion );
0400 
0401     my $html = $parser->{POD_HTMLEASY}
0402         ->{ON_TEXTBLOCK}( $parser->{POD_HTMLEASY}, $expansion );
0403 
0404     # And remove any NUL escapes
0405     _remove_nul_escapes( \$html );
0406 
0407     if ( $html ne $EMPTY ) { print { $parser->output_handle() } $html; }
0408 
0409     return;
0410 }
0411 
0412 #####################
0413 # INTERIOR_SEQUENCE #
0414 #####################
0415 
0416 # Overrides interior_sequence() provided by base class in Pod::Parser
0417 sub interior_sequence {
0418     my ( $parser, $seq_command, $seq_argument, $pod_seq ) = @_;
0419 
0420     my $ret;
0421 
0422     # If we're in the middle of a link then escaping now could break some of
0423     # the link uncracking code.
0424     _encode_entities(\$seq_argument) unless $seq_command eq 'L';
0425 
0426     # Not sure how these get in here but HTML doesn't support &sol; (which is
0427     # simply forward slash
0428     $seq_argument =~ s/&sol;/\//g;
0429 
0430     if ($pod_seq->nested() and $pod_seq->nested()->cmd_name() eq 'L'
0431         and $seq_command ne 'E')
0432     {
0433         # Interpolating into a hyperlink, ignore formatting, unless we are
0434         # processing an escape code
0435         return $seq_argument;
0436     }
0437 
0438     given ($seq_command) {
0439         when ("B") {
0440             $ret = $parser->{POD_HTMLEASY}
0441                 ->{ON_B}( $parser->{POD_HTMLEASY}, $seq_argument );
0442         }
0443         when ("C") {
0444             $ret = $parser->{POD_HTMLEASY}
0445                 ->{ON_C}( $parser->{POD_HTMLEASY}, $seq_argument );
0446         }
0447         when ("E") {
0448             $ret = $parser->{POD_HTMLEASY}
0449                 ->{ON_E}( $parser->{POD_HTMLEASY}, $seq_argument );
0450         }
0451         when ("F") {
0452             $ret = $parser->{POD_HTMLEASY}
0453                 ->{ON_F}( $parser->{POD_HTMLEASY}, $seq_argument );
0454         }
0455         when ("I") {
0456             $ret = $parser->{POD_HTMLEASY}
0457                 ->{ON_I}( $parser->{POD_HTMLEASY}, $seq_argument );
0458         }
0459         when ("L") {
0460             my ( $text, $name, $section, $type ) = _parselink($seq_argument);
0461 
0462             # Held off on escaping these earlier, take care of it now.
0463             _encode_entities(\$text);
0464             _encode_entities(\$section) if $section;
0465             _encode_entities(\$name) if $name;
0466 
0467             $ret = $parser->{POD_HTMLEASY}->{ON_L}(
0468                 $parser->{POD_HTMLEASY},
0469                 $seq_argument, $text, $name, $section, $type, $fnPage,
0470             );
0471         }
0472         when ("S") {
0473             $ret = $parser->{POD_HTMLEASY}
0474                 ->{ON_S}( $parser->{POD_HTMLEASY}, $seq_argument );
0475         }
0476         when ("Z") {
0477             $ret = $parser->{POD_HTMLEASY}
0478                 ->{ON_Z}( $parser->{POD_HTMLEASY}, $seq_argument );
0479         }
0480         default {
0481             if ( defined $parser->{POD_HTMLEASY}->{qq{ON_\U$seq_command\E}} )
0482             {
0483                 $ret = $parser->{POD_HTMLEASY}
0484                     ->{qq{ON_\U$seq_command\E}}( $parser->{POD_HTMLEASY},
0485                     $seq_argument );
0486             }
0487             else {
0488                 $ret = qq{$seq_command<$seq_argument>};
0489             }
0490         }
0491     }
0492 
0493     # Escape HTML-significant characters to prevent them from being escaped
0494     # later.
0495     _nul_escape( \$ret );
0496 
0497     return $ret;
0498 }
0499 
0500 ########################
0501 # PREPROCESS_PARAGRAPH #
0502 ########################
0503 
0504 # Overrides preprocess_paragraph() provided by base class in Pod::Parser
0505 # NB: the text is _not altered.
0506 sub preprocess_paragraph {
0507     my $parser = shift;
0508     my ( $text, $line_num ) = @_;
0509 
0510     if ( $parser->{POD_HTMLEASY}{INFO_COUNT} == 3 ) {
0511         return $text;
0512     }
0513 
0514     if ( not exists $parser->{POD_HTMLEASY}{PACKAGE} ) {
0515         if ( $text =~ m{package}smx ) {
0516             my ($pack) = $text =~ m{(\w+(?:::\w+)*)}smx;
0517             if ( defined $pack ) {
0518                 $parser->{POD_HTMLEASY}{PACKAGE} = $pack;
0519                 $parser->{POD_HTMLEASY}{INFO_COUNT}++;
0520             }
0521         }
0522     }
0523 
0524     if ( not exists $parser->{POD_HTMLEASY}{VERSION} ) {
0525         if ( $text =~ m{VERSION}smx ) {
0526             my ($ver) = $text =~ m{(\d)(?:\.\d*)?}smx;
0527             if ( defined $ver ) {
0528                 $parser->{POD_HTMLEASY}{VERSION} = $ver;
0529                 $parser->{POD_HTMLEASY}{INFO_COUNT}++;
0530             }
0531         }
0532     }
0533 
0534     # This situation is created by evt_on_head1()
0535     if (    ( exists $parser->{POD_HTMLEASY}{TITLE} )
0536         and ( not defined $parser->{POD_HTMLEASY}{TITLE} ) )
0537     {
0538         my @lines = split m{\n}smx, $text;
0539         my $tmp_text = shift @lines;
0540         if ( not defined $tmp_text ) { return $text; }
0541         $tmp_text =~ s{^\s*}{}gsmx;   # delete surrounding whitespace
0542         $tmp_text =~ s{\s*$}{}gsmx;   # delete surrounding whitespace
0543         $parser->{POD_HTMLEASY}{TITLE} = $tmp_text;
0544         $parser->{POD_HTMLEASY}{INFO_COUNT}++;
0545     }
0546 
0547     return $text;
0548 }
0549 
0550 ##################
0551 # _PARSE_SECTION #
0552 ##################
0553 
0554 # Parse a link that is not a URL to get the name and/or section
0555 # Algorithm may be found in perlpodspec. "About L<...> Codes"
0556 
0557 sub _parse_section {
0558     my $link = shift;
0559     $link =~ s{^\s*}{}sm;    # delete surrounding whitespace
0560     $link =~ s{\s*$}{}sm;    # delete surrounding whitespace
0561 
0562     # L<"FooBar"> is a the way to specify a section without a name.
0563     # However, L<Foo Bar> is possible, though deprecated. See below.
0564     if ($link =~ m/^"/) {
0565         $link =~ s{^"+\s*}{}sm;  # strip the "s
0566         $link =~ s{\s*"+$}{}sm;
0567         return ( undef, $link );
0568     }
0569 
0570     # So now we have either a name by itself, or name/section
0571     my ( $name, $section ) = split m[\s*/\s*]sm, $link, 2;
0572 
0573     # Trim leading and trailing whitespace and quotes from section
0574     if ($section) {
0575         $section =~ s{"}{}gsm;                # quotes
0576         $section =~ s{^\s*}{}sm;    # delete surrounding whitespace
0577         $section =~ s{\s*$}{}sm;    # delete surrounding whitespace
0578     }    # new leading/trailing
0579 
0580 # Perlpodspec observes that and acceptable way to distinguish between L<name> and
0581 # L<section> is that if the link contains any whitespace, then it is a section.
0582 # The construct L<section> is deprecated.
0583     if ( $name && $name =~ m{\s}sm && !defined $section ) {
0584         $section = $name;
0585         $name    = undef;
0586     }
0587 
0588     return ( $name, $section );
0589 }
0590 
0591 ###############
0592 # _INFER_TEXT #
0593 ###############
0594 
0595 # Infer the text content of a L<...> with no text| part (ie a text|-less link)
0596 # By definition (?) either name or section is nonempty, Algorithm from perlpodspec
0597 
0598 sub _infer_text {
0599     my ( $name, $section ) = @_;
0600 
0601     if ($name) {
0602         return $section
0603             ? "\"$section\" in $name"
0604             : $name;
0605     }
0606 
0607     return $section;
0608 }
0609 
0610 ##############
0611 # _PARSELINK #
0612 ##############
0613 
0614 # Parse the content of L<...> and return
0615 #   The text label
0616 #   The name or URL
0617 #   The section (if relevant)
0618 #   The type of link discovered: url, man or pod
0619 
0620 sub _parselink {
0621     my $link = shift;
0622     my $text;
0623 
0624     # Squeeze out multiple spaces
0625     $link =~ s{\s+}{$SPACE}gsm;
0626 
0627     if ( $link =~ m{\|}smx ) {
0628 
0629         # Link is in the form "L<Foo|Foo::Bar>"
0630         ( $text, $link ) = split m{\|}sm, $link, 2;
0631     }
0632 
0633 # Check for a generalized URL. The regex is defined in perlpodspec.
0634 # Quoting perlpodspec: "Authors wanting to link to a particular (absolute) URL, must do so
0635 # only with "L<scheme:...>" codes and must not attempt "L<Some Site Name|scheme:...>"
0636 # Consequently, although $text might be nonempty, we ignore it.
0637     if ($link =~ m{
0638                     \A      # The beginning of the string
0639                     \w+     # followed by some alphanumerics, which would be the protocol (or scheme)
0640                     :       # literal ":"
0641                     [^:\s]  # one char that is neither a ":" or whitespace
0642                     \S*     # maybe some non-whitespace
0643                     \z      # the end of the string
0644                    }smx
0645         )
0646     {
0647         return ( $link, $link, undef, q{url} );
0648     }
0649 
0650     # OK, we've eliminated URLs, so we must be dealing with something else
0651 
0652     my ( $name, $section ) = _parse_section($link);
0653     if ( not defined $text ) { $text = _infer_text( $name, $section ); }
0654 
0655 # A link with parenthesized non-whitespace is assumed to be a manpage reference
0656 # (per perlpodspec))
0657     my $type =
0658         ( $name && $name =~ m{\(\S*\)}smx )
0659         ? q{man}
0660         : q{pod};
0661 
0662     return ( $text, $name, $section, $type );
0663 }
0664 
0665 ###################
0666 # _ADD_TREE_POINT #
0667 ###################
0668 
0669 sub _add_tree_point {
0670     my ( $parser, $name, $level ) = @_;
0671     $level ||= 1;
0672 
0673     if ( $level == 1 ) {
0674         $parser->{POD_HTMLEASY}->{INDEX}{p}
0675             = $parser->{POD_HTMLEASY}->{INDEX}{tree};
0676     }
0677     else {
0678         if ( exists $parser->{POD_HTMLEASY}->{INDEX}{p} ) {
0679             while ( $parser->{POD_HTMLEASY}
0680                 ->{INDEX}{l}{ $parser->{POD_HTMLEASY}->{INDEX}{p} }
0681                 > ( $level - 1 ) )
0682             {
0683                 last
0684                     if !$parser->{POD_HTMLEASY}
0685                     ->{INDEX}{b}{ $parser->{POD_HTMLEASY}->{INDEX}{p} };
0686                 $parser->{POD_HTMLEASY}->{INDEX}{p} = $parser->{POD_HTMLEASY}
0687                     ->{INDEX}{b}{ $parser->{POD_HTMLEASY}->{INDEX}{p} };
0688             }
0689         }
0690     }
0691 
0692     my $array = [];
0693 
0694     $parser->{POD_HTMLEASY}->{INDEX}{l}{$array} = $level;
0695     $parser->{POD_HTMLEASY}->{INDEX}{b}{$array}
0696         = $parser->{POD_HTMLEASY}->{INDEX}{p};
0697 
0698     push @{ $parser->{POD_HTMLEASY}->{INDEX}{p} }, $name, $array;
0699     $parser->{POD_HTMLEASY}->{INDEX}{p} = $array;
0700 
0701     return;
0702 
0703 }
0704 
0705 #############
0706 # BEGIN_POD #
0707 #############
0708 
0709 # Overrides begin_pod() provided by base class in Pod::Parser
0710 sub begin_pod {
0711     my ($parser) = @_;
0712 
0713     if ( $parser->{POD_HTMLEASY_INCLUDE} ) { return; }
0714 
0715     delete $parser->{POD_HTMLEASY}->{INDEX};
0716     $parser->{POD_HTMLEASY}->{INDEX} = { tree => [] };
0717 
0718     return 1;
0719 }
0720 
0721 ###########
0722 # END_POD #
0723 ###########
0724 
0725 # Overrides end_pod() provided by base class in Pod::Parser
0726 sub end_pod {
0727     my ($parser) = @_;
0728 
0729     if ( $parser->{POD_HTMLEASY_INCLUDE} ) { return; }
0730 
0731     if ( defined $parser->{POD_HTMLEASY}->{VERBATIM_BUFFER} ) {
0732         _verbatim($parser);
0733     }
0734 
0735     my $tree = $parser->{POD_HTMLEASY}->{INDEX}{tree};
0736 
0737     delete $parser->{POD_HTMLEASY}->{INDEX};
0738 
0739     $parser->{POD_HTMLEASY}->{INDEX} = $tree;
0740 
0741     return 1;
0742 }
0743 
0744 ###########
0745 # _ERRORS #
0746 ###########
0747 
0748 sub _errors {
0749     my ( $parser, $error ) = @_;
0750 
0751     carp "$error";
0752     $error =~ s{^\s*\**\s*errors?:?\s*}{}ismx;
0753     $error =~ s{\s+$}{}smx;
0754 
0755     my $html = $parser->{POD_HTMLEASY}
0756         ->{ON_ERROR}( $parser->{POD_HTMLEASY}, $error );
0757     if ( $html ne $EMPTY ) {
0758         print { $parser->output_handle() } $html, $NL;
0759     }
0760 
0761     return 1;
0762 }
0763 
0764 ###########
0765 # DESTROY #
0766 ###########
0767 
0768 sub DESTROY { }
0769 
0770 #######
0771 # END #
0772 #######
0773 
0774 1;
0775 
0776 #############################################################################
0777 ## Name:        HtmlEasy.pm
0778 ## Purpose:     Pod::HtmlEasy
0779 ## Author:      Graciliano M. P.
0780 ## Modified by: Geoffrey Leach
0781 ## Created:     2004-01-11
0782 ## Updated:     2007-02-28
0783 ## Copyright:   (c) 2004 Graciliano M. P.
0784 ## Licence:     This program is free software; you can redistribute it and/or
0785 ##              modify it under the same terms as Perl itself
0786 #############################################################################
0787 
0788 package Pod::HtmlEasy;
0789 use 5.008;
0790 
0791 use Carp;
0792 use English qw{ -no_match_vars };
0793 
0794 use strict;
0795 use warnings;
0796 
0797 our $VERSION = 0.09;    # Also appears in "=head1 VERSION" in the POD below
0798 
0799 our $EMPTY = q{};
0800 our $NL    = qq{\n};
0801 our $NUL   = qq{\0};
0802 our $SPACE = q{ };
0803 
0804 ########
0805 # VARS #
0806 ########
0807 
0808 my %BODY_DEF = (
0809     bgcolor => '#FFFFFF',
0810     text    => '#000000',
0811     link    => '#000000',
0812     vlink   => '#000066',
0813     alink   => '#FF0000',
0814 );
0815 
0816 # This keeps track of valid options
0817 my %OPTS = (
0818     basic_entities  => 1,
0819     body            => 1,
0820     common_entities => 1,
0821     css             => 1,
0822     faq_page        => 0,
0823     function_page   => 0,
0824     index           => 1,
0825     index_item      => 1,
0826     no_css          => 1,
0827     no_generator    => 1,
0828     no_index        => 1,
0829     only_content    => 1,
0830     parserwarn      => 1,
0831     title           => 1,
0832     top             => 1,
0833 );
0834 
0835 my $output_file;
0836 
0837 #######
0838 # CSS #
0839 #######
0840 
0841 my $CSS_DEF = q`
0842 /*
0843 ** HTML elements
0844 */
0845 
0846 body {
0847     margin: 10px;
0848     padding: 0;
0849     text-align: center;
0850     font-size: 0.8em;
0851     font-family: "Bitstream Vera Sans", "Lucida Grande", "Trebuchet MS", sans-serif;
0852     color: #535353;
0853     background: #ffffff;
0854 }
0855 
0856 
0857 /*
0858 ** HTML Tags
0859 */
0860 
0861 h1, h2, h3, h4
0862 {
0863     padding: 0;
0864     text-align: left;
0865     font-weight: bold;
0866     color: #f7800a;
0867     background: transparent;
0868 }
0869 
0870 h1 {
0871     margin: 0 0 0.3em 0;
0872     font-size: 1.7em;
0873 }
0874 
0875 h1.name + p {
0876     font-size: larger;
0877     font-style: oblique;
0878 }
0879 
0880 h2, h3, h4 {
0881     margin: 1.3em 0 0 0.3em
0882 }
0883 
0884 h2 {
0885     font-size: 1.5em;
0886 }
0887 
0888 h3 {
0889     font-size: 1.4em;
0890 }
0891 
0892 h4 {
0893     font-size: 1.3em;
0894 }
0895 
0896 h5 {
0897     font-size: 1.2em;
0898 }
0899 
0900 a:link {
0901     padding-bottom: 0;
0902     text-decoration: none;
0903     color: #0057ae;
0904 }
0905 
0906 a:visited {
0907     padding-bottom: 0;
0908     text-decoration: none;
0909     color: #644A9B;
0910 }
0911 
0912 
0913 a[href]:hover {
0914     text-decoration: underline;
0915 }
0916 
0917 hr {
0918     margin: 0.3em 1em 0.3em 1em;
0919     height: 1px;
0920     border: #bcbcbc dashed;
0921     border-width: 0 0 1px 0;
0922 }
0923 
0924 pre {
0925     display: block;
0926     margin: 0.3em;
0927     padding: 0.3em;
0928     font-size: 1em;
0929     color: #000000;
0930     text-align: left;
0931     background: #f9f9f9;
0932     border: #2f6fab dashed;
0933     border-width: 1px;
0934     overflow: auto;
0935     line-height: 1.1em;
0936 }
0937 
0938 input, textarea, select {
0939     margin: 0.2em;
0940     padding: 0.1em;
0941     color: #888888;
0942     background: #ffffff;
0943     border: 1px solid;
0944 }
0945 
0946 blockquote {
0947     margin: 0.3em;
0948     padding-left: 2.5em;
0949     background: transparent;
0950 }
0951 
0952 del {
0953     color: #800000;
0954     text-decoration: line-through;
0955 }
0956 
0957 dt {
0958     font-weight: bold;
0959     font-size: 1.05em;
0960     color: #0057ae;
0961 }
0962 
0963 dd {
0964     margin-left: 1em;
0965 }
0966 
0967 p {
0968     margin-top: 0.5em;
0969     margin-bottom: 0.9em;
0970     text-align: justify;
0971 }
0972 fieldset {
0973     border: #cccccc 1px solid;
0974 }
0975 
0976 li {
0977     text-align: left;
0978 }
0979 
0980 fieldset {
0981     margin-bottom: 1em;
0982     padding: .5em;
0983 }
0984 
0985 form {
0986     margin: 0;
0987     padding: 0;
0988 }
0989 
0990 hr {
0991     height: 1px;
0992     border: #888888 1px solid;
0993     background: #888888;
0994     margin: 0.5em 0 0.5em 0 ;
0995 }
0996 
0997 .toc a {
0998     text-decoration: none;
0999 }
1000 
1001 .toc li {
1002     list-style-type: none;
1003     line-height: larger;
1004 }
1005 
1006 img {
1007     border: 0;
1008 }
1009 
1010 table {
1011     border-collapse: collapse;
1012     font-size: 1em;
1013 }
1014 
1015 th {
1016     text-align: left;
1017     padding-right: 1em;
1018     border: #cccccc solid;
1019     border-width: 0 0 3px 0;
1020 }
1021 `;
1022 
1023 ###############
1024 # DEFAULT_CSS #
1025 ###############
1026 
1027 sub default_css {
1028     return $CSS_DEF;
1029 }
1030 
1031 #######################
1032 # _ORGANIZE_CALLBACKS #
1033 #######################
1034 
1035 sub _organize_callbacks {
1036     my $this = shift;
1037 
1038     $this->{ON_B} = \&evt_on_B;
1039     $this->{ON_C} = \&evt_on_C;
1040     $this->{ON_E} = \&evt_on_E;
1041     $this->{ON_F} = \&evt_on_F;
1042     $this->{ON_I} = \&evt_on_I;
1043     $this->{ON_L} = \&evt_on_L;
1044     $this->{ON_S} = \&evt_on_S;
1045     $this->{ON_X} = \&evt_on_X;    # [20078]
1046     $this->{ON_Z} = \&evt_on_Z;
1047 
1048     $this->{ON_HEAD1} = \&evt_on_head1;
1049     $this->{ON_HEAD2} = \&evt_on_head2;
1050     $this->{ON_HEAD3} = \&evt_on_head3;
1051     $this->{ON_HEAD4} = \&evt_on_head4;
1052 
1053     $this->{ON_VERBATIM}  = \&evt_on_verbatim;
1054     $this->{ON_TEXTBLOCK} = \&evt_on_textblock;
1055 
1056     $this->{ON_OVER} = \&evt_on_over;
1057     $this->{ON_ITEM} = \&evt_on_item;
1058     $this->{ON_BACK} = \&evt_on_back;
1059 
1060     $this->{ON_FOR}   = \&evt_on_for;
1061     $this->{ON_BEGIN} = \&evt_on_begin;
1062     $this->{ON_END}   = \&evt_on_end;
1063 
1064     $this->{ON_INDEX_NODE_START} = \&evt_on_index_node_start;
1065     $this->{ON_INDEX_NODE_END}   = \&evt_on_index_node_end;
1066 
1067     $this->{ON_INCLUDE} = \&evt_on_include;
1068     $this->{ON_URI}     = \&evt_on_uri;
1069 
1070     $this->{ON_ERROR} = \&evt_on_error;
1071 
1072     return;
1073 }
1074 
1075 #######
1076 # NEW #
1077 #######
1078 
1079 sub new {
1080     my $this = shift;
1081     return $this if ref $this;
1082     my $class = $this || __PACKAGE__;
1083     $this = bless {}, $class;
1084 
1085     my (%args) = @_;
1086     _organize_callbacks($this);
1087 
1088     # Backwards compatibility
1089     if ( exists $args{on_verbatin} ) {
1090         $this->{ON_VERBATIM} = $args{on_verbatin};
1091     }
1092 
1093     foreach my $key ( keys %args ) {
1094 
1095         # Add in any ON_ callbacks
1096         if ( $key =~ m{^on_(\w+)$}ismx ) {
1097             my $cmd = uc $1;
1098             $this->{qq{ON_$cmd}} = $args{$key};
1099         }
1100         elsif ( $key =~ m{^(?:=(\w+)|(\w)<>)$}smx ) {
1101             my $cmd = uc $1 || $2;
1102             $this->{$cmd} = $args{$key};
1103         }
1104     }
1105 
1106     return $this;
1107 }
1108 
1109 ############
1110 # POD2HTML #
1111 ############
1112 
1113 sub pod2html {
1114     my $this = shift;
1115     my $file = shift;
1116 
1117     # Assume a non-option second arg is a file name
1118     my $save = (exists $OPTS{ $_[0] } ? undef: shift) if defined $_[0];
1119     my %args = @_;
1120 
1121     # Check options for validity
1122     foreach my $key ( keys %args ) {
1123         if ( not exists $OPTS{$key} ) {
1124             carp qq{option $key is not supported};
1125         }
1126     }
1127 
1128     # No /x please
1129     if ( defined $save && $save =~ m{$NL}sm ) {
1130 
1131         # Is this a M$ way of saying "nothing there"?
1132         $save = undef;
1133     }
1134 
1135     # This will fall through to Pod::Parser::new
1136     # which is the base for Pod::HtmlEasy::Parser
1137     # and Pod::HtmlEasy::Parser does not implement new()
1138     my $parser = Pod::HtmlEasy::Parser->new();
1139 
1140     $parser->errorsub( sub { Pod::HtmlEasy::Parser::errors( $parser, @_ ); }
1141     );
1142 
1143     # Pod::Parser wiii complain about multiple blank lines in the input
1144     # which is moderately annoying
1145     if ( exists $args{parserwarn} ) { $parser->parseopts( -warnings => 1 ); }
1146 
1147     # This allows us to search for non-POD stuff is preprocess_paragraph
1148     $parser->parseopts( -want_nonPODs => 1 );
1149 
1150     if (exists $args{'function_page'}) { $parser->setFunctionPage($args{'function_page'}); }
1151 
1152     # This puts a subsection in the $parser hash that will record data
1153     # that is "local" to this code.  Throughout, $parser will refer to
1154     # Pod::Parser and $this to Pod::HtmlEasy
1155     $parser->{POD_HTMLEASY} = $this;
1156 
1157     if ( exists $args{index_item} ) { $parser->{INDEX_ITEM} = 1; }
1158     if ( exists $args{basic_entities} ) {
1159         carp q{"basic_entities" is deprecated.};
1160     }
1161     if ( exists $args{common_entities} ) {
1162         carp q{"common_entities" is deprecated.};
1163     }
1164 
1165     # *HTML supplies a PRINT method that's used by the parser to do output
1166     # It gets accumulated into HTML, which is tied to $output.
1167     # You'll also see calls to  print {$parser->output_handle()} ...
1168     # which accomplishes the same thing. When all is said and done, the output
1169     # of the parse winds up in $output declared below, and used in the construction
1170     # of @html.
1171 
1172     my $output = [];
1173     local *HTML;
1174     tie *HTML => 'Pod::HtmlEasy::TieHandler', $output;
1175     my $html = \*HTML;
1176     $this->{TIEDOUTPUT} = $html;
1177 
1178     my $title = $args{title};
1179     if ( ref $file eq q{GLOB} ) {    # $file is an open filehandle
1180         if ( not defined $title ) { $title = q{<DATA>}; }
1181     }
1182     else {
1183         if ( !-e $file ) {
1184             carp qq{No file $file};
1185             return;
1186         }
1187         if ( not defined $title ) { $title = $file; }
1188     }
1189 
1190     # Build the header to the HTML file
1191     my @html;
1192     push @html,
1193         qq{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">$NL};
1194     push @html, qq{<html><head>$NL};
1195     push @html,
1196         qq{<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">$NL};
1197 
1198     if ( not exists $args{no_generator} ) {
1199         push @html,
1200             qq{<meta name="GENERATOR" content="kio_perldoc Pod::HtmlEasy/$VERSION Pod::Parser/$Pod::Parser::VERSION Perl/$] [$^O]">$NL};
1201     }
1202     push @html, qq{<title>$title</title>$NL};
1203     my $title_line_ref = \$html[-1];
1204     push @html, _organize_css( \%args );
1205     push @html, qq{</head>$NL};
1206     if ( not exists $args{only_content} ) {
1207         push @html, _organize_body( \%args );
1208     }
1209 
1210     delete $this->{UPARROW};
1211     delete $this->{UPARROW_FILE};
1212     push @html, qq{<div id="header"><div id="header_top"><div><div>$NL};
1213     push @html, qq{<img src="help:/kdoctools5-common/part_of_the_kde_family_horizontal_190.png" alt="Part of the KDE family"> $title - KDE Perldoc Viewer</div></div></div></div>};
1214     push @html, q{<div class="pod">};
1215 
1216     if ( exists $args{top} ) {
1217         push @html, qq{$NL<a name='_top'></a>$NL};
1218         if ( -e $args{top} ) {
1219             $this->{UPARROW_FILE} = $args{top};
1220         }
1221         else {
1222             $this->{UPARROW} = $args{top};
1223         }
1224     }
1225 
1226     if ($args{'faq_page'}) {
1227         push @html, qq{$NL<p>The following FAQ results were found:</p>$NL};
1228     }
1229 
1230     # Avoid carry-over on multiple files
1231     delete $this->{IN_BEGIN};
1232     delete $this->{PACKAGE};
1233     delete $this->{TITLE};
1234     delete $this->{VERSION};
1235     $this->{INFO_COUNT} = 0;
1236 
1237     # A filehandle as both args is not documented, but is supported
1238     # Everything that Pod::Parser prints winds up in $output
1239     $parser->parse_from_file( $file, $html );
1240 
1241     # If there's a head1 NAME, we've picked this up during processing
1242     if ( defined $this->{TITLE} && length $this->{TITLE} > 0 ) {
1243         ${$title_line_ref} = qq{<title>$this->{TITLE}</title>$NL};
1244     }
1245 
1246   # Note conflict here: user can specify an index, and no_index; no_index wins
1247     if ( not exists $args{index} ) { $args{index} = $this->build_index(); }
1248     if ( exists $args{no_index} )  { $args{index} = $EMPTY; }
1249 
1250     if ( $args{'faq_page'} && not @{$output} ) {
1251         @{$output} = ("<b>None</b>");
1252     }
1253 
1254     my $kio_perldoc_version = $ENV{'KIO_PERLDOC_VERSION'} || "Unknown";
1255 
1256     push @html, qq{$args{index}$NL};
1257     push @html, @{$output};                      # The pod converted to HTML
1258     push @html, qq{</div><div id="footer"><div id="footer_text">Generated by kio_perldoc, version $kio_perldoc_version</div></div>};
1259     push @html, qq{</body></html>$NL};
1260 
1261     delete $this->{TIEDOUTPUT};
1262     close $html or carp q{Could not close html};
1263     untie $html or carp q{Could not untie html};
1264 
1265     if ( defined $save ) {
1266         open my $out, q{>}, $save or croak qq{Unable to open $save - $!};
1267         print {$out} @html;
1268         close $out;
1269     }
1270 
1271     return wantarray ? @html : join $EMPTY, @html;
1272 }
1273 
1274 #################
1275 # PARSE_INCLUDE #
1276 #################
1277 
1278 sub parse_include {
1279     my $this = shift;
1280     my $file = shift;
1281 
1282     my $parser = Pod::HtmlEasy::Parser->new();
1283     $parser->errorsub( sub { Pod::HtmlEasy::Parser::errors( $parser, @_ ); }
1284     );
1285     $parser->{POD_HTMLEASY}         = $this;
1286     $parser->{POD_HTMLEASY_INCLUDE} = 1;
1287 
1288     $parser->parse_from_file( $file, $this->{TIEDOUTPUT} );
1289 
1290     return 1;
1291 }
1292 
1293 ##############
1294 # WALK_INDEX #
1295 ##############
1296 
1297 sub walk_index {
1298     my ( $this, $tree, $on_open, $on_close, $output ) = @_;
1299 
1300     my $i = 0;
1301     while ( $i < @{$tree} ) {
1302         my $nk =
1303             ref( ${$tree}[ $i + 1 ] ) eq q{ARRAY}
1304             ? @{ ${$tree}[ $i + 1 ] }
1305             : undef;
1306         $nk = $nk >= 1 ? 1 : undef;
1307 
1308         my $a_name = ${$tree}[$i];
1309         $a_name =~ s{<.*?>}{}gsmx;
1310 
1311         #$a_name =~ s{&\w+;}{}gsmx;
1312         #$a_name =~ s{\W+}{-}gsmx;
1313 
1314         if ($on_open) {
1315             my $ret = $on_open->( $this, ${$tree}[$i], $a_name, $nk );
1316             if ( $output and defined $ret ) {
1317                 ${$output} .= $ret;
1318             }    # [6062]
1319         }
1320 
1321         if ($nk) {
1322             walk_index( $this, ${$tree}[ $i + 1 ],
1323                 $on_open, $on_close, $output );
1324         }
1325 
1326         if ($on_close) {
1327             my $ret = $on_close->( $this, ${$tree}[$i], $a_name, $nk );
1328             if ( $output and defined $ret ) {
1329                 ${$output} .= $ret;
1330             }    # [6062]
1331         }
1332         $i += 2;
1333     }
1334     return;
1335 }
1336 
1337 ###############
1338 # BUILD_INDEX #
1339 ###############
1340 
1341 sub build_index {
1342     my $this = shift;
1343 
1344     my $index = $EMPTY;    # [6062]
1345     $this->walk_index(
1346         $this->{INDEX},
1347         $this->{ON_INDEX_NODE_START},
1348         $this->{ON_INDEX_NODE_END}, \$index
1349     );
1350 
1351     return qq{<div class="toc">$NL<ul>$NL$index</ul>$NL</div>$NL};
1352 }
1353 
1354 #################
1355 # _ORGANIZE_BODY #
1356 #################
1357 
1358 sub _organize_body {
1359     my $args_ref = shift;
1360 
1361     my ( $body, %body );
1362 
1363     $body = $EMPTY;
1364     if ( ref $args_ref->{body} eq q{HASH} ) {
1365         %body = %BODY_DEF;
1366         my %body_attr = %{ $args_ref->{body} };
1367         foreach my $key ( keys %body_attr ) {
1368             $body{$key} = $body_attr{$key};
1369         }
1370     }
1371     elsif ( !exists $args_ref->{body} ) { %body = %BODY_DEF; }
1372 
1373     if (%body) {
1374         foreach my $key ( sort keys %body ) {
1375             if ( $body{$key} !~ m{\#}smx && defined $BODY_DEF{$key} ) {
1376                 $body{$key} = qq{#$body{$key}};
1377             }
1378             my $value =
1379                 $body{$key} !~ m{"}smx
1380                 ? qq{"$body{$key}"}
1381                 : qq{'$body{$key}'};
1382             $body .= qq{ $key=$value};
1383         }
1384     }
1385     else { $body = $args_ref->{body}; }
1386 
1387     return qq{<body $body>};
1388 }
1389 
1390 ################
1391 # ORGANIZE_CSS #
1392 ################
1393 
1394 sub _organize_css {
1395     my $perldoc_css = $ENV{'KIO_PERLDOC_CSSLOCATION'};
1396     chomp $perldoc_css;
1397 
1398     return <<"EOL";
1399 <link rel="stylesheet" href="help:/kdoctools5-common/kde-default.css" type="text/css">
1400 <link rel="stylesheet" href="file://$perldoc_css" type="text/css">
1401 <style>
1402     #header_top {
1403         background-image: url("help:/kdoctools5-common/top.jpg");
1404     }
1405     #header_top div {
1406         background-image: url("help:/kdoctools5-common/top-left.jpg");
1407     }
1408     #header_top div div {
1409         background-image: url("help:/kdoctools5-common/top-right.jpg");
1410     }
1411     div.pod {
1412         margin-left: 5em;
1413         margin-right: 5em;
1414     }
1415 </style>
1416 EOL
1417 }
1418 
1419 ##################
1420 # EVENT SUPPORT  #
1421 ##################
1422 
1423 sub do_title {
1424     my $this = shift;
1425     my ( $txt, $a_name ) = @_;
1426 
1427     # This happens only on the _first_ head1 NAME
1428     if ( ( not exists $this->{TITLE} ) and ( $txt =~ m{\ANAME}smx ) ) {
1429         my ($title) = $txt =~ m{\ANAME\s+(.*)}smx;
1430         if ( defined $title ) {
1431 
1432             # Oh, goody
1433             $title =~ s{^\s*}{}gsmx;  # delete surrounding whitespace
1434             $title =~ s{\s*$}{}gsmx;  # delete surrounding whitespace
1435             $this->{TITLE} = $title;
1436         }
1437         else {
1438 
1439 # If we don't get anything off of NAME, it will be filled in by preprocess_paragraph()
1440             $this->{TITLE} = undef;
1441         }
1442     }
1443     return;
1444 }
1445 
1446 ##################
1447 # DEFAULT EVENTS #
1448 ##################
1449 
1450 sub evt_on_head1 {
1451     my $this = shift;
1452     my ( $txt, $a_name ) = @_;
1453 
1454     if ( not defined $txt ) { $txt = $EMPTY; }
1455 
1456     do_title( $this, $txt, $a_name );
1457 
1458     if ( exists $this->{UPARROW_FILE} ) {
1459         return "<h1><a href='#_top'
1460                  title='click to go to top of document' 
1461                  name='$a_name'>$txt<img src='$this->{UPARROW_FILE}'
1462                  alt=&uArr;></a></h1>$NL";
1463     }
1464     elsif ( exists $this->{UPARROW} ) {
1465         return qq{<h1><a href='#_top'
1466                   title='click to go to top of document' 
1467                   name='$a_name'>$txt&$this->{UPARROW};</a></h1>$NL};
1468     }
1469 
1470     my $style = '';
1471     $style = 'class="name"' if $txt =~ m{\ANAME};
1472 
1473     return qq{<a name='$a_name'></a><h1 $style>$txt</h1>$NL};
1474 }
1475 
1476 sub evt_on_head2 {
1477     my $this = shift;
1478     my ( $txt, $a_name ) = @_;
1479     return qq{<a name='$a_name'></a><h2>$txt</h2>$NL$NL};
1480 }
1481 
1482 sub evt_on_head3 {
1483     my $this = shift;
1484     my ( $txt, $a_name ) = @_;
1485     return qq{<a name='$a_name'></a><h3>$txt</h3>$NL$NL};
1486 }
1487 
1488 sub evt_on_head4 {
1489     my $this = shift;
1490     my ( $txt, $a_name ) = @_;
1491     return qq{<a name='$a_name'></a><h4>$txt</h4>$NL$NL};
1492 }
1493 
1494 sub evt_on_begin {
1495     my $this = shift;
1496     my ( $txt, $a_name ) = @_;
1497     $this->{IN_BEGIN} = 1;
1498     return $EMPTY;
1499 }
1500 
1501 sub evt_on_end {
1502     my $this = shift;
1503     my ( $txt, $a_name ) = @_;
1504     delete $this->{IN_BEGIN};
1505     return $EMPTY;
1506 }
1507 
1508 sub evt_on_L {
1509     my ($this, $L, $text, $page, $section, $type, $fnPage) = @_;
1510 
1511     Pod::HtmlEasy::Parser::_encode_entities(\$page);
1512 
1513     if($type eq 'pod') {
1514         $page = '' unless defined $page;
1515 
1516         if($fnPage and not $page) {
1517             $page = "functions/";
1518             $section = "" unless defined $section;
1519         }
1520         else {
1521             $section = defined $section ? "#$section" : "";
1522         }
1523 
1524         # Keep later escaping functions from incorrectly messing this up.
1525         $section =~ s/^(.)/$1$NUL/;
1526 
1527         # The browser knows what to do for simple links in the same page,
1528         # and knows how to do it better than we do.
1529         return "<a href=\"$section\">$text</a>" unless $page;
1530 
1531         $page =~ s/^(.)/$1$NUL/;
1532         return "<a href=\"perldoc:$page$section\">$text</a>";
1533     }
1534     elsif($type eq 'man') {
1535         return "<a href=\"man:/$page\">$text</a>";
1536     }
1537     elsif($type eq 'url') {
1538         # Keep later escaping functions from incorrectly messing this up.
1539         $page =~ s/^(.)/$1$NUL/;
1540         $text =~ s/^(.)/$1$NUL/;
1541 
1542         return "<a href=\"$page\" target=\"_blank\">$text</a>";
1543     }
1544 }
1545 
1546 sub evt_on_B {
1547     my $this = shift;
1548     my $txt  = shift;
1549     return qq{<b>$txt</b>};
1550 }
1551 
1552 sub evt_on_I {
1553     my $this = shift;
1554     my $txt  = shift;
1555     return qq{<i>$txt</i>};
1556 }
1557 
1558 sub evt_on_C {
1559     my $this = shift;
1560     my $txt  = shift;
1561 
1562     # Who would put IMG tags in this?
1563     return $EMPTY if $txt =~ m{<IMG}i;
1564     return "<tt>$txt</tt>";
1565 }
1566 
1567 sub evt_on_E {
1568     my $this = shift;
1569     my $txt  = shift;
1570 
1571     # Hey guess what, not all &foo; entities are part of HTML!  So don't go
1572     # throwing them out without checking.
1573     return "/" if $txt eq '&sol;';
1574 
1575     $txt =~ s{^&}{}smx;
1576     $txt =~ s{;$}{}smx;
1577     if ( $txt =~ m{^\d+$}smx ) { $txt = qq{#$txt}; }
1578     return qq{$NUL&$txt;};
1579 }
1580 
1581 sub evt_on_F {
1582     my $this = shift;
1583     my $txt  = shift;
1584 
1585     # Some pod encoders put hyperlinks in F<>??
1586     return qq{<a href="$txt">$txt</a>} if $txt =~ m'^http://';
1587     return qq{<b><i>$txt</i></b>};
1588 }
1589 
1590 sub evt_on_S {
1591     my $this = shift;
1592     my $txt  = shift;
1593     $txt =~ s{$SPACE}{&nbsp;}gs;
1594     return $txt;
1595 }
1596 
1597 sub evt_on_X { return $EMPTY; }    # [20078]
1598 
1599 sub evt_on_Z { return $EMPTY; }
1600 
1601 sub evt_on_verbatim {
1602     my $this = shift;
1603     my $txt  = shift;
1604 
1605     return if exists $this->{IN_BEGIN};
1606 
1607     # Multiple empty lines are parsed as verbatim text by Pod::Parser
1608     # And will show up as empty <pre> blocks, which is mucho messy
1609     {
1610         local $RS = $EMPTY;
1611         chomp $txt;
1612     }
1613 
1614     if ( not length $txt ) { return $EMPTY; }
1615     return qq{<pre>$txt</pre>$NL};
1616 }
1617 
1618 sub evt_on_textblock {
1619     my $this = shift;
1620     my $txt  = shift;
1621     return if exists $this->{IN_BEGIN};
1622     return qq{<p>$txt</p>$NL};
1623 }
1624 
1625 sub evt_on_over {
1626     my $this  = shift;
1627     my $level = shift;
1628     return qq{<ul>$NL};
1629 }
1630 
1631 sub evt_on_item {
1632     my $this = shift;
1633     my ( $txt, $a_name ) = @_;
1634 
1635     # POD has no list item tag, so most authors simulate it, breaking the HTML.
1636     # So, strip out most bullet characters.
1637     $txt =~ s/^\s*[\*oO\+\-\.](?!\S)//;
1638 
1639     # If POD authors are going so far as to number the list, it's already too
1640     # late to fix it by using <ol> tag, but we can change the CSS style.
1641     my $style = '';
1642     if ($txt =~ m/^\d+\.?/) {
1643         $style = qq{ style="list-style-type:decimal;"};
1644         $txt =~ s/^\d+\.?\s*//;
1645     }
1646 
1647     return qq{<li$style>$NL} if $txt =~ /^\s*$/;
1648     return qq{<li$style><a name='$a_name'></a><b>$txt</b></li>$NL};
1649 }
1650 
1651 sub evt_on_back {
1652     my $this = shift;
1653     return qq{</ul>$NL};
1654 }
1655 
1656 sub evt_on_for { return $EMPTY; }
1657 
1658 sub evt_on_error {
1659     my $this = shift;
1660     my $txt  = shift;
1661     return qq{<!-- POD_ERROR: $txt -->};
1662 }
1663 
1664 sub evt_on_include {
1665     my $this = shift;
1666     my $file = shift;
1667     return $file;
1668 }
1669 
1670 sub evt_on_uri {
1671     my $this = shift;
1672     my $uri  = shift;
1673     my $target =
1674         $uri !~ m{^(?:mailto|telnet|ssh|irc):}ismx
1675         ? q{ target='_blank'}
1676         : $EMPTY;    # [6062]
1677     my $txt = $uri;
1678     $txt =~ s{^mailto:}{}ismx;
1679     return qq{<a href='$uri'$target>$txt</a>};
1680 }
1681 
1682 sub evt_on_index_node_start {
1683     my $this = shift;
1684     my ( $txt, $a_name, $has_children ) = @_;
1685 
1686     my $ret = qq{<li><a href='#$a_name'>$txt</a>$NL};
1687     if ($has_children) {
1688         $ret .= qq{$NL<ul>$NL};
1689     }
1690     return $ret;
1691 }
1692 
1693 sub evt_on_index_node_end {
1694     my $this = shift;
1695     my ( $txt, $a_name, $has_children ) = @_;
1696 
1697     my $ret = $has_children ? q{</ul>} : undef;
1698     return $ret;
1699 }
1700 
1701 ##############
1702 # PM_VERSION #
1703 ##############
1704 
1705 sub pm_version {
1706     my $this = ref( $_[0] ) ? shift: undef;
1707     if ( not defined $this ) {
1708         carp q{pm_version must be referenced through Pod::HtmlEasy};
1709         return;
1710     }
1711 
1712     return $this->{VERSION};
1713 }
1714 
1715 ##############
1716 # PM_PACKAGE #
1717 ##############
1718 
1719 sub pm_package {
1720     my $this = ref( $_[0] ) ? shift: undef;
1721     if ( not defined $this ) {
1722         carp q{pm_package must be referenced through Pod::HtmlEasy};
1723         return;
1724     }
1725 
1726     return $this->{PACKAGE};
1727 }
1728 
1729 ###########
1730 # PM_NAME #
1731 ###########
1732 
1733 sub pm_name {
1734     my $this = ref( $_[0] ) ? shift: undef;
1735     if ( not defined $this ) {
1736         carp q{pm_name must be referenced through Pod::HtmlEasy};
1737         return;
1738     }
1739     return $this->{TITLE};
1740 }
1741 
1742 ###########################
1743 # PM_PACKAGE_VERSION_NAME #
1744 ###########################
1745 
1746 sub pm_package_version_name {
1747     my $this = ref( $_[0] ) ? shift: undef;
1748     if ( not defined $this ) {
1749         carp
1750             q{pm_package_version_name must be referenced through Pod::HtmlEasy};
1751         return;
1752     }
1753 
1754     return ( $this->pm_package(), $this->pm_version(), $this->pm_name() );
1755 }
1756 
1757 #######
1758 # END #
1759 #######
1760 
1761 1;
1762 
1763 package main;
1764 
1765 # Pass argv to perldoc, which we will redirect the output of perldoc to this
1766 # process.  This syntax only works in Perl 5.8 and higher, but that's OK, the
1767 # rest of the code also requires Perl 5.8.
1768 open my $perlDocInput, "-|", qw/perldoc -u -T/, @ARGV
1769      or die "Unable to open pipe: $!";
1770 
1771 my $usingFunctionPage = 0;
1772 if(exists $ARGV[0] and $ARGV[0] eq '-f')
1773 {
1774     # Looking for a function, rewrite some URLs that refer to other functions
1775     # on the same page.
1776     $usingFunctionPage = 1;
1777 }
1778 
1779 my $podhtml = Pod::HtmlEasy->new();
1780 
1781 print $podhtml->pod2html($perlDocInput,
1782                          'function_page'=>$usingFunctionPage,
1783                          'faq_page' => exists $ARGV[0] && $ARGV[0] eq '-q',
1784                          title=>"$ARGV[-1]"), "\n";