File Coverage

blib/lib/Perl/Tidy/HtmlWriter.pm
Criterion Covered Total %
statement 389 736 52.8
branch 63 280 22.5
condition 12 51 23.5
subroutine 34 45 75.5
pod 0 22 0.0
total 498 1134 43.9


line stmt bran cond sub pod time code
1             #####################################################################
2             #
3             # The Perl::Tidy::HtmlWriter class writes a copy of the input stream in html
4             #
5             #####################################################################
6              
7             package Perl::Tidy::HtmlWriter;
8 38     38   309 use strict;
  38         87  
  38         1711  
9 38     38   245 use warnings;
  38         87  
  38         2222  
10             our $VERSION = '20230701';
11              
12 38     38   263 use English qw( -no_match_vars );
  38         89  
  38         379  
13 38     38   16425 use File::Basename;
  38         104  
  38         4922  
14              
15 38     38   301 use constant EMPTY_STRING => q{};
  38         91  
  38         2908  
16 38     38   274 use constant SPACE => q{ };
  38         100  
  38         7023  
17              
18             # class variables
19             my (
20              
21             # INITIALIZER: BEGIN block
22             $missing_html_entities,
23             $missing_pod_html,
24              
25             # INITIALIZER: BEGIN block
26             %short_to_long_names,
27             %token_short_names,
28              
29             # INITIALIZER: sub check_options
30             $rOpts,
31             $rOpts_html_entities,
32             $css_linkname,
33             %html_bold,
34             %html_color,
35             %html_italic,
36              
37             );
38              
39             # replace unsafe characters with HTML entity representation if HTML::Entities
40             # is available
41             #{ eval "use HTML::Entities"; $missing_html_entities = $@; }
42              
43             BEGIN {
44              
45 38     38   165 $missing_html_entities = EMPTY_STRING;
46 38 50       85 if ( !eval { require HTML::Entities; 1 } ) {
  38         21598  
  38         237849  
47 0 0       0 $missing_html_entities = $EVAL_ERROR ? $EVAL_ERROR : 1;
48             }
49              
50 38         144 $missing_pod_html = EMPTY_STRING;
51 38 50       112 if ( !eval { require Pod::Html; 1 } ) {
  38         21387  
  38         2527653  
52 0 0       0 $missing_pod_html = $EVAL_ERROR ? $EVAL_ERROR : 1;
53             }
54             } ## end BEGIN
55              
56             sub AUTOLOAD {
57              
58             # Catch any undefined sub calls so that we are sure to get
59             # some diagnostic information. This sub should never be called
60             # except for a programming error.
61 0     0   0 our $AUTOLOAD;
62 0 0       0 return if ( $AUTOLOAD =~ /\bDESTROY$/ );
63 0         0 my ( $pkg, $fname, $lno ) = caller();
64 0         0 my $my_package = __PACKAGE__;
65 0         0 print STDERR <<EOM;
66             ======================================================================
67             Error detected in package '$my_package', version $VERSION
68             Received unexpected AUTOLOAD call for sub '$AUTOLOAD'
69             Called from package: '$pkg'
70             Called from File '$fname' at line '$lno'
71             This error is probably due to a recent programming change
72             ======================================================================
73             EOM
74 0         0 exit 1;
75             } ## end sub AUTOLOAD
76              
77       0     sub DESTROY {
78              
79             # required to avoid call to AUTOLOAD in some versions of perl
80             }
81              
82             sub new {
83              
84 1     1 0 7 my ( $class, @args ) = @_;
85              
86 1         11 my %defaults = (
87             input_file => undef,
88             html_file => undef,
89             extension => undef,
90             html_toc_extension => undef,
91             html_src_extension => undef,
92             );
93 1         7 my %args = ( %defaults, @args );
94              
95 1         6 my $input_file = $args{input_file};
96 1         4 my $html_file = $args{html_file};
97 1         2 my $extension = $args{extension};
98 1         3 my $html_toc_extension = $args{html_toc_extension};
99 1         7 my $html_src_extension = $args{html_src_extension};
100              
101 1         2 my $html_file_opened = 0;
102 1         3 my $html_fh;
103 1         5 ( $html_fh, my $html_filename ) =
104             Perl::Tidy::streamhandle( $html_file, 'w' );
105 1 50       7 unless ($html_fh) {
106 0         0 Perl::Tidy::Warn("can't open $html_file: $ERRNO\n");
107 0         0 return;
108             }
109 1         2 $html_file_opened = 1;
110              
111 1 50 33     17 if ( !$input_file || $input_file eq '-' || ref($input_file) ) {
      33        
112 0         0 $input_file = "NONAME";
113             }
114              
115             # write the table of contents to a string
116 1         3 my $toc_string;
117 1         5 my $html_toc_fh = Perl::Tidy::IOScalar->new( \$toc_string, 'w' );
118              
119 1         4 my $html_pre_fh;
120             my @pre_string_stack;
121 1 50       13 if ( $rOpts->{'html-pre-only'} ) {
122              
123             # pre section goes directly to the output stream
124 0         0 $html_pre_fh = $html_fh;
125 0         0 $html_pre_fh->print( <<"PRE_END");
126             <pre>
127             PRE_END
128             }
129             else {
130              
131             # pre section go out to a temporary string
132 1         18 my $pre_string;
133 1         6 $html_pre_fh = Perl::Tidy::IOScalar->new( \$pre_string, 'w' );
134 1         4 push @pre_string_stack, \$pre_string;
135             }
136              
137             # pod text gets diverted if the 'pod2html' is used
138 1         3 my $html_pod_fh;
139             my $pod_string;
140 1 50       4 if ( $rOpts->{'pod2html'} ) {
141 1 50       5 if ( $rOpts->{'html-pre-only'} ) {
142 0         0 undef $rOpts->{'pod2html'};
143             }
144             else {
145             ##eval "use Pod::Html";
146             #if ($@) {
147 1 50       4 if ($missing_pod_html) {
148 0         0 Perl::Tidy::Warn(
149             "unable to find Pod::Html; cannot use pod2html\n-npod disables this message\n"
150             );
151 0         0 undef $rOpts->{'pod2html'};
152             }
153             else {
154 1         4 $html_pod_fh = Perl::Tidy::IOScalar->new( \$pod_string, 'w' );
155             }
156             }
157             }
158              
159 1         3 my $toc_filename;
160             my $src_filename;
161 1 50       3 if ( $rOpts->{'frames'} ) {
162 0 0       0 unless ($extension) {
163 0         0 Perl::Tidy::Warn(
164             "cannot use frames without a specified output extension; ignoring -frm\n"
165             );
166 0         0 undef $rOpts->{'frames'};
167             }
168             else {
169 0         0 $toc_filename = $input_file . $html_toc_extension . $extension;
170 0         0 $src_filename = $input_file . $html_src_extension . $extension;
171             }
172             }
173              
174             # ----------------------------------------------------------
175             # Output is now directed as follows:
176             # html_toc_fh <-- table of contents items
177             # html_pre_fh <-- the <pre> section of formatted code, except:
178             # html_pod_fh <-- pod goes here with the pod2html option
179             # ----------------------------------------------------------
180              
181 1         5 my $title = $rOpts->{'title'};
182 1 50       3 unless ($title) {
183 1         48 ( $title, my $path ) = fileparse($input_file);
184             }
185 1         5 my $toc_item_count = 0;
186 1         3 my $in_toc_package = EMPTY_STRING;
187 1         2 my $last_level = 0;
188 1         30 return bless {
189             _input_file => $input_file, # name of input file
190             _title => $title, # title, unescaped
191             _html_file => $html_file, # name of .html output file
192             _toc_filename => $toc_filename, # for frames option
193             _src_filename => $src_filename, # for frames option
194             _html_file_opened => $html_file_opened, # a flag
195             _html_fh => $html_fh, # the output stream
196             _html_pre_fh => $html_pre_fh, # pre section goes here
197             _rpre_string_stack => \@pre_string_stack, # stack of pre sections
198             _html_pod_fh => $html_pod_fh, # pod goes here if pod2html
199             _rpod_string => \$pod_string, # string holding pod
200             _pod_cut_count => 0, # how many =cut's?
201             _html_toc_fh => $html_toc_fh, # fh for table of contents
202             _rtoc_string => \$toc_string, # string holding toc
203             _rtoc_item_count => \$toc_item_count, # how many toc items
204             _rin_toc_package => \$in_toc_package, # package name
205             _rtoc_name_count => {}, # hash to track unique names
206             _rpackage_stack => [], # stack to check for package
207             # name changes
208             _rlast_level => \$last_level, # brace indentation level
209             }, $class;
210             } ## end sub new
211              
212             sub close_object {
213 1     1 0 3 my ($object) = @_;
214              
215             # returns true if close works, false if not
216             # failure probably means there is no close method
217 1         2 return eval { $object->close(); 1 };
  1         5  
  1         2  
218             } ## end sub close_object
219              
220             sub add_toc_item {
221              
222             # Add an item to the html table of contents.
223             # This is called even if no table of contents is written,
224             # because we still want to put the anchors in the <pre> text.
225             # We are given an anchor name and its type; types are:
226             # 'package', 'sub', '__END__', '__DATA__', 'EOF'
227             # There must be an 'EOF' call at the end to wrap things up.
228 1     1 0 4 my ( $self, $name, $type ) = @_;
229 1         2 my $html_toc_fh = $self->{_html_toc_fh};
230 1         3 my $html_pre_fh = $self->{_html_pre_fh};
231 1         2 my $rtoc_name_count = $self->{_rtoc_name_count};
232 1         2 my $rtoc_item_count = $self->{_rtoc_item_count};
233 1         3 my $rlast_level = $self->{_rlast_level};
234 1         2 my $rin_toc_package = $self->{_rin_toc_package};
235 1         4 my $rpackage_stack = $self->{_rpackage_stack};
236              
237             # packages contain sublists of subs, so to avoid errors all package
238             # items are written and finished with the following routines
239             my $end_package_list = sub {
240 0 0   0   0 if ( ${$rin_toc_package} ) {
  0         0  
241 0         0 $html_toc_fh->print("</ul>\n</li>\n");
242 0         0 ${$rin_toc_package} = EMPTY_STRING;
  0         0  
243             }
244 0         0 return;
245 1         5 };
246              
247             my $start_package_list = sub {
248 0     0   0 my ( $unique_name, $package ) = @_;
249 0 0       0 if ( ${$rin_toc_package} ) { $end_package_list->() }
  0         0  
  0         0  
250 0         0 $html_toc_fh->print(<<EOM);
251             <li><a href=\"#$unique_name\">package $package</a>
252             <ul>
253             EOM
254 0         0 ${$rin_toc_package} = $package;
  0         0  
255 0         0 return;
256 1         7 };
257              
258             # start the table of contents on the first item
259 1 50       3 unless ( ${$rtoc_item_count} ) {
  1         5  
260              
261             # but just quit if we hit EOF without any other entries
262             # in this case, there will be no toc
263 1 50       15 return if ( $type eq 'EOF' );
264 0         0 $html_toc_fh->print( <<"TOC_END");
265             <!-- BEGIN CODE INDEX --><a name="code-index"></a>
266             <ul>
267             TOC_END
268             }
269 0         0 ${$rtoc_item_count}++;
  0         0  
270              
271             # make a unique anchor name for this location:
272             # - packages get a 'package-' prefix
273             # - subs use their names
274 0         0 my $unique_name = $name;
275 0 0       0 if ( $type eq 'package' ) { $unique_name = "package-$name" }
  0         0  
276              
277             # append '-1', '-2', etc if necessary to make unique; this will
278             # be unique because subs and packages cannot have a '-'
279 0 0       0 if ( my $count = $rtoc_name_count->{ lc $unique_name }++ ) {
280 0         0 $unique_name .= "-$count";
281             }
282              
283             # - all names get terminal '-' if pod2html is used, to avoid
284             # conflicts with anchor names created by pod2html
285 0 0       0 if ( $rOpts->{'pod2html'} ) { $unique_name .= '-' }
  0         0  
286              
287             # start/stop lists of subs
288 0 0       0 if ( $type eq 'sub' ) {
289 0         0 my $package = $rpackage_stack->[ ${$rlast_level} ];
  0         0  
290 0 0       0 unless ($package) { $package = 'main' }
  0         0  
291              
292             # if we're already in a package/sub list, be sure its the right
293             # package or else close it
294 0 0 0     0 if ( ${$rin_toc_package} && ${$rin_toc_package} ne $package ) {
  0         0  
  0         0  
295 0         0 $end_package_list->();
296             }
297              
298             # start a package/sub list if necessary
299 0 0       0 unless ( ${$rin_toc_package} ) {
  0         0  
300 0         0 $start_package_list->( $unique_name, $package );
301             }
302             }
303              
304             # now write an entry in the toc for this item
305 0 0       0 if ( $type eq 'package' ) {
    0          
306 0         0 $start_package_list->( $unique_name, $name );
307             }
308             elsif ( $type eq 'sub' ) {
309 0         0 $html_toc_fh->print("<li><a href=\"#$unique_name\">$name</a></li>\n");
310             }
311             else {
312 0         0 $end_package_list->();
313 0         0 $html_toc_fh->print("<li><a href=\"#$unique_name\">$name</a></li>\n");
314             }
315              
316             # write the anchor in the <pre> section
317 0         0 $html_pre_fh->print("<a name=\"$unique_name\"></a>");
318              
319             # end the table of contents, if any, on the end of file
320 0 0       0 if ( $type eq 'EOF' ) {
321 0         0 $html_toc_fh->print( <<"TOC_END");
322             </ul>
323             <!-- END CODE INDEX -->
324             TOC_END
325             }
326 0         0 return;
327             } ## end sub add_toc_item
328              
329             BEGIN {
330              
331             # This is the official list of tokens which may be identified by the
332             # user. Long names are used as getopt keys. Short names are
333             # convenient short abbreviations for specifying input. Short names
334             # somewhat resemble token type characters, but are often different
335             # because they may only be alphanumeric, to allow command line
336             # input. Also, note that because of case insensitivity of html,
337             # this table must be in a single case only (I've chosen to use all
338             # lower case).
339             # When adding NEW_TOKENS: update this hash table
340             # short names => long names
341 38     38   646 %short_to_long_names = (
342             'n' => 'numeric',
343             'p' => 'paren',
344             'q' => 'quote',
345             's' => 'structure',
346             'c' => 'comment',
347             'v' => 'v-string',
348             'cm' => 'comma',
349             'w' => 'bareword',
350             'co' => 'colon',
351             'pu' => 'punctuation',
352             'i' => 'identifier',
353             'j' => 'label',
354             'h' => 'here-doc-target',
355             'hh' => 'here-doc-text',
356             'k' => 'keyword',
357             'sc' => 'semicolon',
358             'm' => 'subroutine',
359             'pd' => 'pod-text',
360             );
361              
362             # Now we have to map actual token types into one of the above short
363             # names; any token types not mapped will get 'punctuation'
364             # properties.
365              
366             # The values of this hash table correspond to the keys of the
367             # previous hash table.
368             # The keys of this hash table are token types and can be seen
369             # by running with --dump-token-types (-dtt).
370              
371             # When adding NEW_TOKENS: update this hash table
372             # $type => $short_name
373 38         661 %token_short_names = (
374             '#' => 'c',
375             'n' => 'n',
376             'v' => 'v',
377             'k' => 'k',
378             'F' => 'k',
379             'Q' => 'q',
380             'q' => 'q',
381             'J' => 'j',
382             'j' => 'j',
383             'h' => 'h',
384             'H' => 'hh',
385             'w' => 'w',
386             ',' => 'cm',
387             '=>' => 'cm',
388             ';' => 'sc',
389             ':' => 'co',
390             'f' => 'sc',
391             '(' => 'p',
392             ')' => 'p',
393             'M' => 'm',
394             'P' => 'pd',
395             'A' => 'co',
396             );
397              
398             # These token types will all be called identifiers for now
399 38         314 my @identifier = qw< i t U C Y Z G :: CORE::>;
400 38         397 @token_short_names{@identifier} = ('i') x scalar(@identifier);
401              
402             # These token types will be called 'structure'
403 38         131 my @structure = qw< { } >;
404 38         26774 @token_short_names{@structure} = ('s') x scalar(@structure);
405              
406             # OLD NOTES: save for reference
407             # Any of these could be added later if it would be useful.
408             # For now, they will by default become punctuation
409             # my @list = qw< L R [ ] >;
410             # @token_long_names{@list} = ('non-structure') x scalar(@list);
411             #
412             # my @list = qw"
413             # / /= * *= ** **= + += - -= % %= = ++ -- << <<= >> >>= pp p m mm
414             # ";
415             # @token_long_names{@list} = ('math') x scalar(@list);
416             #
417             # my @list = qw" & &= ~ ~= ^ ^= | |= ";
418             # @token_long_names{@list} = ('bit') x scalar(@list);
419             #
420             # my @list = qw" == != < > <= <=> ";
421             # @token_long_names{@list} = ('numerical-comparison') x scalar(@list);
422             #
423             # my @list = qw" && || ! &&= ||= //= ";
424             # @token_long_names{@list} = ('logical') x scalar(@list);
425             #
426             # my @list = qw" . .= =~ !~ x x= ";
427             # @token_long_names{@list} = ('string-operators') x scalar(@list);
428             #
429             # # Incomplete..
430             # my @list = qw" .. -> <> ... \ ? ";
431             # @token_long_names{@list} = ('misc-operators') x scalar(@list);
432              
433             } ## end BEGIN
434              
435             sub make_getopt_long_names {
436 552     552 0 2119 my ( $class, $rgetopt_names ) = @_;
437 552         4030 while ( my ( $short_name, $name ) = each %short_to_long_names ) {
438 9936         13675 push @{$rgetopt_names}, "html-color-$name=s";
  9936         21032  
439 9936         14073 push @{$rgetopt_names}, "html-italic-$name!";
  9936         19086  
440 9936         13303 push @{$rgetopt_names}, "html-bold-$name!";
  9936         37166  
441             }
442 552         1993 push @{$rgetopt_names}, "html-color-background=s";
  552         1756  
443 552         1186 push @{$rgetopt_names}, "html-linked-style-sheet=s";
  552         1389  
444 552         1138 push @{$rgetopt_names}, "nohtml-style-sheets";
  552         1359  
445 552         1224 push @{$rgetopt_names}, "html-pre-only";
  552         1432  
446 552         1216 push @{$rgetopt_names}, "html-line-numbers";
  552         2001  
447 552         1284 push @{$rgetopt_names}, "html-entities!";
  552         1371  
448 552         1200 push @{$rgetopt_names}, "stylesheet";
  552         1230  
449 552         1089 push @{$rgetopt_names}, "html-table-of-contents!";
  552         1217  
450 552         1019 push @{$rgetopt_names}, "pod2html!";
  552         1176  
451 552         987 push @{$rgetopt_names}, "frames!";
  552         1134  
452 552         1023 push @{$rgetopt_names}, "html-toc-extension=s";
  552         1108  
453 552         1052 push @{$rgetopt_names}, "html-src-extension=s";
  552         1193  
454              
455             # Pod::Html parameters:
456 552         1068 push @{$rgetopt_names}, "backlink=s";
  552         1213  
457 552         1067 push @{$rgetopt_names}, "cachedir=s";
  552         1163  
458 552         1047 push @{$rgetopt_names}, "htmlroot=s";
  552         1157  
459 552         1032 push @{$rgetopt_names}, "libpods=s";
  552         1150  
460 552         1149 push @{$rgetopt_names}, "podpath=s";
  552         1460  
461 552         1096 push @{$rgetopt_names}, "podroot=s";
  552         1172  
462 552         1010 push @{$rgetopt_names}, "title=s";
  552         1084  
463              
464             # Pod::Html parameters with leading 'pod' which will be removed
465             # before the call to Pod::Html
466 552         928 push @{$rgetopt_names}, "podquiet!";
  552         1116  
467 552         1064 push @{$rgetopt_names}, "podverbose!";
  552         1237  
468 552         1058 push @{$rgetopt_names}, "podrecurse!";
  552         1106  
469 552         923 push @{$rgetopt_names}, "podflush";
  552         1121  
470 552         1008 push @{$rgetopt_names}, "podheader!";
  552         1143  
471 552         991 push @{$rgetopt_names}, "podindex!";
  552         1094  
472 552         1646 return;
473             } ## end sub make_getopt_long_names
474              
475             sub make_abbreviated_names {
476              
477             # We're appending things like this to the expansion list:
478             # 'hcc' => [qw(html-color-comment)],
479             # 'hck' => [qw(html-color-keyword)],
480             # etc
481 552     552 0 2988 my ( $class, $rexpansion ) = @_;
482              
483             # abbreviations for color/bold/italic properties
484 552         3356 while ( my ( $short_name, $long_name ) = each %short_to_long_names ) {
485 9936         22047 ${$rexpansion}{"hc$short_name"} = ["html-color-$long_name"];
  9936         22228  
486 9936         20185 ${$rexpansion}{"hb$short_name"} = ["html-bold-$long_name"];
  9936         38523  
487 9936         21330 ${$rexpansion}{"hi$short_name"} = ["html-italic-$long_name"];
  9936         20494  
488 9936         19509 ${$rexpansion}{"nhb$short_name"} = ["nohtml-bold-$long_name"];
  9936         20754  
489 9936         19749 ${$rexpansion}{"nhi$short_name"} = ["nohtml-italic-$long_name"];
  9936         34717  
490             }
491              
492             # abbreviations for all other html options
493 552         2248 ${$rexpansion}{"hcbg"} = ["html-color-background"];
  552         2286  
494 552         1504 ${$rexpansion}{"pre"} = ["html-pre-only"];
  552         1438  
495 552         1390 ${$rexpansion}{"toc"} = ["html-table-of-contents"];
  552         1480  
496 552         1379 ${$rexpansion}{"ntoc"} = ["nohtml-table-of-contents"];
  552         1427  
497 552         1356 ${$rexpansion}{"nnn"} = ["html-line-numbers"];
  552         1471  
498 552         1480 ${$rexpansion}{"hent"} = ["html-entities"];
  552         1450  
499 552         1393 ${$rexpansion}{"nhent"} = ["nohtml-entities"];
  552         1401  
500 552         1501 ${$rexpansion}{"css"} = ["html-linked-style-sheet"];
  552         1448  
501 552         1408 ${$rexpansion}{"nss"} = ["nohtml-style-sheets"];
  552         1690  
502 552         1385 ${$rexpansion}{"ss"} = ["stylesheet"];
  552         1313  
503 552         1323 ${$rexpansion}{"pod"} = ["pod2html"];
  552         1386  
504 552         1397 ${$rexpansion}{"npod"} = ["nopod2html"];
  552         7174  
505 552         1560 ${$rexpansion}{"frm"} = ["frames"];
  552         1350  
506 552         1307 ${$rexpansion}{"nfrm"} = ["noframes"];
  552         1327  
507 552         1529 ${$rexpansion}{"text"} = ["html-toc-extension"];
  552         1396  
508 552         1322 ${$rexpansion}{"sext"} = ["html-src-extension"];
  552         1321  
509 552         1984 return;
510             } ## end sub make_abbreviated_names
511              
512             sub check_options {
513              
514             # This will be called once after options have been parsed
515             # Note that we are defining the package variable $rOpts here:
516 1     1 0 5 ( my $class, $rOpts ) = @_;
517              
518             # X11 color names for default settings that seemed to look ok
519             # (these color names are only used for programming clarity; the hex
520             # numbers are actually written)
521 38     38   533 use constant ForestGreen => "#228B22";
  38         102  
  38         3416  
522 38     38   319 use constant SaddleBrown => "#8B4513";
  38         123  
  38         2231  
523 38     38   302 use constant magenta4 => "#8B008B";
  38         150  
  38         2012  
524 38     38   307 use constant IndianRed3 => "#CD5555";
  38         117  
  38         2165  
525 38     38   296 use constant DeepSkyBlue4 => "#00688B";
  38         107  
  38         2079  
526 38     38   323 use constant MediumOrchid3 => "#B452CD";
  38         163  
  38         2359  
527 38     38   327 use constant black => "#000000";
  38         161  
  38         2141  
528 38     38   290 use constant white => "#FFFFFF";
  38         124  
  38         2191  
529 38     38   326 use constant red => "#FF0000";
  38         127  
  38         210047  
530              
531             # set default color, bold, italic properties
532             # anything not listed here will be given the default (punctuation) color --
533             # these types currently not listed and get default: ws pu s sc cm co p
534             # When adding NEW_TOKENS: add an entry here if you don't want defaults
535              
536             # set_default_properties( $short_name, default_color, bold?, italic? );
537 1         8 set_default_properties( 'c', ForestGreen, 0, 0 );
538 1         11 set_default_properties( 'pd', ForestGreen, 0, 1 );
539 1         6 set_default_properties( 'k', magenta4, 1, 0 ); # was SaddleBrown
540 1         10 set_default_properties( 'q', IndianRed3, 0, 0 );
541 1         13 set_default_properties( 'hh', IndianRed3, 0, 1 );
542 1         14 set_default_properties( 'h', IndianRed3, 1, 0 );
543 1         13 set_default_properties( 'i', DeepSkyBlue4, 0, 0 );
544 1         14 set_default_properties( 'w', black, 0, 0 );
545 1         21 set_default_properties( 'n', MediumOrchid3, 0, 0 );
546 1         23 set_default_properties( 'v', MediumOrchid3, 0, 0 );
547 1         15 set_default_properties( 'j', IndianRed3, 1, 0 );
548 1         14 set_default_properties( 'm', red, 1, 0 );
549              
550 1         10 set_default_color( 'html-color-background', white );
551 1         10 set_default_color( 'html-color-punctuation', black );
552              
553             # setup property lookup tables for tokens based on their short names
554             # every token type has a short name, and will use these tables
555             # to do the html markup
556 1         9 while ( my ( $short_name, $long_name ) = each %short_to_long_names ) {
557 18         58 $html_color{$short_name} = $rOpts->{"html-color-$long_name"};
558 18         32 $html_bold{$short_name} = $rOpts->{"html-bold-$long_name"};
559 18         57 $html_italic{$short_name} = $rOpts->{"html-italic-$long_name"};
560             }
561              
562             # write style sheet to STDOUT and die if requested
563 1 50       8 if ( defined( $rOpts->{'stylesheet'} ) ) {
564 0         0 write_style_sheet_file('-');
565 0         0 Perl::Tidy::Exit(0);
566             }
567              
568             # make sure user gives a file name after -css
569 1         4 $css_linkname = EMPTY_STRING;
570 1 50       5 if ( defined( $rOpts->{'html-linked-style-sheet'} ) ) {
571 0         0 $css_linkname = $rOpts->{'html-linked-style-sheet'};
572 0 0       0 if ( $css_linkname =~ /^-/ ) {
573 0         0 Perl::Tidy::Die("You must specify a valid filename after -css\n");
574             }
575             }
576              
577             # check for conflict
578 1 0 33     4 if ( $css_linkname && $rOpts->{'nohtml-style-sheets'} ) {
579 0         0 $rOpts->{'nohtml-style-sheets'} = 0;
580 0         0 Perl::Tidy::Warn(
581             "You can't specify both -css and -nss; -nss ignored\n");
582             }
583              
584             # write a style sheet file if necessary
585 1 50       5 if ($css_linkname) {
586              
587             # if the selected filename exists, don't write, because user may
588             # have done some work by hand to create it; use backup name instead
589             # Also, this will avoid a potential disaster in which the user
590             # forgets to specify the style sheet, like this:
591             # perltidy -html -css myfile1.pl myfile2.pl
592             # This would cause myfile1.pl to parsed as the style sheet by GetOpts
593 0 0       0 unless ( -e $css_linkname ) {
594 0         0 write_style_sheet_file($css_linkname);
595             }
596             }
597 1         5 $rOpts_html_entities = $rOpts->{'html-entities'};
598 1         3 return;
599             } ## end sub check_options
600              
601             sub write_style_sheet_file {
602              
603 0     0 0 0 my $filename = shift;
604 0         0 my $fh;
605 0 0       0 unless ( $fh = IO::File->new("> $filename") ) {
606 0         0 Perl::Tidy::Die("can't open $filename: $ERRNO\n");
607             }
608 0         0 write_style_sheet_data($fh);
609 0         0 close_object($fh);
610 0         0 return;
611             } ## end sub write_style_sheet_file
612              
613             sub write_style_sheet_data {
614              
615             # write the style sheet data to an open file handle
616 1     1 0 4 my $fh = shift;
617              
618 1         6 my $bg_color = $rOpts->{'html-color-background'};
619 1         3 my $text_color = $rOpts->{'html-color-punctuation'};
620              
621             # pre-bgcolor is new, and may not be defined
622 1         2 my $pre_bg_color = $rOpts->{'html-pre-color-background'};
623 1 50       6 $pre_bg_color = $bg_color unless $pre_bg_color;
624              
625 1         15 $fh->print(<<"EOM");
626             /* default style sheet generated by perltidy */
627             body {background: $bg_color; color: $text_color}
628             pre { color: $text_color;
629             background: $pre_bg_color;
630             font-family: courier;
631             }
632              
633             EOM
634              
635 1         12 foreach my $short_name ( sort keys %short_to_long_names ) {
636 18         32 my $long_name = $short_to_long_names{$short_name};
637              
638 18         26 my $abbrev = '.' . $short_name;
639 18 100       38 if ( length($short_name) == 1 ) { $abbrev .= SPACE } # for alignment
  12         16  
640 18         31 my $color = $html_color{$short_name};
641 18 100       38 if ( !defined($color) ) { $color = $text_color }
  5         7  
642 18         52 $fh->print("$abbrev \{ color: $color;");
643              
644 18 100       35 if ( $html_bold{$short_name} ) {
645 4         20 $fh->print(" font-weight:bold;");
646             }
647              
648 18 100       40 if ( $html_italic{$short_name} ) {
649 2         14 $fh->print(" font-style:italic;");
650             }
651 18         41 $fh->print("} /* $long_name */\n");
652             }
653 1         10 return;
654             } ## end sub write_style_sheet_data
655              
656             sub set_default_color {
657              
658             # make sure that options hash $rOpts->{$key} contains a valid color
659 14     14 0 21 my ( $key, $color ) = @_;
660 14 50       33 if ( $rOpts->{$key} ) { $color = $rOpts->{$key} }
  0         0  
661 14         25 $rOpts->{$key} = check_RGB($color);
662 14         23 return;
663             } ## end sub set_default_color
664              
665             sub check_RGB {
666              
667             # if color is a 6 digit hex RGB value, prepend a #, otherwise
668             # assume that it is a valid ascii color name
669 14     14 0 22 my ($color) = @_;
670 14 50       34 if ( $color =~ /^[0-9a-fA-F]{6,6}$/ ) { $color = "#$color" }
  0         0  
671 14         37 return $color;
672             } ## end sub check_RGB
673              
674             sub set_default_properties {
675 12     12 0 29 my ( $short_name, $color, $bold, $italic ) = @_;
676              
677 12         47 set_default_color( "html-color-$short_to_long_names{$short_name}", $color );
678 12         18 my $key;
679 12         19 $key = "html-bold-$short_to_long_names{$short_name}";
680 12 50       28 $rOpts->{$key} = ( defined $rOpts->{$key} ) ? $rOpts->{$key} : $bold;
681 12         22 $key = "html-italic-$short_to_long_names{$short_name}";
682 12 50       25 $rOpts->{$key} = ( defined $rOpts->{$key} ) ? $rOpts->{$key} : $italic;
683 12         20 return;
684             } ## end sub set_default_properties
685              
686             sub pod_to_html {
687              
688             # Use Pod::Html to process the pod and make the page
689             # then merge the perltidy code sections into it.
690             # return 1 if success, 0 otherwise
691 1     1 0 4 my ( $self, $pod_string, $css_string, $toc_string, $rpre_string_stack ) =
692             @_;
693 1         3 my $input_file = $self->{_input_file};
694 1         6 my $title = $self->{_title};
695 1         4 my $success_flag = 0;
696              
697             # don't try to use pod2html if no pod
698 1 50       4 unless ($pod_string) {
699 1         4 return $success_flag;
700             }
701              
702             # Pod::Html requires a real temporary filename
703 0         0 my ( $fh_tmp, $tmpfile ) = File::Temp::tempfile();
704 0 0       0 unless ($fh_tmp) {
705 0         0 Perl::Tidy::Warn(
706             "unable to open temporary file $tmpfile; cannot use pod2html\n");
707 0         0 return $success_flag;
708             }
709              
710             #------------------------------------------------------------------
711             # Warning: a temporary file is open; we have to clean up if
712             # things go bad. From here on all returns should be by going to
713             # RETURN so that the temporary file gets unlinked.
714             #------------------------------------------------------------------
715              
716             # write the pod text to the temporary file
717 0         0 $fh_tmp->print($pod_string);
718 0         0 $fh_tmp->close();
719              
720             # Hand off the pod to pod2html.
721             # Note that we can use the same temporary filename for input and output
722             # because of the way pod2html works.
723             {
724              
725 0         0 my @args;
  0         0  
726 0         0 push @args, "--infile=$tmpfile", "--outfile=$tmpfile", "--title=$title";
727              
728             # Flags with string args:
729             # "backlink=s", "cachedir=s", "htmlroot=s", "libpods=s",
730             # "podpath=s", "podroot=s"
731             # Note: -css=s is handled by perltidy itself
732 0         0 foreach my $kw (qw(backlink cachedir htmlroot libpods podpath podroot))
733             {
734 0 0       0 if ( $rOpts->{$kw} ) { push @args, "--$kw=$rOpts->{$kw}" }
  0         0  
735             }
736              
737             # Toggle switches; these have extra leading 'pod'
738             # "header!", "index!", "recurse!", "quiet!", "verbose!"
739 0         0 foreach my $kw (qw(podheader podindex podrecurse podquiet podverbose)) {
740 0         0 my $kwd = $kw; # allows us to strip 'pod'
741 0 0       0 if ( $rOpts->{$kw} ) { $kwd =~ s/^pod//; push @args, "--$kwd" }
  0 0       0  
  0         0  
742             elsif ( defined( $rOpts->{$kw} ) ) {
743 0         0 $kwd =~ s/^pod//;
744 0         0 push @args, "--no$kwd";
745             }
746             }
747              
748             # "flush",
749 0         0 my $kw = 'podflush';
750 0 0       0 if ( $rOpts->{$kw} ) { $kw =~ s/^pod//; push @args, "--$kw" }
  0         0  
  0         0  
751              
752             # Must clean up if pod2html dies (it can);
753             # Be careful not to overwrite callers __DIE__ routine
754             local $SIG{__DIE__} = sub {
755 0 0   0   0 unlink $tmpfile if -e $tmpfile;
756 0         0 Perl::Tidy::Die( $_[0] );
757 0         0 };
758              
759 0         0 Pod::Html::pod2html(@args);
760             }
761 0         0 $fh_tmp = IO::File->new( $tmpfile, 'r' );
762 0 0       0 unless ($fh_tmp) {
763              
764             # this error shouldn't happen ... we just used this filename
765 0         0 Perl::Tidy::Warn(
766             "unable to open temporary file $tmpfile; cannot use pod2html\n");
767 0         0 return $success_flag;
768             }
769              
770 0         0 my $html_fh = $self->{_html_fh};
771 0         0 my @toc;
772             my $in_toc;
773 0         0 my $ul_level = 0;
774 0         0 my $no_print;
775              
776             # This routine will write the html selectively and store the toc
777             my $html_print = sub {
778 0     0   0 foreach my $line (@_) {
779 0 0       0 $html_fh->print($line) unless ($no_print);
780 0 0       0 if ($in_toc) { push @toc, $line }
  0         0  
781             }
782 0         0 return;
783 0         0 };
784              
785             # loop over lines of html output from pod2html and merge in
786             # the necessary perltidy html sections
787 0         0 my ( $saw_body, $saw_index, $saw_body_end );
788              
789 0         0 my $timestamp = EMPTY_STRING;
790 0 0       0 if ( $rOpts->{'timestamp'} ) {
791 0         0 my $date = localtime;
792 0         0 $timestamp = "on $date";
793             }
794 0         0 while ( my $line = $fh_tmp->getline() ) {
795              
796 0 0 0     0 if ( $line =~ /^\s*<html>\s*$/i ) {
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
797             ##my $date = localtime;
798             ##$html_print->("<!-- Generated by perltidy on $date -->\n");
799 0         0 $html_print->("<!-- Generated by perltidy $timestamp -->\n");
800 0         0 $html_print->($line);
801             }
802              
803             # Copy the perltidy css, if any, after <body> tag
804             elsif ( $line =~ /^\s*<body.*>\s*$/i ) {
805 0         0 $saw_body = 1;
806 0 0       0 $html_print->($css_string) if $css_string;
807 0         0 $html_print->($line);
808              
809             # add a top anchor and heading
810 0         0 $html_print->("<a name=\"-top-\"></a>\n");
811 0         0 $title = escape_html($title);
812 0         0 $html_print->("<h1>$title</h1>\n");
813             }
814              
815             # check for start of index, old pod2html
816             # before Pod::Html VERSION 1.15_02 it is delimited by comments as:
817             # <!-- INDEX BEGIN -->
818             # <ul>
819             # ...
820             # </ul>
821             # <!-- INDEX END -->
822             #
823             elsif ( $line =~ /^\s*<!-- INDEX BEGIN -->\s*$/i ) {
824 0         0 $in_toc = 'INDEX';
825              
826             # when frames are used, an extra table of contents in the
827             # contents panel is confusing, so don't print it
828             $no_print = $rOpts->{'frames'}
829 0   0     0 || !$rOpts->{'html-table-of-contents'};
830 0 0       0 $html_print->("<h2>Doc Index:</h2>\n") if $rOpts->{'frames'};
831 0         0 $html_print->($line);
832             }
833              
834             # check for start of index, new pod2html
835             # After Pod::Html VERSION 1.15_02 it is delimited as:
836             # <ul id="index">
837             # ...
838             # </ul>
839             elsif ( $line =~ /^\s*<ul\s+id="index">/i ) {
840 0         0 $in_toc = 'UL';
841 0         0 $ul_level = 1;
842              
843             # when frames are used, an extra table of contents in the
844             # contents panel is confusing, so don't print it
845             $no_print = $rOpts->{'frames'}
846 0   0     0 || !$rOpts->{'html-table-of-contents'};
847 0 0       0 $html_print->("<h2>Doc Index:</h2>\n") if $rOpts->{'frames'};
848 0         0 $html_print->($line);
849             }
850              
851             # Check for end of index, old pod2html
852             elsif ( $line =~ /^\s*<!-- INDEX END -->\s*$/i ) {
853 0         0 $saw_index = 1;
854 0         0 $html_print->($line);
855              
856             # Copy the perltidy toc, if any, after the Pod::Html toc
857 0 0       0 if ($toc_string) {
858 0 0       0 $html_print->("<hr />\n") if $rOpts->{'frames'};
859 0         0 $html_print->("<h2>Code Index:</h2>\n");
860             ##my @toc = map { $_ .= "\n" } split /\n/, $toc_string;
861 0         0 my @toc_st = map { $_ . "\n" } split /\n/, $toc_string;
  0         0  
862 0         0 $html_print->(@toc_st);
863             }
864 0         0 $in_toc = EMPTY_STRING;
865 0         0 $no_print = 0;
866             }
867              
868             # must track <ul> depth level for new pod2html
869             elsif ( $line =~ /\s*<ul>\s*$/i && $in_toc eq 'UL' ) {
870 0         0 $ul_level++;
871 0         0 $html_print->($line);
872             }
873              
874             # Check for end of index, for new pod2html
875             elsif ( $line =~ /\s*<\/ul>/i && $in_toc eq 'UL' ) {
876 0         0 $ul_level--;
877 0         0 $html_print->($line);
878              
879             # Copy the perltidy toc, if any, after the Pod::Html toc
880 0 0       0 if ( $ul_level <= 0 ) {
881 0         0 $saw_index = 1;
882 0 0       0 if ($toc_string) {
883 0 0       0 $html_print->("<hr />\n") if $rOpts->{'frames'};
884 0         0 $html_print->("<h2>Code Index:</h2>\n");
885             ##my @toc = map { $_ .= "\n" } split /\n/, $toc_string;
886 0         0 my @toc_st = map { $_ . "\n" } split /\n/, $toc_string;
  0         0  
887 0         0 $html_print->(@toc_st);
888             }
889 0         0 $in_toc = EMPTY_STRING;
890 0         0 $ul_level = 0;
891 0         0 $no_print = 0;
892             }
893             }
894              
895             # Copy one perltidy section after each marker
896             elsif ( $line =~ /^(.*)<!-- pERLTIDY sECTION -->(.*)$/ ) {
897 0         0 $line = $2;
898 0 0       0 $html_print->($1) if $1;
899              
900             # Intermingle code and pod sections if we saw multiple =cut's.
901 0 0       0 if ( $self->{_pod_cut_count} > 1 ) {
902 0         0 my $rpre_string = shift( @{$rpre_string_stack} );
  0         0  
903 0 0       0 if ( ${$rpre_string} ) {
  0         0  
904 0         0 $html_print->('<pre>');
905 0         0 $html_print->( ${$rpre_string} );
  0         0  
906 0         0 $html_print->('</pre>');
907             }
908             else {
909              
910             # shouldn't happen: we stored a string before writing
911             # each marker.
912 0         0 Perl::Tidy::Warn(
913             "Problem merging html stream with pod2html; order may be wrong\n"
914             );
915             }
916 0         0 $html_print->($line);
917             }
918              
919             # If didn't see multiple =cut lines, we'll put the pod out first
920             # and then the code, because it's less confusing.
921             else {
922              
923             # since we are not intermixing code and pod, we don't need
924             # or want any <hr> lines which separated pod and code
925 0 0       0 $html_print->($line) unless ( $line =~ /^\s*<hr>\s*$/i );
926             }
927             }
928              
929             # Copy any remaining code section before the </body> tag
930             elsif ( $line =~ /^\s*<\/body>\s*$/i ) {
931 0         0 $saw_body_end = 1;
932 0 0       0 if ( @{$rpre_string_stack} ) {
  0         0  
933 0 0       0 unless ( $self->{_pod_cut_count} > 1 ) {
934 0         0 $html_print->('<hr />');
935             }
936 0         0 while ( my $rpre_string = shift( @{$rpre_string_stack} ) ) {
  0         0  
937 0         0 $html_print->('<pre>');
938 0         0 $html_print->( ${$rpre_string} );
  0         0  
939 0         0 $html_print->('</pre>');
940             }
941             }
942 0         0 $html_print->($line);
943             }
944             else {
945 0         0 $html_print->($line);
946             }
947             }
948              
949 0         0 $success_flag = 1;
950 0 0       0 unless ($saw_body) {
951 0         0 Perl::Tidy::Warn("Did not see <body> in pod2html output\n");
952 0         0 $success_flag = 0;
953             }
954 0 0       0 unless ($saw_body_end) {
955 0         0 Perl::Tidy::Warn("Did not see </body> in pod2html output\n");
956 0         0 $success_flag = 0;
957             }
958 0 0       0 unless ($saw_index) {
959 0         0 Perl::Tidy::Warn("Did not find INDEX END in pod2html output\n");
960 0         0 $success_flag = 0;
961             }
962              
963 0         0 close_object($html_fh);
964              
965             # note that we have to unlink tmpfile before making frames
966             # because the tmpfile may be one of the names used for frames
967 0 0       0 if ( -e $tmpfile ) {
968 0 0       0 unless ( unlink($tmpfile) ) {
969 0         0 Perl::Tidy::Warn(
970             "couldn't unlink temporary file $tmpfile: $ERRNO\n");
971 0         0 $success_flag = 0;
972             }
973             }
974              
975 0 0 0     0 if ( $success_flag && $rOpts->{'frames'} ) {
976 0         0 $self->make_frame( \@toc );
977             }
978 0         0 return $success_flag;
979             } ## end sub pod_to_html
980              
981             sub make_frame {
982              
983             # Make a frame with table of contents in the left panel
984             # and the text in the right panel.
985             # On entry:
986             # $html_filename contains the no-frames html output
987             # $rtoc is a reference to an array with the table of contents
988 0     0 0 0 my ( $self, $rtoc ) = @_;
989 0         0 my $input_file = $self->{_input_file};
990 0         0 my $html_filename = $self->{_html_file};
991 0         0 my $toc_filename = $self->{_toc_filename};
992 0         0 my $src_filename = $self->{_src_filename};
993 0         0 my $title = $self->{_title};
994 0         0 $title = escape_html($title);
995              
996             # FUTURE input parameter:
997 0         0 my $top_basename = EMPTY_STRING;
998              
999             # We need to produce 3 html files:
1000             # 1. - the table of contents
1001             # 2. - the contents (source code) itself
1002             # 3. - the frame which contains them
1003              
1004             # get basenames for relative links
1005 0         0 my ( $toc_basename, $toc_path ) = fileparse($toc_filename);
1006 0         0 my ( $src_basename, $src_path ) = fileparse($src_filename);
1007              
1008             # 1. Make the table of contents panel, with appropriate changes
1009             # to the anchor names
1010 0         0 my $src_frame_name = 'SRC';
1011 0         0 my $first_anchor =
1012             write_toc_html( $title, $toc_filename, $src_basename, $rtoc,
1013             $src_frame_name );
1014              
1015             # 2. The current .html filename is renamed to be the contents panel
1016 0 0       0 rename( $html_filename, $src_filename )
1017             or Perl::Tidy::Die(
1018             "Cannot rename $html_filename to $src_filename: $ERRNO\n");
1019              
1020             # 3. Then use the original html filename for the frame
1021 0         0 write_frame_html(
1022             $title, $html_filename, $top_basename,
1023             $toc_basename, $src_basename, $src_frame_name
1024             );
1025 0         0 return;
1026             } ## end sub make_frame
1027              
1028             sub write_toc_html {
1029              
1030             # write a separate html table of contents file for frames
1031 0     0 0 0 my ( $title, $toc_filename, $src_basename, $rtoc, $src_frame_name ) = @_;
1032 0 0       0 my $fh = IO::File->new( $toc_filename, 'w' )
1033             or Perl::Tidy::Die("Cannot open $toc_filename: $ERRNO\n");
1034 0         0 $fh->print(<<EOM);
1035             <html>
1036             <head>
1037             <title>$title</title>
1038             </head>
1039             <body>
1040             <h1><a href=\"$src_basename#-top-" target="$src_frame_name">$title</a></h1>
1041             EOM
1042              
1043 0         0 my $first_anchor =
1044             change_anchor_names( $rtoc, $src_basename, "$src_frame_name" );
1045 0         0 $fh->print( join EMPTY_STRING, @{$rtoc} );
  0         0  
1046              
1047 0         0 $fh->print(<<EOM);
1048             </body>
1049             </html>
1050             EOM
1051              
1052 0         0 return;
1053             } ## end sub write_toc_html
1054              
1055             sub write_frame_html {
1056              
1057             # write an html file to be the table of contents frame
1058             my (
1059 0     0 0 0 $title, $frame_filename, $top_basename,
1060             $toc_basename, $src_basename, $src_frame_name
1061             ) = @_;
1062              
1063 0 0       0 my $fh = IO::File->new( $frame_filename, 'w' )
1064             or Perl::Tidy::Die("Cannot open $toc_basename: $ERRNO\n");
1065              
1066 0         0 $fh->print(<<EOM);
1067             <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
1068             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
1069             <?xml version="1.0" encoding="iso-8859-1" ?>
1070             <html xmlns="http://www.w3.org/1999/xhtml">
1071             <head>
1072             <title>$title</title>
1073             </head>
1074             EOM
1075              
1076             # two left panels, one right, if master index file
1077 0 0       0 if ($top_basename) {
1078 0         0 $fh->print(<<EOM);
1079             <frameset cols="20%,80%">
1080             <frameset rows="30%,70%">
1081             <frame src = "$top_basename" />
1082             <frame src = "$toc_basename" />
1083             </frameset>
1084             EOM
1085             }
1086              
1087             # one left panels, one right, if no master index file
1088             else {
1089 0         0 $fh->print(<<EOM);
1090             <frameset cols="20%,*">
1091             <frame src = "$toc_basename" />
1092             EOM
1093             }
1094 0         0 $fh->print(<<EOM);
1095             <frame src = "$src_basename" name = "$src_frame_name" />
1096             <noframes>
1097             <body>
1098             <p>If you see this message, you are using a non-frame-capable web client.</p>
1099             <p>This document contains:</p>
1100             <ul>
1101             <li><a href="$toc_basename">A table of contents</a></li>
1102             <li><a href="$src_basename">The source code</a></li>
1103             </ul>
1104             </body>
1105             </noframes>
1106             </frameset>
1107             </html>
1108             EOM
1109 0         0 return;
1110             } ## end sub write_frame_html
1111              
1112             sub change_anchor_names {
1113              
1114             # add a filename and target to anchors
1115             # also return the first anchor
1116 0     0 0 0 my ( $rlines, $filename, $target ) = @_;
1117 0         0 my $first_anchor;
1118 0         0 foreach my $line ( @{$rlines} ) {
  0         0  
1119              
1120             # We're looking for lines like this:
1121             # <LI><A HREF="#synopsis">SYNOPSIS</A></LI>
1122             # ---- - -------- -----------------
1123             # $1 $4 $5
1124 0 0       0 if ( $line =~ /^(.*)<a(.*)href\s*=\s*"([^#]*)#([^"]+)"[^>]*>(.*)$/i ) {
1125 0         0 my $pre = $1;
1126 0         0 my $name = $4;
1127 0         0 my $post = $5;
1128 0         0 my $href = "$filename#$name";
1129 0         0 $line = "$pre<a href=\"$href\" target=\"$target\">$post\n";
1130 0 0       0 unless ($first_anchor) { $first_anchor = $href }
  0         0  
1131             }
1132             }
1133 0         0 return $first_anchor;
1134             } ## end sub change_anchor_names
1135              
1136             sub close_html_file {
1137 1     1 0 4 my $self = shift;
1138 1 50       6 return unless $self->{_html_file_opened};
1139              
1140 1         4 my $html_fh = $self->{_html_fh};
1141 1         2 my $rtoc_string = $self->{_rtoc_string};
1142              
1143             # There are 3 basic paths to html output...
1144              
1145             # ---------------------------------
1146             # Path 1: finish up if in -pre mode
1147             # ---------------------------------
1148 1 50       5 if ( $rOpts->{'html-pre-only'} ) {
1149 0         0 $html_fh->print( <<"PRE_END");
1150             </pre>
1151             PRE_END
1152 0         0 close_object($html_fh);
1153 0         0 return;
1154             }
1155              
1156             # Finish the index
1157 1         8 $self->add_toc_item( 'EOF', 'EOF' );
1158              
1159 1         2 my $rpre_string_stack = $self->{_rpre_string_stack};
1160              
1161             # Patch to darken the <pre> background color in case of pod2html and
1162             # interleaved code/documentation. Otherwise, the distinction
1163             # between code and documentation is blurred.
1164 1 50 33     15 if ( $rOpts->{pod2html}
      33        
1165             && $self->{_pod_cut_count} >= 1
1166             && $rOpts->{'html-color-background'} eq '#FFFFFF' )
1167             {
1168 0         0 $rOpts->{'html-pre-color-background'} = '#F0F0F0';
1169             }
1170              
1171             # put the css or its link into a string, if used
1172 1         3 my $css_string;
1173 1         6 my $fh_css = Perl::Tidy::IOScalar->new( \$css_string, 'w' );
1174              
1175             # use css linked to another file
1176 1 50       9 if ( $rOpts->{'html-linked-style-sheet'} ) {
    50          
1177 0         0 $fh_css->print(
1178             qq(<link rel="stylesheet" href="$css_linkname" type="text/css" />));
1179             }
1180              
1181             # use css embedded in this file
1182             elsif ( !$rOpts->{'nohtml-style-sheets'} ) {
1183 1         5 $fh_css->print( <<'ENDCSS');
1184             <style type="text/css">
1185             <!--
1186             ENDCSS
1187 1         12 write_style_sheet_data($fh_css);
1188 1         8 $fh_css->print( <<"ENDCSS");
1189             -->
1190             </style>
1191             ENDCSS
1192             }
1193              
1194             # -----------------------------------------------------------
1195             # path 2: use pod2html if requested
1196             # If we fail for some reason, continue on to path 3
1197             # -----------------------------------------------------------
1198 1 50       6 if ( $rOpts->{'pod2html'} ) {
1199 1         9 my $rpod_string = $self->{_rpod_string};
1200             $self->pod_to_html(
1201 1         2 ${$rpod_string}, $css_string,
1202 1 50       2 ${$rtoc_string}, $rpre_string_stack
  1         4  
1203             ) && return;
1204             }
1205              
1206             # --------------------------------------------------
1207             # path 3: write code in html, with pod only in italics
1208             # --------------------------------------------------
1209 1         3 my $input_file = $self->{_input_file};
1210 1         3 my $title = escape_html($input_file);
1211 1         4 my $timestamp = EMPTY_STRING;
1212 1 50       17 if ( $rOpts->{'timestamp'} ) {
1213 0         0 my $date = localtime;
1214 0         0 $timestamp = "on $date";
1215             }
1216 1         9 $html_fh->print( <<"HTML_START");
1217             <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1218             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1219             <!-- Generated by perltidy $timestamp -->
1220             <html xmlns="http://www.w3.org/1999/xhtml">
1221             <head>
1222             <title>$title</title>
1223             HTML_START
1224              
1225             # output the css, if used
1226 1 50       7 if ($css_string) {
1227 1         4 $html_fh->print($css_string);
1228 1         7 $html_fh->print( <<"ENDCSS");
1229             </head>
1230             <body>
1231             ENDCSS
1232             }
1233             else {
1234              
1235 0         0 $html_fh->print( <<"HTML_START");
1236             </head>
1237             <body bgcolor=\"$rOpts->{'html-color-background'}\" text=\"$rOpts->{'html-color-punctuation'}\">
1238             HTML_START
1239             }
1240              
1241 1         9 $html_fh->print("<a name=\"-top-\"></a>\n");
1242 1         10 $html_fh->print( <<"EOM");
1243             <h1>$title</h1>
1244             EOM
1245              
1246             # copy the table of contents
1247 1 0 33     10 if ( ${$rtoc_string}
  1   33     5  
1248             && !$rOpts->{'frames'}
1249             && $rOpts->{'html-table-of-contents'} )
1250             {
1251 0         0 $html_fh->print( ${$rtoc_string} );
  0         0  
1252             }
1253              
1254             # copy the pre section(s)
1255 1         5 my $fname_comment = $input_file;
1256 1         4 $fname_comment =~ s/--+/-/g; # protect HTML comment tags
1257 1         6 $html_fh->print( <<"END_PRE");
1258             <hr />
1259             <!-- contents of filename: $fname_comment -->
1260             <pre>
1261             END_PRE
1262              
1263 1         4 foreach my $rpre_string ( @{$rpre_string_stack} ) {
  1         9  
1264 1         2 $html_fh->print( ${$rpre_string} );
  1         7  
1265             }
1266              
1267             # and finish the html page
1268 1         6 $html_fh->print( <<"HTML_END");
1269             </pre>
1270             </body>
1271             </html>
1272             HTML_END
1273 1         10 close_object($html_fh);
1274              
1275 1 50       8 if ( $rOpts->{'frames'} ) {
1276             ##my @toc = map { $_ .= "\n" } split /\n/, ${$rtoc_string};
1277 0         0 my @toc = map { $_ . "\n" } split /\n/, ${$rtoc_string};
  0         0  
  0         0  
1278 0         0 $self->make_frame( \@toc );
1279             }
1280 1         4 return;
1281             } ## end sub close_html_file
1282              
1283             sub markup_tokens {
1284 2     2 0 6 my ( $self, $rtokens, $rtoken_type, $rlevels ) = @_;
1285 2         4 my ( @colored_tokens, $type, $token, $level );
1286 2         5 my $rlast_level = $self->{_rlast_level};
1287 2         4 my $rpackage_stack = $self->{_rpackage_stack};
1288              
1289 2         3 foreach my $j ( 0 .. @{$rtoken_type} - 1 ) {
  2         7  
1290 46         71 $type = $rtoken_type->[$j];
1291 46         69 $token = $rtokens->[$j];
1292 46         65 $level = $rlevels->[$j];
1293 46 50       87 $level = 0 if ( $level < 0 );
1294              
1295             #-------------------------------------------------------
1296             # Update the package stack. The package stack is needed to keep
1297             # the toc correct because some packages may be declared within
1298             # blocks and go out of scope when we leave the block.
1299             #-------------------------------------------------------
1300 46 100       57 if ( $level > ${$rlast_level} ) {
  46 100       84  
1301 3 100       10 unless ( $rpackage_stack->[ $level - 1 ] ) {
1302 1         3 $rpackage_stack->[ $level - 1 ] = 'main';
1303             }
1304 3         7 $rpackage_stack->[$level] = $rpackage_stack->[ $level - 1 ];
1305             }
1306 43         73 elsif ( $level < ${$rlast_level} ) {
1307 3         14 my $package = $rpackage_stack->[$level];
1308 3 50       10 unless ($package) { $package = 'main' }
  0         0  
1309              
1310             # if we change packages due to a nesting change, we
1311             # have to make an entry in the toc
1312 3 50       8 if ( $package ne $rpackage_stack->[ $level + 1 ] ) {
1313 0         0 $self->add_toc_item( $package, 'package' );
1314             }
1315             }
1316 46         71 ${$rlast_level} = $level;
  46         66  
1317              
1318             #-------------------------------------------------------
1319             # Intercept a sub name here; split it
1320             # into keyword 'sub' and sub name; and add an
1321             # entry in the toc
1322             #-------------------------------------------------------
1323 46 50 66     107 if ( $type eq 'i' && $token =~ /^(sub\s+)(\w.*)$/ ) {
1324 0         0 $token = $self->markup_html_element( $1, 'k' );
1325 0         0 push @colored_tokens, $token;
1326 0         0 $token = $2;
1327 0         0 $type = 'M';
1328              
1329             # but don't include sub declarations in the toc;
1330             # these will have leading token types 'i;'
1331 0         0 my $signature = join EMPTY_STRING, @{$rtoken_type};
  0         0  
1332 0 0       0 unless ( $signature =~ /^i;/ ) {
1333 0         0 my $subname = $token;
1334 0         0 $subname =~ s/[\s\(].*$//; # remove any attributes and prototype
1335 0         0 $self->add_toc_item( $subname, 'sub' );
1336             }
1337             }
1338              
1339             #-------------------------------------------------------
1340             # Intercept a package name here; split it
1341             # into keyword 'package' and name; add to the toc,
1342             # and update the package stack
1343             #-------------------------------------------------------
1344 46 50 66     96 if ( $type eq 'i' && $token =~ /^(package\s+)(\w.*)$/ ) {
1345 0         0 $token = $self->markup_html_element( $1, 'k' );
1346 0         0 push @colored_tokens, $token;
1347 0         0 $token = $2;
1348 0         0 $type = 'i';
1349 0         0 $self->add_toc_item( "$token", 'package' );
1350 0         0 $rpackage_stack->[$level] = $token;
1351             }
1352              
1353 46         84 $token = $self->markup_html_element( $token, $type );
1354 46         91 push @colored_tokens, $token;
1355             }
1356 2         10 return ( \@colored_tokens );
1357             } ## end sub markup_tokens
1358              
1359             sub markup_html_element {
1360 46     46 0 86 my ( $self, $token, $type ) = @_;
1361              
1362 46 100       91 return $token if ( $type eq 'b' ); # skip a blank token
1363 25 50       76 return $token if ( $token =~ /^\s*$/ ); # skip a blank line
1364 25         48 $token = escape_html($token);
1365              
1366             # get the short abbreviation for this token type
1367 25         70 my $short_name = $token_short_names{$type};
1368 25 100       54 if ( !defined($short_name) ) {
1369 4         6 $short_name = "pu"; # punctuation is default
1370             }
1371              
1372             # handle style sheets..
1373 25 50       50 if ( !$rOpts->{'nohtml-style-sheets'} ) {
1374 25 100       50 if ( $short_name ne 'pu' ) {
1375 21         55 $token = qq(<span class="$short_name">) . $token . "</span>";
1376             }
1377             }
1378              
1379             # handle no style sheets..
1380             else {
1381 0         0 my $color = $html_color{$short_name};
1382              
1383 0 0 0     0 if ( $color && ( $color ne $rOpts->{'html-color-punctuation'} ) ) {
1384 0         0 $token = qq(<font color="$color">) . $token . "</font>";
1385             }
1386 0 0       0 if ( $html_italic{$short_name} ) { $token = "<i>$token</i>" }
  0         0  
1387 0 0       0 if ( $html_bold{$short_name} ) { $token = "<b>$token</b>" }
  0         0  
1388             }
1389 25         46 return $token;
1390             } ## end sub markup_html_element
1391              
1392             sub escape_html {
1393              
1394 26     26 0 53 my $token = shift;
1395 26 50 33     79 if ( $missing_html_entities || !$rOpts_html_entities ) {
1396 0         0 $token =~ s/\&/&amp;/g;
1397 0         0 $token =~ s/\</&lt;/g;
1398 0         0 $token =~ s/\>/&gt;/g;
1399 0         0 $token =~ s/\"/&quot;/g;
1400             }
1401             else {
1402 26         65 HTML::Entities::encode_entities($token);
1403             }
1404 26         375 return $token;
1405             } ## end sub escape_html
1406              
1407             sub finish_formatting {
1408              
1409             # called after last line
1410 1     1 0 3 my $self = shift;
1411 1         5 $self->close_html_file();
1412 1         2 return;
1413             } ## end sub finish_formatting
1414              
1415             sub write_line {
1416              
1417 2     2 0 5 my ( $self, $line_of_tokens ) = @_;
1418 2 50       7 return unless $self->{_html_file_opened};
1419 2         6 my $html_pre_fh = $self->{_html_pre_fh};
1420 2         5 my $line_type = $line_of_tokens->{_line_type};
1421 2         4 my $input_line = $line_of_tokens->{_line_text};
1422 2         5 my $line_number = $line_of_tokens->{_line_number};
1423 2         5 chomp $input_line;
1424              
1425             # markup line of code..
1426 2         3 my $html_line;
1427 2 50       7 if ( $line_type eq 'CODE' ) {
1428 2         5 my $rtoken_type = $line_of_tokens->{_rtoken_type};
1429 2         3 my $rtokens = $line_of_tokens->{_rtokens};
1430 2         4 my $rlevels = $line_of_tokens->{_rlevels};
1431              
1432 2 50       12 if ( $input_line =~ /(^\s*)/ ) {
1433 2         11 $html_line = $1;
1434             }
1435             else {
1436 0         0 $html_line = EMPTY_STRING;
1437             }
1438 2         8 my ($rcolored_tokens) =
1439             $self->markup_tokens( $rtokens, $rtoken_type, $rlevels );
1440 2         10 $html_line .= join EMPTY_STRING, @{$rcolored_tokens};
  2         10  
1441             }
1442              
1443             # markup line of non-code..
1444             else {
1445 0         0 my $line_character;
1446 0 0       0 if ( $line_type eq 'HERE' ) { $line_character = 'H' }
  0 0       0  
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1447 0         0 elsif ( $line_type eq 'HERE_END' ) { $line_character = 'h' }
1448 0         0 elsif ( $line_type eq 'FORMAT' ) { $line_character = 'H' }
1449 0         0 elsif ( $line_type eq 'FORMAT_END' ) { $line_character = 'h' }
1450 0         0 elsif ( $line_type eq 'SKIP' ) { $line_character = 'H' }
1451 0         0 elsif ( $line_type eq 'SKIP_END' ) { $line_character = 'h' }
1452 0         0 elsif ( $line_type eq 'SYSTEM' ) { $line_character = 'c' }
1453             elsif ( $line_type eq 'END_START' ) {
1454 0         0 $line_character = 'k';
1455 0         0 $self->add_toc_item( '__END__', '__END__' );
1456             }
1457             elsif ( $line_type eq 'DATA_START' ) {
1458 0         0 $line_character = 'k';
1459 0         0 $self->add_toc_item( '__DATA__', '__DATA__' );
1460             }
1461             elsif ( $line_type =~ /^POD/ ) {
1462 0         0 $line_character = 'P';
1463 0 0       0 if ( $rOpts->{'pod2html'} ) {
1464 0         0 my $html_pod_fh = $self->{_html_pod_fh};
1465 0 0       0 if ( $line_type eq 'POD_START' ) {
1466              
1467 0         0 my $rpre_string_stack = $self->{_rpre_string_stack};
1468 0         0 my $rpre_string = $rpre_string_stack->[-1];
1469              
1470             # if we have written any non-blank lines to the
1471             # current pre section, start writing to a new output
1472             # string
1473 0 0       0 if ( ${$rpre_string} =~ /\S/ ) {
  0         0  
1474 0         0 my $pre_string;
1475 0         0 $html_pre_fh =
1476             Perl::Tidy::IOScalar->new( \$pre_string, 'w' );
1477 0         0 $self->{_html_pre_fh} = $html_pre_fh;
1478 0         0 push @{$rpre_string_stack}, \$pre_string;
  0         0  
1479              
1480             # leave a marker in the pod stream so we know
1481             # where to put the pre section we just
1482             # finished.
1483 0         0 my $for_html = '=for html'; # don't confuse pod utils
1484 0         0 $html_pod_fh->print(<<EOM);
1485              
1486             $for_html
1487             <!-- pERLTIDY sECTION -->
1488              
1489             EOM
1490             }
1491              
1492             # otherwise, just clear the current string and start
1493             # over
1494             else {
1495 0         0 ${$rpre_string} = EMPTY_STRING;
  0         0  
1496 0         0 $html_pod_fh->print("\n");
1497             }
1498             }
1499 0         0 $html_pod_fh->print( $input_line . "\n" );
1500 0 0       0 if ( $line_type eq 'POD_END' ) {
1501 0         0 $self->{_pod_cut_count}++;
1502 0         0 $html_pod_fh->print("\n");
1503             }
1504 0         0 return;
1505             }
1506             }
1507 0         0 else { $line_character = 'Q' }
1508 0         0 $html_line = $self->markup_html_element( $input_line, $line_character );
1509             }
1510              
1511             # add the line number if requested
1512 2 50       10 if ( $rOpts->{'html-line-numbers'} ) {
1513 0 0       0 my $extra_space =
    0          
    0          
1514             ( $line_number < 10 ) ? SPACE x 3
1515             : ( $line_number < 100 ) ? SPACE x 2
1516             : ( $line_number < 1000 ) ? SPACE
1517             : EMPTY_STRING;
1518 0         0 $html_line = $extra_space . $line_number . SPACE . $html_line;
1519             }
1520              
1521             # write the line
1522 2         13 $html_pre_fh->print("$html_line\n");
1523 2         34 return;
1524             } ## end sub write_line
1525             1;