Improve -DT output and fix wild buffer pointer error
[p5sagit/p5-mst-13.2.git] / lib / Pod / Html.pm
CommitLineData
54310121 1package Pod::Html;
0e4548d5 2use strict;
54310121 3require Exporter;
0e4548d5 4
776d31fe 5use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
612cfdf2 6$VERSION = 1.0503;
0e4548d5 7@ISA = qw(Exporter);
54310121 8@EXPORT = qw(pod2html htmlify);
776d31fe 9@EXPORT_OK = qw(anchorify);
54310121 10
11use Carp;
0e4548d5 12use Config;
13use Cwd;
af75be11 14use File::Spec;
0e4548d5 15use File::Spec::Unix;
16use Getopt::Long;
54310121 17
3ec07288 18use locale; # make \w work right in non-ASCII lands
19
54310121 20=head1 NAME
21
7b8d334a 22Pod::Html - module to convert pod files to HTML
54310121 23
24=head1 SYNOPSIS
25
7b8d334a 26 use Pod::Html;
54310121 27 pod2html([options]);
28
29=head1 DESCRIPTION
30
31Converts files from pod format (see L<perlpod>) to HTML format. It
32can automatically generate indexes and cross-references, and it keeps
33a cache of things it knows how to cross-reference.
34
35=head1 ARGUMENTS
36
37Pod::Html takes the following arguments:
38
39=over 4
40
0e4548d5 41=item backlink
42
43 --backlink="Back to Top"
44
59ecbafa 45Adds "Back to Top" links in front of every C<head1> heading (except for
46the first). By default, no backlinks are generated.
0e4548d5 47
b42210d7 48=item cachedir
49
50 --cachedir=name
51
52Creates the item and directory caches in the given directory.
53
0e4548d5 54=item css
55
56 --css=stylesheet
57
59ecbafa 58Specify the URL of a cascading style sheet. Also disables all HTML/CSS
59C<style> attributes that are output by default (to avoid conflicts).
0e4548d5 60
61=item flush
62
63 --flush
64
65Flushes the item and directory caches.
66
67=item header
68
69 --header
70 --noheader
71
59ecbafa 72Creates header and footer blocks containing the text of the C<NAME>
73section. By default, no headers are generated.
0e4548d5 74
54310121 75=item help
76
77 --help
78
79Displays the usage message.
80
99a6169d 81=item hiddendirs
82
83 --hiddendirs
84 --nohiddendirs
85
86Include hidden directories in the search for POD's in podpath if recurse
87is set.
88The default is not to traverse any directory whose name begins with C<.>.
89See L</"podpath"> and L</"recurse">.
90
91[This option is for backward compatibility only.
92It's hard to imagine that one would usefully create a module with a
93name component beginning with C<.>.]
94
5a039dd3 95=item htmldir
96
97 --htmldir=name
98
99Sets the directory in which the resulting HTML file is placed. This
29f227c9 100is used to generate relative links to other files. Not passing this
101causes all links to be absolute, since this is the value that tells
102Pod::Html the root of the documentation tree.
5a039dd3 103
54310121 104=item htmlroot
105
106 --htmlroot=name
107
108Sets the base URL for the HTML files. When cross-references are made,
109the HTML root is prepended to the URL.
110
0e4548d5 111=item index
112
113 --index
114 --noindex
115
116Generate an index at the top of the HTML file. This is the default
117behaviour.
118
54310121 119=item infile
120
121 --infile=name
122
123Specify the pod file to convert. Input is taken from STDIN if no
124infile is specified.
125
54310121 126=item libpods
127
128 --libpods=name:...:name
129
130List of page names (eg, "perlfunc") which contain linkable C<=item>s.
131
132=item netscape
133
134 --netscape
0e4548d5 135 --nonetscape
54310121 136
59ecbafa 137B<Deprecated>, has no effect. For backwards compatibility only.
54310121 138
0e4548d5 139=item outfile
54310121 140
0e4548d5 141 --outfile=name
54310121 142
0e4548d5 143Specify the HTML file to create. Output goes to STDOUT if no outfile
144is specified.
54310121 145
0e4548d5 146=item podpath
54310121 147
0e4548d5 148 --podpath=name:...:name
54310121 149
0e4548d5 150Specify which subdirectories of the podroot contain pod files whose
59ecbafa 151HTML converted forms can be linked to in cross references.
54310121 152
0e4548d5 153=item podroot
54310121 154
0e4548d5 155 --podroot=name
54310121 156
0e4548d5 157Specify the base directory for finding library pods.
54310121 158
0e4548d5 159=item quiet
54310121 160
0e4548d5 161 --quiet
162 --noquiet
54310121 163
0e4548d5 164Don't display I<mostly harmless> warning messages. These messages
165will be displayed by default. But this is not the same as C<verbose>
166mode.
54310121 167
0e4548d5 168=item recurse
54310121 169
0e4548d5 170 --recurse
54310121 171 --norecurse
172
0e4548d5 173Recurse into subdirectories specified in podpath (default behaviour).
54310121 174
175=item title
176
177 --title=title
178
179Specify the title of the resulting HTML file.
180
181=item verbose
182
183 --verbose
0e4548d5 184 --noverbose
54310121 185
0e4548d5 186Display progress messages. By default, they won't be displayed.
34db337b 187
54310121 188=back
189
190=head1 EXAMPLE
191
192 pod2html("pod2html",
59ecbafa 193 "--podpath=lib:ext:pod:vms",
54310121 194 "--podroot=/usr/src/perl",
195 "--htmlroot=/perl/nmanual",
196 "--libpods=perlfunc:perlguts:perlvar:perlrun:perlop",
197 "--recurse",
198 "--infile=foo.pod",
199 "--outfile=/perl/nmanual/foo.html");
200
34db337b 201=head1 ENVIRONMENT
202
59ecbafa 203Uses C<$Config{pod2html}> to setup default options.
34db337b 204
54310121 205=head1 AUTHOR
206
207Tom Christiansen, E<lt>tchrist@perl.comE<gt>.
208
54310121 209=head1 SEE ALSO
210
211L<perlpod>
212
213=head1 COPYRIGHT
214
215This program is distributed under the Artistic License.
216
217=cut
218
54310121 219
69348ecf 220my($Cachedir);
221my($Dircache, $Itemcache);
222my @Begin_Stack;
223my @Libpods;
224my($Htmlroot, $Htmldir, $Htmlfile, $Htmlfileurl);
225my($Podfile, @Podpath, $Podroot);
226my $Css;
54310121 227
69348ecf 228my $Recurse;
229my $Quiet;
99a6169d 230my $HiddenDirs;
69348ecf 231my $Verbose;
232my $Doindex;
54310121 233
69348ecf 234my $Backlink;
235my($Listlevel, @Listend);
236my $After_Lpar;
237use vars qw($Ignore); # need to localize it later.
99cb6bd8 238
69348ecf 239my(%Items_Named, @Items_Seen);
240my($Title, $Header);
99cb6bd8 241
69348ecf 242my $Top;
243my $Paragraph;
99cb6bd8 244
69348ecf 245my %Sections;
99cb6bd8 246
247# Caches
69348ecf 248my %Pages = (); # associative array used to find the location
54310121 249 # of pages referenced by L<> links.
69348ecf 250my %Items = (); # associative array used to find the location
54310121 251 # of =item directives referenced by C<> links
99cb6bd8 252
69348ecf 253my %Local_Items;
99cb6bd8 254my $Is83;
69348ecf 255my $PTQuote;
99cb6bd8 256
257my $Curdir = File::Spec->curdir;
258
259init_globals();
39e571d4 260
54310121 261sub init_globals {
69348ecf 262 $Cachedir = "."; # The directory to which item and directory
99cb6bd8 263 # caches will be written.
264
69348ecf 265 $Dircache = "pod2htmd.tmp";
266 $Itemcache = "pod2htmi.tmp";
54310121 267
69348ecf 268 @Begin_Stack = (); # begin/end stack
54310121 269
69348ecf 270 @Libpods = (); # files to search for links from C<> directives
271 $Htmlroot = "/"; # http-server base directory from which all
54310121 272 # relative paths in $podpath stem.
69348ecf 273 $Htmldir = ""; # The directory to which the html pages
67398a75 274 # will (eventually) be written.
69348ecf 275 $Htmlfile = ""; # write to stdout by default
276 $Htmlfileurl = "" ; # The url that other files would use to
99cb6bd8 277 # refer to this file. This is only used
278 # to make relative urls that point to
279 # other files.
280
69348ecf 281 $Podfile = ""; # read from stdin by default
282 @Podpath = (); # list of directories containing library pods.
283 $Podroot = $Curdir; # filesystem base directory from which all
54310121 284 # relative paths in $podpath stem.
69348ecf 285 $Css = ''; # Cascading style sheet
286 $Recurse = 1; # recurse on subdirectories in $podpath.
287 $Quiet = 0; # not quiet by default
288 $Verbose = 0; # not verbose by default
289 $Doindex = 1; # non-zero if we should generate an index
290 $Backlink = ''; # text for "back to top" links
291 $Listlevel = 0; # current list depth
292 @Listend = (); # the text to use to end the list.
293 $After_Lpar = 0; # set to true after a par in an =item
294 $Ignore = 1; # whether or not to format text. we don't
54310121 295 # format text until we hit our first pod
296 # directive.
297
69348ecf 298 @Items_Seen = (); # for multiples of the same item in perlfunc
299 %Items_Named = ();
300 $Header = 0; # produce block header/footer
301 $Title = ''; # title to give the pod(s)
302 $Top = 1; # true if we are at the top of the doc. used
59ecbafa 303 # to prevent the first <hr /> directive.
69348ecf 304 $Paragraph = ''; # which paragraph we're processing (used
54310121 305 # for error messages)
69348ecf 306 $PTQuote = 0; # status of double-quote conversion
307 %Sections = (); # sections within this page
3e3baf6d 308
69348ecf 309 %Local_Items = ();
99cb6bd8 310 $Is83 = $^O eq 'dos'; # Is it an 8.3 filesystem?
54310121 311}
312
2a28b791 313#
314# clean_data: global clean-up of pod data
315#
316sub clean_data($){
317 my( $dataref ) = @_;
bf202ccd 318 for my $i ( 0..$#{$dataref} ) {
2a28b791 319 ${$dataref}[$i] =~ s/\s+\Z//;
320
321 # have a look for all-space lines
4135c0a0 322 if( ${$dataref}[$i] =~ /^\s+$/m and $dataref->[$i] !~ /^\s/ ){
2a28b791 323 my @chunks = split( /^\s+$/m, ${$dataref}[$i] );
324 splice( @$dataref, $i, 1, @chunks );
325 }
326 }
327}
328
329
54310121 330sub pod2html {
331 local(@ARGV) = @_;
332 local($/);
333 local $_;
334
335 init_globals();
336
39e571d4 337 $Is83 = 0 if (defined (&Dos::UseLFN) && Dos::UseLFN());
338
69348ecf 339 # cache of %Pages and %Items from last time we ran pod2html
54310121 340
341 #undef $opt_help if defined $opt_help;
342
343 # parse the command-line parameters
344 parse_command_line();
345
59ecbafa 346 # escape the backlink argument (same goes for title but is done later...)
69348ecf 347 $Backlink = html_escape($Backlink) if defined $Backlink;
59ecbafa 348
54310121 349 # set some variables to their default values if necessary
350 local *POD;
59ecbafa 351 unless (@ARGV && $ARGV[0]) {
69348ecf 352 $Podfile = "-" unless $Podfile; # stdin
353 open(POD, "<$Podfile")
354 || die "$0: cannot open $Podfile file for input: $!\n";
54310121 355 } else {
69348ecf 356 $Podfile = $ARGV[0]; # XXX: might be more filenames
54310121 357 *POD = *ARGV;
59ecbafa 358 }
69348ecf 359 $Htmlfile = "-" unless $Htmlfile; # stdout
360 $Htmlroot = "" if $Htmlroot eq "/"; # so we don't get a //
361 $Htmldir =~ s#/\z## ; # so we don't get a //
362 if ( $Htmlroot eq ''
363 && defined( $Htmldir )
364 && $Htmldir ne ''
365 && substr( $Htmlfile, 0, length( $Htmldir ) ) eq $Htmldir
59ecbafa 366 )
5a039dd3 367 {
29f227c9 368 # Set the 'base' url for this file, so that we can use it
59ecbafa 369 # as the location from which to calculate relative links
29f227c9 370 # to other files. If this is '', then absolute links will
371 # be used throughout.
69348ecf 372 $Htmlfileurl= "$Htmldir/" . substr( $Htmlfile, length( $Htmldir ) + 1);
5a039dd3 373 }
54310121 374
375 # read the pod a paragraph at a time
69348ecf 376 warn "Scanning for sections in input file(s)\n" if $Verbose;
54310121 377 $/ = "";
378 my @poddata = <POD>;
379 close(POD);
bbed45f6 380
381 # be eol agnostic
382 for (@poddata) {
5551e547 383 if (/\r/) {
384 if (/\r\n/) {
385 @poddata = map { s/\r\n/\n/g;
bbed45f6 386 /\n\n/ ?
387 map { "$_\n\n" } split /\n\n/ :
388 $_ } @poddata;
389 } else {
5551e547 390 @poddata = map { s/\r/\n/g;
bbed45f6 391 /\n\n/ ?
392 map { "$_\n\n" } split /\n\n/ :
393 $_ } @poddata;
394 }
395 last;
396 }
397 }
398
2a28b791 399 clean_data( \@poddata );
54310121 400
401 # scan the pod for =head[1-6] directives and build an index
69348ecf 402 my $index = scan_headings(\%Sections, @poddata);
54310121 403
3e3baf6d 404 unless($index) {
69348ecf 405 warn "No headings in $Podfile\n" if $Verbose;
3e3baf6d 406 }
407
54310121 408 # open the output file
69348ecf 409 open(HTML, ">$Htmlfile")
410 || die "$0: cannot open $Htmlfile file for output: $!\n";
54310121 411
d011ffae 412 # put a title in the HTML file if one wasn't specified
69348ecf 413 if ($Title eq '') {
d011ffae 414 TITLE_SEARCH: {
59ecbafa 415 for (my $i = 0; $i < @poddata; $i++) {
d011ffae 416 if ($poddata[$i] =~ /^=head1\s*NAME\b/m) {
59ecbafa 417 for my $para ( @poddata[$i, $i+1] ) {
d011ffae 418 last TITLE_SEARCH
69348ecf 419 if ($Title) = $para =~ /(\S+\s+-+.*\S)/s;
d011ffae 420 }
59ecbafa 421 }
54310121 422
59ecbafa 423 }
d011ffae 424 }
425 }
69348ecf 426 if (!$Title and $Podfile =~ /\.pod\z/) {
3e3baf6d 427 # probably a split pod so take first =head[12] as title
59ecbafa 428 for (my $i = 0; $i < @poddata; $i++) {
69348ecf 429 last if ($Title) = $poddata[$i] =~ /^=head[12]\s*(.*)/;
59ecbafa 430 }
69348ecf 431 warn "adopted '$Title' as title for $Podfile\n"
432 if $Verbose and $Title;
59ecbafa 433 }
69348ecf 434 if ($Title) {
435 $Title =~ s/\s*\(.*\)//;
7b8d334a 436 } else {
69348ecf 437 warn "$0: no title for $Podfile.\n" unless $Quiet;
438 $Podfile =~ /^(.*)(\.[^.\/]+)?\z/s;
439 $Title = ($Podfile eq "-" ? 'No Title' : $1);
440 warn "using $Title" if $Verbose;
54310121 441 }
69348ecf 442 $Title = html_escape($Title);
59ecbafa 443
444 my $csslink = '';
445 my $bodystyle = ' style="background-color: white"';
446 my $tdstyle = ' style="background-color: #cccccc"';
447
69348ecf 448 if ($Css) {
449 $csslink = qq(\n<link rel="stylesheet" href="$Css" type="text/css" />);
59ecbafa 450 $csslink =~ s,\\,/,g;
451 $csslink =~ s,(/.):,$1|,;
452 $bodystyle = '';
453 $tdstyle = '';
454 }
455
69348ecf 456 my $block = $Header ? <<END_OF_BLOCK : '';
59ecbafa 457<table border="0" width="100%" cellspacing="0" cellpadding="3">
458<tr><td class="block"$tdstyle valign="middle">
69348ecf 459<big><strong><span class="block">&nbsp;$Title</span></strong></big>
59ecbafa 460</td></tr>
461</table>
34db337b 462END_OF_BLOCK
463
54310121 464 print HTML <<END_OF_HEAD;
59ecbafa 465<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
466<html xmlns="http://www.w3.org/1999/xhtml">
8d481ee5 467<head>
69348ecf 468<title>$Title</title>$csslink
59ecbafa 469<link rev="made" href="mailto:$Config{perladmin}" />
470</head>
471
472<body$bodystyle>
34db337b 473$block
54310121 474END_OF_HEAD
475
69348ecf 476 # load/reload/validate/cache %Pages and %Items
477 get_cache($Dircache, $Itemcache, \@Podpath, $Podroot, $Recurse);
54310121 478
479 # scan the pod for =item directives
69348ecf 480 scan_items( \%Local_Items, "", @poddata);
54310121 481
69348ecf 482 # put an index at the top of the file. note, if $Doindex is 0 we
54310121 483 # still generate an index, but surround it with an html comment.
484 # that way some other program can extract it if desired.
485 $index =~ s/--+/-/g;
59ecbafa 486 print HTML "<p><a name=\"__index__\"></a></p>\n";
54310121 487 print HTML "<!-- INDEX BEGIN -->\n";
69348ecf 488 print HTML "<!--\n" unless $Doindex;
54310121 489 print HTML $index;
69348ecf 490 print HTML "-->\n" unless $Doindex;
54310121 491 print HTML "<!-- INDEX END -->\n\n";
69348ecf 492 print HTML "<hr />\n" if $Doindex and $index;
54310121 493
494 # now convert this file
2a28b791 495 my $after_item; # set to true after an =item
59ecbafa 496 my $need_dd = 0;
69348ecf 497 warn "Converting input file $Podfile\n" if $Verbose;
2a28b791 498 foreach my $i (0..$#poddata){
69348ecf 499 $PTQuote = 0; # status of quote conversion
2a28b791 500
54310121 501 $_ = $poddata[$i];
69348ecf 502 $Paragraph = $i+1;
54310121 503 if (/^(=.*)/s) { # is it a pod directive?
69348ecf 504 $Ignore = 0;
2a28b791 505 $after_item = 0;
59ecbafa 506 $need_dd = 0;
54310121 507 $_ = $1;
508 if (/^=begin\s+(\S+)\s*(.*)/si) {# =begin
509 process_begin($1, $2);
510 } elsif (/^=end\s+(\S+)\s*(.*)/si) {# =end
511 process_end($1, $2);
512 } elsif (/^=cut/) { # =cut
513 process_cut();
514 } elsif (/^=pod/) { # =pod
515 process_pod();
516 } else {
69348ecf 517 next if @Begin_Stack && $Begin_Stack[-1] ne 'html';
54310121 518
7b8d334a 519 if (/^=(head[1-6])\s+(.*\S)/s) { # =head[1-6] heading
69348ecf 520 process_head( $1, $2, $Doindex && $index );
2a28b791 521 } elsif (/^=item\s*(.*\S)?/sm) { # =item text
59ecbafa 522 $need_dd = process_item( $1 );
2a28b791 523 $after_item = 1;
54310121 524 } elsif (/^=over\s*(.*)/) { # =over N
525 process_over();
526 } elsif (/^=back/) { # =back
945ffa4f 527 process_back($need_dd);
a45bd81d 528 } elsif (/^=for\s+(\S+)\s*(.*)/si) {# =for
54310121 529 process_for($1,$2);
530 } else {
531 /^=(\S*)\s*/;
69348ecf 532 warn "$0: $Podfile: unknown pod directive '$1' in "
ac094485 533 . "paragraph $Paragraph. ignoring.\n" unless $Quiet;
54310121 534 }
535 }
69348ecf 536 $Top = 0;
54310121 537 }
538 else {
69348ecf 539 next if $Ignore;
540 next if @Begin_Stack && $Begin_Stack[-1] ne 'html';
541 print HTML and next if @Begin_Stack && $Begin_Stack[-1] eq 'html';
59ecbafa 542 print HTML "<dd>\n" if $need_dd;
54310121 543 my $text = $_;
2a28b791 544 if( $text =~ /\A\s+/ ){
545 process_pre( \$text );
59ecbafa 546 print HTML "<pre>\n$text</pre>\n";
2a28b791 547
548 } else {
549 process_text( \$text );
550
551 # experimental: check for a paragraph where all lines
552 # have some ...\t...\t...\n pattern
553 if( $text =~ /\t/ ){
554 my @lines = split( "\n", $text );
555 if( @lines > 1 ){
556 my $all = 2;
557 foreach my $line ( @lines ){
558 if( $line =~ /\S/ && $line !~ /\t/ ){
559 $all--;
560 last if $all == 0;
561 }
562 }
563 if( $all > 0 ){
59ecbafa 564 $text =~ s/\t+/<td>/g;
565 $text =~ s/^/<tr><td>/gm;
566 $text = '<table cellspacing="0" cellpadding="0">' .
567 $text . '</table>';
2a28b791 568 }
569 }
570 }
571 ## end of experimental
572
573 if( $after_item ){
69348ecf 574 $After_Lpar = 1;
2a28b791 575 }
945ffa4f 576 print HTML "<p>$text</p>\n";
2a28b791 577 }
59ecbafa 578 print HTML "</dd>\n" if $need_dd;
2a28b791 579 $after_item = 0;
54310121 580 }
581 }
582
583 # finish off any pending directives
584 finish_list();
2a28b791 585
586 # link to page index
69348ecf 587 print HTML "<p><a href=\"#__index__\"><small>$Backlink</small></a></p>\n"
588 if $Doindex and $index and $Backlink;
2a28b791 589
54310121 590 print HTML <<END_OF_TAIL;
34db337b 591$block
59ecbafa 592</body>
54310121 593
59ecbafa 594</html>
54310121 595END_OF_TAIL
596
597 # close the html file
598 close(HTML);
599
69348ecf 600 warn "Finished\n" if $Verbose;
54310121 601}
602
603##############################################################################
604
54310121 605sub usage {
606 my $podfile = shift;
607 warn "$0: $podfile: @_\n" if @_;
69348ecf 608 die <<END_OF_USAGE;
54310121 609Usage: $0 --help --htmlroot=<name> --infile=<name> --outfile=<name>
610 --podpath=<name>:...:<name> --podroot=<name>
611 --libpods=<name>:...:<name> --recurse --verbose --index
b42210d7 612 --netscape --norecurse --noindex --cachedir=<name>
54310121 613
0e4548d5 614 --backlink - set text for "back to top" links (default: none).
b42210d7 615 --cachedir - directory for the item and directory cache files.
0e4548d5 616 --css - stylesheet URL
617 --flush - flushes the item and directory caches.
618 --[no]header - produce block header/footer (default is no headers).
619 --help - prints this message.
99a6169d 620 --hiddendirs - search hidden directories in podpath
0e4548d5 621 --htmldir - directory for resulting HTML files.
622 --htmlroot - http-server base directory from which all relative paths
623 in podpath stem (default is /).
624 --[no]index - generate an index at the top of the resulting html
625 (default behaviour).
626 --infile - filename for the pod to convert (input taken from stdin
627 by default).
628 --libpods - colon-separated list of pages to search for =item pod
629 directives in as targets of C<> and implicit links (empty
630 by default). note, these are not filenames, but rather
631 page names like those that appear in L<> links.
0e4548d5 632 --outfile - filename for the resulting html file (output sent to
633 stdout by default).
634 --podpath - colon-separated list of directories containing library
635 pods (empty by default).
636 --podroot - filesystem base directory from which all relative paths
637 in podpath stem (default is .).
3c4b39be 638 --[no]quiet - suppress some benign warning messages (default is off).
0e4548d5 639 --[no]recurse - recurse on those subdirectories listed in podpath
640 (default behaviour).
641 --title - title that will appear in resulting html file.
642 --[no]verbose - self-explanatory (off by default).
59ecbafa 643 --[no]netscape - deprecated, has no effect. for backwards compatibility only.
54310121 644
645END_OF_USAGE
646
69348ecf 647}
648
54310121 649sub parse_command_line {
b42210d7 650 my ($opt_backlink,$opt_cachedir,$opt_css,$opt_flush,$opt_header,$opt_help,
651 $opt_htmldir,$opt_htmlroot,$opt_index,$opt_infile,$opt_libpods,
652 $opt_netscape,$opt_outfile,$opt_podpath,$opt_podroot,$opt_quiet,
99a6169d 653 $opt_recurse,$opt_title,$opt_verbose,$opt_hiddendirs);
0e4548d5 654
34db337b 655 unshift @ARGV, split ' ', $Config{pod2html} if $Config{pod2html};
54310121 656 my $result = GetOptions(
0e4548d5 657 'backlink=s' => \$opt_backlink,
b42210d7 658 'cachedir=s' => \$opt_cachedir,
0e4548d5 659 'css=s' => \$opt_css,
29f227c9 660 'flush' => \$opt_flush,
0e4548d5 661 'header!' => \$opt_header,
29f227c9 662 'help' => \$opt_help,
99a6169d 663 'hiddendirs!'=> \$opt_hiddendirs,
29f227c9 664 'htmldir=s' => \$opt_htmldir,
54310121 665 'htmlroot=s' => \$opt_htmlroot,
29f227c9 666 'index!' => \$opt_index,
54310121 667 'infile=s' => \$opt_infile,
668 'libpods=s' => \$opt_libpods,
669 'netscape!' => \$opt_netscape,
670 'outfile=s' => \$opt_outfile,
671 'podpath=s' => \$opt_podpath,
672 'podroot=s' => \$opt_podroot,
0e4548d5 673 'quiet!' => \$opt_quiet,
54310121 674 'recurse!' => \$opt_recurse,
675 'title=s' => \$opt_title,
0e4548d5 676 'verbose!' => \$opt_verbose,
54310121 677 );
678 usage("-", "invalid parameters") if not $result;
679
680 usage("-") if defined $opt_help; # see if the user asked for help
681 $opt_help = ""; # just to make -w shut-up.
682
69348ecf 683 @Podpath = split(":", $opt_podpath) if defined $opt_podpath;
684 @Libpods = split(":", $opt_libpods) if defined $opt_libpods;
685
686 $Backlink = $opt_backlink if defined $opt_backlink;
687 $Cachedir = $opt_cachedir if defined $opt_cachedir;
688 $Css = $opt_css if defined $opt_css;
689 $Header = $opt_header if defined $opt_header;
690 $Htmldir = $opt_htmldir if defined $opt_htmldir;
691 $Htmlroot = $opt_htmlroot if defined $opt_htmlroot;
692 $Doindex = $opt_index if defined $opt_index;
693 $Podfile = $opt_infile if defined $opt_infile;
99a6169d 694 $HiddenDirs = $opt_hiddendirs if defined $opt_hiddendirs;
69348ecf 695 $Htmlfile = $opt_outfile if defined $opt_outfile;
696 $Podroot = $opt_podroot if defined $opt_podroot;
697 $Quiet = $opt_quiet if defined $opt_quiet;
698 $Recurse = $opt_recurse if defined $opt_recurse;
699 $Title = $opt_title if defined $opt_title;
700 $Verbose = $opt_verbose if defined $opt_verbose;
0e4548d5 701
54310121 702 warn "Flushing item and directory caches\n"
703 if $opt_verbose && defined $opt_flush;
69348ecf 704 $Dircache = "$Cachedir/pod2htmd.tmp";
705 $Itemcache = "$Cachedir/pod2htmi.tmp";
383e43c7 706 if (defined $opt_flush) {
69348ecf 707 1 while unlink($Dircache, $Itemcache);
383e43c7 708 }
54310121 709}
710
3e3baf6d 711
69348ecf 712my $Saved_Cache_Key;
3e3baf6d 713
714sub get_cache {
715 my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
716 my @cache_key_args = @_;
717
718 # A first-level cache:
719 # Don't bother reading the cache files if they still apply
720 # and haven't changed since we last read them.
721
722 my $this_cache_key = cache_key(@cache_key_args);
723
69348ecf 724 return if $Saved_Cache_Key and $this_cache_key eq $Saved_Cache_Key;
3e3baf6d 725
69348ecf 726 # load the cache of %Pages and %Items if possible. $tests will be
3e3baf6d 727 # non-zero if successful.
728 my $tests = 0;
729 if (-f $dircache && -f $itemcache) {
69348ecf 730 warn "scanning for item cache\n" if $Verbose;
3e3baf6d 731 $tests = load_cache($dircache, $itemcache, $podpath, $podroot);
732 }
733
734 # if we didn't succeed in loading the cache then we must (re)build
69348ecf 735 # %Pages and %Items.
3e3baf6d 736 if (!$tests) {
69348ecf 737 warn "scanning directories in pod-path\n" if $Verbose;
3e3baf6d 738 scan_podpath($podroot, $recurse, 0);
739 }
69348ecf 740 $Saved_Cache_Key = cache_key(@cache_key_args);
3e3baf6d 741}
742
743sub cache_key {
744 my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
745 return join('!', $dircache, $itemcache, $recurse,
29f227c9 746 @$podpath, $podroot, stat($dircache), stat($itemcache));
3e3baf6d 747}
748
54310121 749#
3e3baf6d 750# load_cache - tries to find if the caches stored in $dircache and $itemcache
69348ecf 751# are valid caches of %Pages and %Items. if they are valid then it loads
54310121 752# them and returns a non-zero value.
753#
3e3baf6d 754sub load_cache {
54310121 755 my($dircache, $itemcache, $podpath, $podroot) = @_;
756 my($tests);
757 local $_;
758
759 $tests = 0;
760
761 open(CACHE, "<$itemcache") ||
762 die "$0: error opening $itemcache for reading: $!\n";
763 $/ = "\n";
764
765 # is it the same podpath?
766 $_ = <CACHE>;
767 chomp($_);
3e3baf6d 768 $tests++ if (join(":", @$podpath) eq $_);
54310121 769
770 # is it the same podroot?
771 $_ = <CACHE>;
772 chomp($_);
773 $tests++ if ($podroot eq $_);
774
775 # load the cache if its good
776 if ($tests != 2) {
777 close(CACHE);
54310121 778 return 0;
779 }
780
69348ecf 781 warn "loading item cache\n" if $Verbose;
54310121 782 while (<CACHE>) {
783 /(.*?) (.*)$/;
69348ecf 784 $Items{$1} = $2;
54310121 785 }
786 close(CACHE);
787
69348ecf 788 warn "scanning for directory cache\n" if $Verbose;
54310121 789 open(CACHE, "<$dircache") ||
790 die "$0: error opening $dircache for reading: $!\n";
791 $/ = "\n";
792 $tests = 0;
793
794 # is it the same podpath?
795 $_ = <CACHE>;
796 chomp($_);
3e3baf6d 797 $tests++ if (join(":", @$podpath) eq $_);
54310121 798
799 # is it the same podroot?
800 $_ = <CACHE>;
801 chomp($_);
802 $tests++ if ($podroot eq $_);
803
804 # load the cache if its good
805 if ($tests != 2) {
806 close(CACHE);
54310121 807 return 0;
808 }
809
69348ecf 810 warn "loading directory cache\n" if $Verbose;
54310121 811 while (<CACHE>) {
812 /(.*?) (.*)$/;
69348ecf 813 $Pages{$1} = $2;
54310121 814 }
815
816 close(CACHE);
817
818 return 1;
819}
820
821#
822# scan_podpath - scans the directories specified in @podpath for directories,
823# .pod files, and .pm files. it also scans the pod files specified in
69348ecf 824# @Libpods for =item directives.
54310121 825#
826sub scan_podpath {
3e3baf6d 827 my($podroot, $recurse, $append) = @_;
54310121 828 my($pwd, $dir);
829 my($libpod, $dirname, $pod, @files, @poddata);
830
3e3baf6d 831 unless($append) {
69348ecf 832 %Items = ();
833 %Pages = ();
3e3baf6d 834 }
835
69348ecf 836 # scan each directory listed in @Podpath
54310121 837 $pwd = getcwd();
838 chdir($podroot)
839 || die "$0: error changing to directory $podroot: $!\n";
69348ecf 840 foreach $dir (@Podpath) {
54310121 841 scan_dir($dir, $recurse);
842 }
843
69348ecf 844 # scan the pods listed in @Libpods for =item directives
845 foreach $libpod (@Libpods) {
54310121 846 # if the page isn't defined then we won't know where to find it
847 # on the system.
69348ecf 848 next unless defined $Pages{$libpod} && $Pages{$libpod};
54310121 849
850 # if there is a directory then use the .pod and .pm files within it.
29f227c9 851 # NOTE: Only finds the first so-named directory in the tree.
69348ecf 852# if ($Pages{$libpod} =~ /([^:]*[^(\.pod|\.pm)]):/) {
853 if ($Pages{$libpod} =~ /([^:]*(?<!\.pod)(?<!\.pm)):/) {
54310121 854 # find all the .pod and .pm files within the directory
855 $dirname = $1;
856 opendir(DIR, $dirname) ||
857 die "$0: error opening directory $dirname: $!\n";
fe6f1558 858 @files = grep(/(\.pod|\.pm)\z/ && ! -d $_, readdir(DIR));
54310121 859 closedir(DIR);
860
861 # scan each .pod and .pm file for =item directives
862 foreach $pod (@files) {
863 open(POD, "<$dirname/$pod") ||
864 die "$0: error opening $dirname/$pod for input: $!\n";
865 @poddata = <POD>;
866 close(POD);
2a28b791 867 clean_data( \@poddata );
54310121 868
69348ecf 869 scan_items( \%Items, "$dirname/$pod", @poddata);
54310121 870 }
871
872 # use the names of files as =item directives too.
2a28b791 873### Don't think this should be done this way - confuses issues.(WL)
874### foreach $pod (@files) {
875### $pod =~ /^(.*)(\.pod|\.pm)$/;
69348ecf 876### $Items{$1} = "$dirname/$1.html" if $1;
2a28b791 877### }
69348ecf 878 } elsif ($Pages{$libpod} =~ /([^:]*\.pod):/ ||
879 $Pages{$libpod} =~ /([^:]*\.pm):/) {
54310121 880 # scan the .pod or .pm file for =item directives
881 $pod = $1;
882 open(POD, "<$pod") ||
883 die "$0: error opening $pod for input: $!\n";
884 @poddata = <POD>;
885 close(POD);
2a28b791 886 clean_data( \@poddata );
54310121 887
69348ecf 888 scan_items( \%Items, "$pod", @poddata);
54310121 889 } else {
ac094485 890 warn "$0: shouldn't be here (line ".__LINE__."\n" unless $Quiet;
54310121 891 }
892 }
893 @poddata = (); # clean-up a bit
894
895 chdir($pwd)
896 || die "$0: error changing to directory $pwd: $!\n";
897
898 # cache the item list for later use
69348ecf 899 warn "caching items for later use\n" if $Verbose;
900 open(CACHE, ">$Itemcache") ||
901 die "$0: error open $Itemcache for writing: $!\n";
54310121 902
69348ecf 903 print CACHE join(":", @Podpath) . "\n$podroot\n";
904 foreach my $key (keys %Items) {
905 print CACHE "$key $Items{$key}\n";
54310121 906 }
907
908 close(CACHE);
909
910 # cache the directory list for later use
69348ecf 911 warn "caching directories for later use\n" if $Verbose;
912 open(CACHE, ">$Dircache") ||
913 die "$0: error open $Dircache for writing: $!\n";
54310121 914
69348ecf 915 print CACHE join(":", @Podpath) . "\n$podroot\n";
916 foreach my $key (keys %Pages) {
917 print CACHE "$key $Pages{$key}\n";
54310121 918 }
919
920 close(CACHE);
921}
922
923#
924# scan_dir - scans the directory specified in $dir for subdirectories, .pod
925# files, and .pm files. notes those that it finds. this information will
926# be used later in order to figure out where the pages specified in L<>
927# links are on the filesystem.
928#
929sub scan_dir {
930 my($dir, $recurse) = @_;
931 my($t, @subdirs, @pods, $pod, $dirname, @dirs);
932 local $_;
933
934 @subdirs = ();
935 @pods = ();
936
937 opendir(DIR, $dir) ||
938 die "$0: error opening directory $dir: $!\n";
939 while (defined($_ = readdir(DIR))) {
99a6169d 940 if (-d "$dir/$_" && $_ ne "." && $_ ne ".."
941 && ($HiddenDirs || !/^\./)
942 ) { # directory
69348ecf 943 $Pages{$_} = "" unless defined $Pages{$_};
944 $Pages{$_} .= "$dir/$_:";
54310121 945 push(@subdirs, $_);
fe6f1558 946 } elsif (/\.pod\z/) { # .pod
947 s/\.pod\z//;
69348ecf 948 $Pages{$_} = "" unless defined $Pages{$_};
949 $Pages{$_} .= "$dir/$_.pod:";
54310121 950 push(@pods, "$dir/$_.pod");
d3f9d0fb 951 } elsif (/\.html\z/) { # .html
952 s/\.html\z//;
69348ecf 953 $Pages{$_} = "" unless defined $Pages{$_};
954 $Pages{$_} .= "$dir/$_.pod:";
fe6f1558 955 } elsif (/\.pm\z/) { # .pm
956 s/\.pm\z//;
69348ecf 957 $Pages{$_} = "" unless defined $Pages{$_};
958 $Pages{$_} .= "$dir/$_.pm:";
54310121 959 push(@pods, "$dir/$_.pm");
945ffa4f 960 } elsif (-T "$dir/$_") { # script(?)
961 local *F;
962 if (open(F, "$dir/$_")) {
963 my $line;
964 while (defined($line = <F>)) {
965 if ($line =~ /^=(?:pod|head1)/) {
966 $Pages{$_} = "" unless defined $Pages{$_};
967 $Pages{$_} .= "$dir/$_.pod:";
968 last;
969 }
970 }
971 close(F);
972 }
54310121 973 }
974 }
975 closedir(DIR);
976
977 # recurse on the subdirectories if necessary
978 if ($recurse) {
979 foreach my $subdir (@subdirs) {
980 scan_dir("$dir/$subdir", $recurse);
981 }
982 }
983}
984
985#
986# scan_headings - scan a pod file for head[1-6] tags, note the tags, and
987# build an index.
988#
989sub scan_headings {
990 my($sections, @data) = @_;
2a28b791 991 my($tag, $which_head, $otitle, $listdepth, $index);
54310121 992
69348ecf 993 local $Ignore = 0;
be173d55 994
54310121 995 $listdepth = 0;
996 $index = "";
997
998 # scan for =head directives, note their name, and build an index
999 # pointing to each of them.
1000 foreach my $line (@data) {
59ecbafa 1001 if ($line =~ /^=(head)([1-6])\s+(.*)/) {
1002 ($tag, $which_head, $otitle) = ($1,$2,$3);
2a28b791 1003
59ecbafa 1004 my $title = depod( $otitle );
2beb85b7 1005 my $name = anchorify( $title );
59ecbafa 1006 $$sections{$name} = 1;
59ecbafa 1007 $title = process_text( \$otitle );
54310121 1008
102c538a 1009 while ($which_head != $listdepth) {
1010 if ($which_head > $listdepth) {
59ecbafa 1011 $index .= "\n" . ("\t" x $listdepth) . "<ul>\n";
102c538a 1012 $listdepth++;
1013 } elsif ($which_head < $listdepth) {
1014 $listdepth--;
59ecbafa 1015 $index .= "\n" . ("\t" x $listdepth) . "</ul>\n";
102c538a 1016 }
54310121 1017 }
54310121 1018
59ecbafa 1019 $index .= "\n" . ("\t" x $listdepth) . "<li>" .
1020 "<a href=\"#" . $name . "\">" .
1021 $title . "</a></li>";
54310121 1022 }
1023 }
1024
1025 # finish off the lists
1026 while ($listdepth--) {
59ecbafa 1027 $index .= "\n" . ("\t" x $listdepth) . "</ul>\n";
54310121 1028 }
1029
1030 # get rid of bogus lists
59ecbafa 1031 $index =~ s,\t*<ul>\s*</ul>\n,,g;
54310121 1032
1033 return $index;
1034}
1035
1036#
1037# scan_items - scans the pod specified by $pod for =item directives. we
1038# will use this information later on in resolving C<> links.
1039#
1040sub scan_items {
2a28b791 1041 my( $itemref, $pod, @poddata ) = @_;
54310121 1042 my($i, $item);
1043 local $_;
1044
fe6f1558 1045 $pod =~ s/\.pod\z//;
54310121 1046 $pod .= ".html" if $pod;
1047
1048 foreach $i (0..$#poddata) {
2a28b791 1049 my $txt = depod( $poddata[$i] );
1050
1051 # figure out what kind of item it is.
1052 # Build string for referencing this item.
1053 if ( $txt =~ /\A=item\s+\*\s*(.*)\Z/s ) { # bullet
1054 next unless $1;
1055 $item = $1;
1056 } elsif( $txt =~ /\A=item\s+(?>\d+\.?)\s*(.*)\Z/s ) { # numbered list
1057 $item = $1;
1058 } elsif( $txt =~ /\A=item\s+(.*)\Z/s ) { # plain item
1059 $item = $1;
1060 } else {
1061 next;
54310121 1062 }
2a28b791 1063 my $fid = fragment_id( $item );
1064 $$itemref{$fid} = "$pod" if $fid;
54310121 1065 }
1066}
1067
1068#
1069# process_head - convert a pod head[1-6] tag and convert it to HTML format.
1070#
1071sub process_head {
2a28b791 1072 my($tag, $heading, $hasindex) = @_;
54310121 1073
1074 # figure out the level of the =head
1075 $tag =~ /head([1-6])/;
1076 my $level = $1;
1077
69348ecf 1078 if( $Listlevel ){
ac094485 1079 warn "$0: $Podfile: unterminated list at =head in paragraph $Paragraph. ignoring.\n" unless $Quiet;
69348ecf 1080 while( $Listlevel ){
2a28b791 1081 process_back();
1082 }
1083 }
1084
59ecbafa 1085 print HTML "<p>\n";
69348ecf 1086 if( $level == 1 && ! $Top ){
1087 print HTML "<a href=\"#__index__\"><small>$Backlink</small></a>\n"
1088 if $hasindex and $Backlink;
59ecbafa 1089 print HTML "</p>\n<hr />\n"
1090 } else {
1091 print HTML "</p>\n";
2a28b791 1092 }
1093
0d396dd4 1094 my $name = anchorify( depod( $heading ) );
2a28b791 1095 my $convert = process_text( \$heading );
59ecbafa 1096 print HTML "<h$level><a name=\"$name\">$convert</a></h$level>\n";
54310121 1097}
1098
2a28b791 1099
54310121 1100#
2a28b791 1101# emit_item_tag - print an =item's text
1102# Note: The global $EmittedItem is used for inhibiting self-references.
54310121 1103#
2a28b791 1104my $EmittedItem;
1105
1106sub emit_item_tag($$$){
1107 my( $otext, $text, $compact ) = @_;
1108 my $item = fragment_id( $text );
54310121 1109
2a28b791 1110 $EmittedItem = $item;
1111 ### print STDERR "emit_item_tag=$item ($text)\n";
54310121 1112
59ecbafa 1113 print HTML '<strong>';
69348ecf 1114 if ($Items_Named{$item}++) {
2a28b791 1115 print HTML process_text( \$otext );
1116 } else {
0d396dd4 1117 my $name = 'item_' . $item;
1118 $name = anchorify($name);
59ecbafa 1119 print HTML qq{<a name="$name">}, process_text( \$otext ), '</a>';
2a28b791 1120 }
945ffa4f 1121 print HTML "</strong>\n";
2a28b791 1122 undef( $EmittedItem );
1123}
1124
1125sub emit_li {
1126 my( $tag ) = @_;
69348ecf 1127 if( $Items_Seen[$Listlevel]++ == 0 ){
1128 push( @Listend, "</$tag>" );
2a28b791 1129 print HTML "<$tag>\n";
1130 }
59ecbafa 1131 my $emitted = $tag eq 'dl' ? 'dt' : 'li';
1132 print HTML "<$emitted>";
1133 return $emitted;
2a28b791 1134}
1135
1136#
1137# process_item - convert a pod item tag and convert it to HTML format.
1138#
1139sub process_item {
1140 my( $otext ) = @_;
59ecbafa 1141 my $need_dd = 0; # set to 1 if we need a <dd></dd> after an item
54310121 1142
1143 # lots of documents start a list without doing an =over. this is
1144 # bad! but, the proper thing to do seems to be to just assume
1145 # they did do an =over. so warn them once and then continue.
69348ecf 1146 if( $Listlevel == 0 ){
ac094485 1147 warn "$0: $Podfile: unexpected =item directive in paragraph $Paragraph. ignoring.\n" unless $Quiet;
2a28b791 1148 process_over();
1149 }
54310121 1150
2a28b791 1151 # formatting: insert a paragraph if preceding item has >1 paragraph
69348ecf 1152 if( $After_Lpar ){
945ffa4f 1153 print HTML $need_dd ? "</dd>\n" : "</li>\n" if $After_Lpar;
69348ecf 1154 $After_Lpar = 0;
2a28b791 1155 }
54310121 1156
1157 # remove formatting instructions from the text
2a28b791 1158 my $text = depod( $otext );
1159
59ecbafa 1160 my $emitted; # the tag actually emitted, used for closing
1161
2a28b791 1162 # all the list variants:
1163 if( $text =~ /\A\*/ ){ # bullet
59ecbafa 1164 $emitted = emit_li( 'ul' );
1165 if ($text =~ /\A\*\s+(.+)\Z/s ) { # with additional text
1166 my $tag = $1;
1167 $otext =~ s/\A\*\s+//;
1168 emit_item_tag( $otext, $tag, 1 );
1169 }
54310121 1170
2a28b791 1171 } elsif( $text =~ /\A\d+/ ){ # numbered list
59ecbafa 1172 $emitted = emit_li( 'ol' );
1173 if ($text =~ /\A(?>\d+\.?)\s*(.+)\Z/s ) { # with additional text
1174 my $tag = $1;
1175 $otext =~ s/\A\d+\.?\s*//;
1176 emit_item_tag( $otext, $tag, 1 );
1177 }
54310121 1178
2a28b791 1179 } else { # definition list
59ecbafa 1180 $emitted = emit_li( 'dl' );
1181 if ($text =~ /\A(.+)\Z/s ){ # should have text
1182 emit_item_tag( $otext, $text, 1 );
1183 }
1184 $need_dd = 1;
54310121 1185 }
54310121 1186 print HTML "\n";
59ecbafa 1187 return $need_dd;
54310121 1188}
1189
1190#
2a28b791 1191# process_over - process a pod over tag and start a corresponding HTML list.
54310121 1192#
1193sub process_over {
1194 # start a new list
69348ecf 1195 $Listlevel++;
1196 push( @Items_Seen, 0 );
1197 $After_Lpar = 0;
54310121 1198}
1199
1200#
1201# process_back - process a pod back tag and convert it to HTML format.
1202#
1203sub process_back {
945ffa4f 1204 my $need_dd = shift;
69348ecf 1205 if( $Listlevel == 0 ){
ac094485 1206 warn "$0: $Podfile: unexpected =back directive in paragraph $Paragraph. ignoring.\n" unless $Quiet;
2a28b791 1207 return;
1208 }
54310121 1209
69348ecf 1210 # close off the list. note, I check to see if $Listend[$Listlevel] is
54310121 1211 # defined because an =item directive may have never appeared and thus
69348ecf 1212 # $Listend[$Listlevel] may have never been initialized.
1213 $Listlevel--;
1214 if( defined $Listend[$Listlevel] ){
945ffa4f 1215 print HTML $need_dd ? "</dd>\n" : "</li>\n" if $After_Lpar;
69348ecf 1216 print HTML $Listend[$Listlevel];
2a28b791 1217 print HTML "\n";
69348ecf 1218 pop( @Listend );
2a28b791 1219 }
69348ecf 1220 $After_Lpar = 0;
54310121 1221
2a28b791 1222 # clean up item count
69348ecf 1223 pop( @Items_Seen );
54310121 1224}
1225
1226#
2a28b791 1227# process_cut - process a pod cut tag, thus start ignoring pod directives.
54310121 1228#
1229sub process_cut {
69348ecf 1230 $Ignore = 1;
54310121 1231}
1232
1233#
d1be9408 1234# process_pod - process a pod tag, thus stop ignoring pod directives
2a28b791 1235# until we see a corresponding cut.
54310121 1236#
1237sub process_pod {
69348ecf 1238 # no need to set $Ignore to 0 cause the main loop did it
54310121 1239}
1240
1241#
2a28b791 1242# process_for - process a =for pod tag. if it's for html, spit
c4d9b39d 1243# it out verbatim, if illustration, center it, otherwise ignore it.
54310121 1244#
1245sub process_for {
1246 my($whom, $text) = @_;
1247 if ( $whom =~ /^(pod2)?html$/i) {
1248 print HTML $text;
c4d9b39d 1249 } elsif ($whom =~ /^illustration$/i) {
1250 1 while chomp $text;
1251 for my $ext (qw[.png .gif .jpeg .jpg .tga .pcl .bmp]) {
1252 $text .= $ext, last if -r "$text$ext";
1253 }
59ecbafa 1254 print HTML qq{<p align="center"><img src="$text" alt="$text illustration" /></p>};
c4d9b39d 1255 }
54310121 1256}
1257
1258#
1259# process_begin - process a =begin pod tag. this pushes
1260# whom we're beginning on the begin stack. if there's a
1261# begin stack, we only print if it us.
1262#
1263sub process_begin {
1264 my($whom, $text) = @_;
1265 $whom = lc($whom);
69348ecf 1266 push (@Begin_Stack, $whom);
54310121 1267 if ( $whom =~ /^(pod2)?html$/) {
1268 print HTML $text if $text;
1269 }
1270}
1271
1272#
1273# process_end - process a =end pod tag. pop the
1274# begin stack. die if we're mismatched.
1275#
1276sub process_end {
1277 my($whom, $text) = @_;
1278 $whom = lc($whom);
69348ecf 1279 if ($Begin_Stack[-1] ne $whom ) {
1280 die "Unmatched begin/end at chunk $Paragraph\n"
59ecbafa 1281 }
69348ecf 1282 pop( @Begin_Stack );
54310121 1283}
1284
1285#
59ecbafa 1286# process_pre - indented paragraph, made into <pre></pre>
54310121 1287#
2a28b791 1288sub process_pre {
1289 my( $text ) = @_;
1290 my( $rest );
69348ecf 1291 return if $Ignore;
54310121 1292
54310121 1293 $rest = $$text;
1294
2a28b791 1295 # insert spaces in place of tabs
24e08cba 1296 $rest =~ s#(.+)#
1297 my $line = $1;
1298 1 while $line =~ s/(\t+)/' ' x ((length($1) * 8) - $-[0] % 8)/e;
be173d55 1299 $line;
1300 #eg;
54310121 1301
2a28b791 1302 # convert some special chars to HTML escapes
59ecbafa 1303 $rest = html_escape($rest);
2a28b791 1304
1305 # try and create links for all occurrences of perl.* within
1306 # the preformatted text.
1307 $rest =~ s{
1308 (\s*)(perl\w+)
1309 }{
69348ecf 1310 if ( defined $Pages{$2} ){ # is a link
1311 qq($1<a href="$Htmlroot/$Pages{$2}">$2</a>);
1312 } elsif (defined $Pages{dosify($2)}) { # is a link
1313 qq($1<a href="$Htmlroot/$Pages{dosify($2)}">$2</a>);
2a28b791 1314 } else {
1315 "$1$2";
1316 }
1317 }xeg;
1318 $rest =~ s{
59ecbafa 1319 (<a\ href="?) ([^>:]*:)? ([^>:]*) \.pod: ([^>:]*:)?
2a28b791 1320 }{
1321 my $url ;
69348ecf 1322 if ( $Htmlfileurl ne '' ){
59ecbafa 1323 # Here, we take advantage of the knowledge
69348ecf 1324 # that $Htmlfileurl ne '' implies $Htmlroot eq ''.
1325 # Since $Htmlroot eq '', we need to prepend $Htmldir
2a28b791 1326 # on the fron of the link to get the absolute path
1327 # of the link's target. We check for a leading '/'
1328 # to avoid corrupting links that are #, file:, etc.
1329 my $old_url = $3 ;
69348ecf 1330 $old_url = "$Htmldir$old_url" if $old_url =~ m{^\/};
1331 $url = relativize_url( "$old_url.html", $Htmlfileurl );
2a28b791 1332 } else {
1333 $url = "$3.html" ;
1334 }
1335 "$1$url" ;
1336 }xeg;
1337
1338 # Look for embedded URLs and make them into links. We don't
1339 # relativize them since they are best left as the author intended.
1340
1341 my $urls = '(' . join ('|', qw{
54310121 1342 http
1343 telnet
1344 mailto
1345 news
1346 gopher
1347 file
1348 wais
1349 ftp
59ecbafa 1350 } )
54310121 1351 . ')';
59ecbafa 1352
2a28b791 1353 my $ltrs = '\w';
1354 my $gunk = '/#~:.?+=&%@!\-';
add5afb0 1355 my $punc = '.:!?\-;';
2a28b791 1356 my $any = "${ltrs}${gunk}${punc}";
54310121 1357
2a28b791 1358 $rest =~ s{
69e2f044 1359 \b # start at word boundary
1360 ( # begin $1 {
1361 $urls : # need resource and a colon
1362 (?!:) # Ignore File::, among others.
1363 [$any] +? # followed by one or more of any valid
1364 # character, but be conservative and
1365 # take only what you need to....
1366 ) # end $1 }
1367 (?=
1368 &quot; &gt; # maybe pre-quoted '<a href="...">'
1369 | # or:
1370 [$punc]* # 0 or more punctuation
1371 (?: # followed
1372 [^$any] # by a non-url char
1373 | # or
1374 $ # end of the string
1375 ) #
1376 | # or else
1377 $ # then end of the string
54310121 1378 )
59ecbafa 1379 }{<a href="$1">$1</a>}igox;
54310121 1380
2a28b791 1381 # text should be as it is (verbatim)
1382 $$text = $rest;
1383}
54310121 1384
54310121 1385
2a28b791 1386#
1387# pure text processing
1388#
1389# pure_text/inIS_text: differ with respect to automatic C<> recognition.
1390# we don't want this to happen within IS
1391#
1392sub pure_text($){
1393 my $text = shift();
69348ecf 1394 process_puretext( $text, \$PTQuote, 1 );
54310121 1395}
1396
2a28b791 1397sub inIS_text($){
1398 my $text = shift();
69348ecf 1399 process_puretext( $text, \$PTQuote, 0 );
2a28b791 1400}
54310121 1401
1402#
1403# process_puretext - process pure text (without pod-escapes) converting
1404# double-quotes and handling implicit C<> links.
1405#
1406sub process_puretext {
2a28b791 1407 my($text, $quote, $notinIS) = @_;
54310121 1408
2a28b791 1409 ## Guessing at func() or [$@%&]*var references in plain text is destined
1410 ## to produce some strange looking ref's. uncomment to disable:
1411 ## $notinIS = 0;
1412
1413 my(@words, $lead, $trail);
54310121 1414
2a28b791 1415 # convert double-quotes to single-quotes
1416 if( $$quote && $text =~ s/"/''/s ){
1417 $$quote = 0;
1418 }
1419 while ($text =~ s/"([^"]*)"/``$1''/sg) {};
1420 $$quote = 1 if $text =~ s/"/``/s;
54310121 1421
1422 # keep track of leading and trailing white-space
2a28b791 1423 $lead = ($text =~ s/\A(\s+)//s ? $1 : "");
1424 $trail = ($text =~ s/(\s+)\Z//s ? $1 : "");
54310121 1425
2a28b791 1426 # split at space/non-space boundaries
1427 @words = split( /(?<=\s)(?=\S)|(?<=\S)(?=\s)/, $text );
54310121 1428
1429 # process each word individually
1430 foreach my $word (@words) {
2a28b791 1431 # skip space runs
1432 next if $word =~ /^\s*$/;
54310121 1433 # see if we can infer a link
02369fa5 1434 if( $notinIS && $word =~ /^(\w+)\((.*)\)$/ ) {
54310121 1435 # has parenthesis so should have been a C<> ref
2a28b791 1436 ## try for a pagename (perlXXX(1))?
02369fa5 1437 my( $func, $args ) = ( $1, $2 );
1438 if( $args =~ /^\d+$/ ){
2a28b791 1439 my $url = page_sect( $word, '' );
1440 if( defined $url ){
59ecbafa 1441 $word = "<a href=\"$url\">the $word manpage</a>";
2a28b791 1442 next;
1443 }
1444 }
02369fa5 1445 ## try function name for a link, append tt'ed argument list
1446 $word = emit_C( $func, '', "($args)");
2a28b791 1447
1448#### disabled. either all (including $\W, $\w+{.*} etc.) or nothing.
1449## } elsif( $notinIS && $word =~ /^[\$\@%&*]+\w+$/) {
1450## # perl variables, should be a C<> ref
1451## $word = emit_C( $word );
1452
54310121 1453 } elsif ($word =~ m,^\w+://\w,) {
1454 # looks like a URL
5a039dd3 1455 # Don't relativize it: leave it as the author intended
59ecbafa 1456 $word = qq(<a href="$word">$word</a>);
af47ee55 1457 } elsif ($word =~ /[\w.-]+\@[\w-]+\.\w/) {
54310121 1458 # looks like an e-mail address
7b8d334a 1459 my ($w1, $w2, $w3) = ("", $word, "");
1460 ($w1, $w2, $w3) = ("(", $1, ")$2") if $word =~ /^\((.*?)\)(,?)/;
1461 ($w1, $w2, $w3) = ("&lt;", $1, "&gt;$2") if $word =~ /^<(.*?)>(,?)/;
59ecbafa 1462 $word = qq($w1<a href="mailto:$w2">$w2</a>$w3);
1463 } else {
7b8d334a 1464 $word = html_escape($word) if $word =~ /["&<>]/;
54310121 1465 }
1466 }
1467
2a28b791 1468 # put everything back together
1469 return $lead . join( '', @words ) . $trail;
1470}
1471
54310121 1472
2a28b791 1473#
1474# process_text - handles plaintext that appears in the input pod file.
1475# there may be pod commands embedded within the text so those must be
1476# converted to html commands.
1477#
7ba65c74 1478
c68ea5d1 1479sub process_text1($$;$$);
1480sub pattern ($) { $_[0] ? '[^\S\n]+'.('>' x ($_[0] + 1)) : '>' }
1481sub closing ($) { local($_) = shift; (defined && s/\s+$//) ? length : 0 }
7ba65c74 1482
2a28b791 1483sub process_text {
69348ecf 1484 return if $Ignore;
2a28b791 1485 my( $tref ) = @_;
1486 my $res = process_text1( 0, $tref );
1487 $$tref = $res;
1488}
1489
c68ea5d1 1490sub process_text1($$;$$){
1491 my( $lev, $rstr, $func, $closing ) = @_;
2a28b791 1492 my $res = '';
1493
60a48b2d 1494 unless (defined $func) {
1495 $func = '';
1496 $lev++;
1497 }
1498
2a28b791 1499 if( $func eq 'B' ){
1500 # B<text> - boldface
59ecbafa 1501 $res = '<strong>' . process_text1( $lev, $rstr ) . '</strong>';
2a28b791 1502
1503 } elsif( $func eq 'C' ){
59ecbafa 1504 # C<code> - can be a ref or <code></code>
2a28b791 1505 # need to extract text
c68ea5d1 1506 my $par = go_ahead( $rstr, 'C', $closing );
2a28b791 1507
1508 ## clean-up of the link target
1509 my $text = depod( $par );
1510
1511 ### my $x = $par =~ /[BI]</ ? 'yes' : 'no' ;
59ecbafa 1512 ### print STDERR "-->call emit_C($par) lev=$lev, par with BI=$x\n";
2a28b791 1513
1514 $res = emit_C( $text, $lev > 1 || ($par =~ /[BI]</) );
1515
1516 } elsif( $func eq 'E' ){
1517 # E<x> - convert to character
be3174d2 1518 $$rstr =~ s/^([^>]*)>//;
1519 my $escape = $1;
1520 $escape =~ s/^(\d+|X[\dA-F]+)$/#$1/i;
1521 $res = "&$escape;";
2a28b791 1522
1523 } elsif( $func eq 'F' ){
1524 # F<filename> - italizice
59ecbafa 1525 $res = '<em>' . process_text1( $lev, $rstr ) . '</em>';
2a28b791 1526
1527 } elsif( $func eq 'I' ){
1528 # I<text> - italizice
59ecbafa 1529 $res = '<em>' . process_text1( $lev, $rstr ) . '</em>';
2a28b791 1530
1531 } elsif( $func eq 'L' ){
1532 # L<link> - link
59ecbafa 1533 ## L<text|cross-ref> => produce text, use cross-ref for linking
2a28b791 1534 ## L<cross-ref> => make text from cross-ref
1535 ## need to extract text
c68ea5d1 1536 my $par = go_ahead( $rstr, 'L', $closing );
2a28b791 1537
1538 # some L<>'s that shouldn't be:
1539 # a) full-blown URL's are emitted as-is
1540 if( $par =~ m{^\w+://}s ){
1541 return make_URL_href( $par );
1542 }
1543 # b) C<...> is stripped and treated as C<>
1544 if( $par =~ /^C<(.*)>$/ ){
1545 my $text = depod( $1 );
1546 return emit_C( $text, $lev > 1 || ($par =~ /[BI]</) );
1547 }
1548
1549 # analyze the contents
1550 $par =~ s/\n/ /g; # undo word-wrapped tags
1551 my $opar = $par;
1552 my $linktext;
1553 if( $par =~ s{^([^|]+)\|}{} ){
1554 $linktext = $1;
1555 }
59ecbafa 1556
2a28b791 1557 # make sure sections start with a /
1558 $par =~ s{^"}{/"};
1559
1560 my( $page, $section, $ident );
1561
1562 # check for link patterns
1563 if( $par =~ m{^([^/]+?)/(?!")(.*?)$} ){ # name/ident
59ecbafa 1564 # we've got a name/ident (no quotes)
2a28b791 1565 ( $page, $ident ) = ( $1, $2 );
1566 ### print STDERR "--> L<$par> to page $page, ident $ident\n";
1567
1568 } elsif( $par =~ m{^(.*?)/"?(.*?)"?$} ){ # [name]/"section"
1569 # even though this should be a "section", we go for ident first
1570 ( $page, $ident ) = ( $1, $2 );
1571 ### print STDERR "--> L<$par> to page $page, section $section\n";
1572
1573 } elsif( $par =~ /\s/ ){ # this must be a section with missing quotes
1574 ( $page, $section ) = ( '', $par );
1575 ### print STDERR "--> L<$par> to void page, section $section\n";
1576
1577 } else {
1578 ( $page, $section ) = ( $par, '' );
1579 ### print STDERR "--> L<$par> to page $par, void section\n";
1580 }
1581
1582 # now, either $section or $ident is defined. the convoluted logic
1583 # below tries to resolve L<> according to what the user specified.
1584 # failing this, we try to find the next best thing...
1585 my( $url, $ltext, $fid );
1586
1587 RESOLVE: {
1588 if( defined $ident ){
1589 ## try to resolve $ident as an item
1590 ( $url, $fid ) = coderef( $page, $ident );
1591 if( $url ){
1592 if( ! defined( $linktext ) ){
1593 $linktext = $ident;
1594 $linktext .= " in " if $ident && $page;
1595 $linktext .= "the $page manpage" if $page;
1596 }
1597 ### print STDERR "got coderef url=$url\n";
1598 last RESOLVE;
1599 }
1600 ## no luck: go for a section (auto-quoting!)
1601 $section = $ident;
1602 }
1603 ## now go for a section
1604 my $htmlsection = htmlify( $section );
1605 $url = page_sect( $page, $htmlsection );
1606 if( $url ){
1607 if( ! defined( $linktext ) ){
1608 $linktext = $section;
1609 $linktext .= " in " if $section && $page;
1610 $linktext .= "the $page manpage" if $page;
1611 }
1612 ### print STDERR "got page/section url=$url\n";
1613 last RESOLVE;
1614 }
59ecbafa 1615 ## no luck: go for an ident
2a28b791 1616 if( $section ){
1617 $ident = $section;
1618 } else {
1619 $ident = $page;
1620 $page = undef();
1621 }
1622 ( $url, $fid ) = coderef( $page, $ident );
1623 if( $url ){
1624 if( ! defined( $linktext ) ){
1625 $linktext = $ident;
1626 $linktext .= " in " if $ident && $page;
1627 $linktext .= "the $page manpage" if $page;
1628 }
1629 ### print STDERR "got section=>coderef url=$url\n";
1630 last RESOLVE;
1631 }
1632
1633 # warning; show some text.
1634 $linktext = $opar unless defined $linktext;
ac094485 1635 warn "$0: $Podfile: cannot resolve L<$opar> in paragraph $Paragraph.\n" unless $Quiet;
2a28b791 1636 }
1637
d1be9408 1638 # now we have a URL or just plain code
2a28b791 1639 $$rstr = $linktext . '>' . $$rstr;
1640 if( defined( $url ) ){
59ecbafa 1641 $res = "<a href=\"$url\">" . process_text1( $lev, $rstr ) . '</a>';
2a28b791 1642 } else {
59ecbafa 1643 $res = '<em>' . process_text1( $lev, $rstr ) . '</em>';
2a28b791 1644 }
1645
1646 } elsif( $func eq 'S' ){
1647 # S<text> - non-breaking spaces
1648 $res = process_text1( $lev, $rstr );
1649 $res =~ s/ /&nbsp;/g;
1650
1651 } elsif( $func eq 'X' ){
1652 # X<> - ignore
1653 $$rstr =~ s/^[^>]*>//;
1654
1655 } elsif( $func eq 'Z' ){
59ecbafa 1656 # Z<> - empty
69348ecf 1657 warn "$0: $Podfile: invalid X<> in paragraph $Paragraph.\n"
ac094485 1658 unless $$rstr =~ s/^>// or $Quiet;
2a28b791 1659
1660 } else {
c68ea5d1 1661 my $term = pattern $closing;
1662 while( $$rstr =~ s/\A(.*?)(([BCEFILSXZ])<(<+[^\S\n]+)?|$term)//s ){
2a28b791 1663 # all others: either recurse into new function or
c68ea5d1 1664 # terminate at closing angle bracket(s)
2a28b791 1665 my $pt = $1;
c68ea5d1 1666 $pt .= $2 if !$3 && $lev == 1;
2a28b791 1667 $res .= $lev == 1 ? pure_text( $pt ) : inIS_text( $pt );
c68ea5d1 1668 return $res if !$3 && $lev > 1;
1669 if( $3 ){
1670 $res .= process_text1( $lev, $rstr, $3, closing $4 );
1671 }
2a28b791 1672 }
1673 if( $lev == 1 ){
1674 $res .= pure_text( $$rstr );
54310121 1675 } else {
ac094485 1676 warn "$0: $Podfile: undelimited $func<> in paragraph $Paragraph.\n" unless $Quiet;
54310121 1677 }
1678 }
2a28b791 1679 return $res;
1680}
54310121 1681
2a28b791 1682#
1683# go_ahead: extract text of an IS (can be nested)
1684#
c68ea5d1 1685sub go_ahead($$$){
1686 my( $rstr, $func, $closing ) = @_;
2a28b791 1687 my $res = '';
c68ea5d1 1688 my @closing = ($closing);
1689 while( $$rstr =~
1690 s/\A(.*?)(([BCEFILSXZ])<(<+[^\S\n]+)?|@{[pattern $closing[0]]})//s ){
2a28b791 1691 $res .= $1;
c68ea5d1 1692 unless( $3 ){
1693 shift @closing;
1694 return $res unless @closing;
2a28b791 1695 } else {
c68ea5d1 1696 unshift @closing, closing $4;
2a28b791 1697 }
1698 $res .= $2;
1699 }
ac094485 1700 warn "$0: $Podfile: undelimited $func<> in paragraph $Paragraph.\n" unless $Quiet;
2a28b791 1701 return $res;
54310121 1702}
1703
1704#
2a28b791 1705# emit_C - output result of C<text>
1706# $text is the depod-ed text
54310121 1707#
02369fa5 1708sub emit_C($;$$){
1709 my( $text, $nocode, $args ) = @_;
60a48b2d 1710 $args = '' unless defined $args;
2a28b791 1711 my $res;
1712 my( $url, $fid ) = coderef( undef(), $text );
1713
1714 # need HTML-safe text
02369fa5 1715 my $linktext = html_escape( "$text$args" );
2a28b791 1716
1717 if( defined( $url ) &&
1718 (!defined( $EmittedItem ) || $EmittedItem ne $fid ) ){
59ecbafa 1719 $res = "<a href=\"$url\"><code>$linktext</code></a>";
2a28b791 1720 } elsif( 0 && $nocode ){
1721 $res = $linktext;
1722 } else {
59ecbafa 1723 $res = "<code>$linktext</code>";
2a28b791 1724 }
1725 return $res;
54310121 1726}
1727
1728#
2a28b791 1729# html_escape: make text safe for HTML
1730#
1731sub html_escape {
1732 my $rest = $_[0];
1733 $rest =~ s/&/&amp;/g;
1734 $rest =~ s/</&lt;/g;
1735 $rest =~ s/>/&gt;/g;
1736 $rest =~ s/"/&quot;/g;
5b25816d 1737 # &apos; is only in XHTML, not HTML4. Be conservative
1738 #$rest =~ s/'/&apos;/g;
2a28b791 1739 return $rest;
59ecbafa 1740}
2a28b791 1741
1742
1743#
39e571d4 1744# dosify - convert filenames to 8.3
1745#
1746sub dosify {
1747 my($str) = @_;
fe4c6be1 1748 return lc($str) if $^O eq 'VMS'; # VMS just needs casing
39e571d4 1749 if ($Is83) {
1750 $str = lc $str;
1751 $str =~ s/(\.\w+)/substr ($1,0,4)/ge;
1752 $str =~ s/(\w+)/substr ($1,0,8)/ge;
1753 }
1754 return $str;
1755}
1756
1757#
d1be9408 1758# page_sect - make a URL from the text of a L<>
54310121 1759#
2a28b791 1760sub page_sect($$) {
1761 my( $page, $section ) = @_;
1762 my( $linktext, $page83, $link); # work strings
1763
1764 # check if we know that this is a section in this page
69348ecf 1765 if (!defined $Pages{$page} && defined $Sections{$page}) {
2a28b791 1766 $section = $page;
1767 $page = "";
1768 ### print STDERR "reset page='', section=$section\n";
54310121 1769 }
1770
39e571d4 1771 $page83=dosify($page);
69348ecf 1772 $page=$page83 if (defined $Pages{$page83});
54310121 1773 if ($page eq "") {
0d396dd4 1774 $link = "#" . anchorify( $section );
350ccacd 1775 } elsif ( $page =~ /::/ ) {
350ccacd 1776 $page =~ s,::,/,g;
29f227c9 1777 # Search page cache for an entry keyed under the html page name,
1778 # then look to see what directory that page might be in. NOTE:
1779 # this will only find one page. A better solution might be to produce
1780 # an intermediate page that is an index to all such pages.
1781 my $page_name = $page ;
fe6f1558 1782 $page_name =~ s,^.*/,,s ;
69348ecf 1783 if ( defined( $Pages{ $page_name } ) &&
1784 $Pages{ $page_name } =~ /([^:]*$page)\.(?:pod|pm):/
29f227c9 1785 ) {
1786 $page = $1 ;
1787 }
1788 else {
1789 # NOTE: This branch assumes that all A::B pages are located in
69348ecf 1790 # $Htmlroot/A/B.html . This is often incorrect, since they are
1791 # often in $Htmlroot/lib/A/B.html or such like. Perhaps we could
1792 # analyze the contents of %Pages and figure out where any
29f227c9 1793 # cousins of A::B are, then assume that. So, if A::B isn't found,
1794 # but A::C is found in lib/A/C.pm, then A::B is assumed to be in
1795 # lib/A/B.pm. This is also limited, but it's an improvement.
1796 # Maybe a hints file so that the links point to the correct places
2a28b791 1797 # nonetheless?
1798
29f227c9 1799 }
69348ecf 1800 $link = "$Htmlroot/$page.html";
0d396dd4 1801 $link .= "#" . anchorify( $section ) if ($section);
69348ecf 1802 } elsif (!defined $Pages{$page}) {
54310121 1803 $link = "";
54310121 1804 } else {
0d396dd4 1805 $section = anchorify( $section ) if $section ne "";
2a28b791 1806 ### print STDERR "...section=$section\n";
54310121 1807
1808 # if there is a directory by the name of the page, then assume that an
1809 # appropriate section will exist in the subdirectory
69348ecf 1810# if ($section ne "" && $Pages{$page} =~ /([^:]*[^(\.pod|\.pm)]):/) {
1811 if ($section ne "" && $Pages{$page} =~ /([^:]*(?<!\.pod)(?<!\.pm)):/) {
1812 $link = "$Htmlroot/$1/$section.html";
2a28b791 1813 ### print STDERR "...link=$link\n";
54310121 1814
1815 # since there is no directory by the name of the page, the section will
1816 # have to exist within a .html of the same name. thus, make sure there
1817 # is a .pod or .pm that might become that .html
1818 } else {
2a28b791 1819 $section = "#$section" if $section;
1820 ### print STDERR "...section=$section\n";
1821
54310121 1822 # check if there is a .pod with the page name
69348ecf 1823 if ($Pages{$page} =~ /([^:]*)\.pod:/) {
1824 $link = "$Htmlroot/$1.html$section";
1825 } elsif ($Pages{$page} =~ /([^:]*)\.pm:/) {
1826 $link = "$Htmlroot/$1.html$section";
54310121 1827 } else {
54310121 1828 $link = "";
54310121 1829 }
1830 }
1831 }
1832
54310121 1833 if ($link) {
69348ecf 1834 # Here, we take advantage of the knowledge that $Htmlfileurl ne ''
1835 # implies $Htmlroot eq ''. This means that the link in question
1836 # needs a prefix of $Htmldir if it begins with '/'. The test for
29f227c9 1837 # the initial '/' is done to avoid '#'-only links, and to allow
1838 # for other kinds of links, like file:, ftp:, etc.
1839 my $url ;
69348ecf 1840 if ( $Htmlfileurl ne '' ) {
1841 $link = "$Htmldir$link" if $link =~ m{^/}s;
1842 $url = relativize_url( $link, $Htmlfileurl );
1843# print( " b: [$link,$Htmlfileurl,$url]\n" );
29f227c9 1844 }
1845 else {
1846 $url = $link ;
1847 }
2a28b791 1848 return $url;
29f227c9 1849
54310121 1850 } else {
2a28b791 1851 return undef();
54310121 1852 }
54310121 1853}
1854
1855#
29f227c9 1856# relativize_url - convert an absolute URL to one relative to a base URL.
1857# Assumes both end in a filename.
1858#
1859sub relativize_url {
1860 my ($dest,$source) = @_ ;
1861
59ecbafa 1862 my ($dest_volume,$dest_directory,$dest_file) =
29f227c9 1863 File::Spec::Unix->splitpath( $dest ) ;
1864 $dest = File::Spec::Unix->catpath( $dest_volume, $dest_directory, '' ) ;
1865
59ecbafa 1866 my ($source_volume,$source_directory,$source_file) =
29f227c9 1867 File::Spec::Unix->splitpath( $source ) ;
1868 $source = File::Spec::Unix->catpath( $source_volume, $source_directory, '' ) ;
1869
1870 my $rel_path = '' ;
1871 if ( $dest ne '' ) {
1872 $rel_path = File::Spec::Unix->abs2rel( $dest, $source ) ;
1873 }
1874
59ecbafa 1875 if ( $rel_path ne '' &&
29f227c9 1876 substr( $rel_path, -1 ) ne '/' &&
59ecbafa 1877 substr( $dest_file, 0, 1 ) ne '#'
29f227c9 1878 ) {
1879 $rel_path .= "/$dest_file" ;
1880 }
1881 else {
1882 $rel_path .= "$dest_file" ;
1883 }
1884
1885 return $rel_path ;
1886}
1887
54310121 1888
1889#
2a28b791 1890# coderef - make URL from the text of a C<>
54310121 1891#
2a28b791 1892sub coderef($$){
1893 my( $page, $item ) = @_;
1894 my( $url );
1895
1896 my $fid = fragment_id( $item );
6e38a594 1897 if( defined( $page ) && $page ne "" ){
2a28b791 1898 # we have been given a $page...
1899 $page =~ s{::}{/}g;
1900
1901 # Do we take it? Item could be a section!
69348ecf 1902 my $base = $Items{$fid} || "";
2a28b791 1903 $base =~ s{[^/]*/}{};
1904 if( $base ne "$page.html" ){
69348ecf 1905 ### print STDERR "coderef( $page, $item ): items{$fid} = $Items{$fid} = $base => discard page!\n";
2a28b791 1906 $page = undef();
1907 }
54310121 1908
2a28b791 1909 } else {
1910 # no page - local items precede cached items
67398a75 1911 if( defined( $fid ) ){
69348ecf 1912 if( exists $Local_Items{$fid} ){
1913 $page = $Local_Items{$fid};
67398a75 1914 } else {
69348ecf 1915 $page = $Items{$fid};
67398a75 1916 }
2a28b791 1917 }
1918 }
54310121 1919
1920 # if there was a pod file that we found earlier with an appropriate
1921 # =item directive, then create a link to that page.
2a28b791 1922 if( defined $page ){
1923 if( $page ){
69348ecf 1924 if( exists $Pages{$page} and $Pages{$page} =~ /([^:.]*)\.[^:]*:/){
2a28b791 1925 $page = $1 . '.html';
29f227c9 1926 }
69348ecf 1927 my $link = "$Htmlroot/$page#item_" . anchorify($fid);
54310121 1928
69348ecf 1929 # Here, we take advantage of the knowledge that $Htmlfileurl
1930 # ne '' implies $Htmlroot eq ''.
1931 if ( $Htmlfileurl ne '' ) {
1932 $link = "$Htmldir$link" ;
1933 $url = relativize_url( $link, $Htmlfileurl ) ;
2a28b791 1934 } else {
1935 $url = $link ;
1936 }
1937 } else {
2beb85b7 1938 $url = "#item_" . anchorify($fid);
2a28b791 1939 }
54310121 1940
2a28b791 1941 confess "url has space: $url" if $url =~ /"[^"]*\s[^"]*"/;
59ecbafa 1942 }
2a28b791 1943 return( $url, $fid );
54310121 1944}
1945
54310121 1946
1947
1948#
29f227c9 1949# Adapted from Nick Ing-Simmons' PodToHtml package.
1950sub relative_url {
1951 my $source_file = shift ;
1952 my $destination_file = shift;
1953
1954 my $source = URI::file->new_abs($source_file);
1955 my $uo = URI::file->new($destination_file,$source)->abs;
1956 return $uo->rel->as_string;
1957}
1958
1959
1960#
54310121 1961# finish_list - finish off any pending HTML lists. this should be called
1962# after the entire pod file has been read and converted.
1963#
1964sub finish_list {
69348ecf 1965 while ($Listlevel > 0) {
59ecbafa 1966 print HTML "</dl>\n";
69348ecf 1967 $Listlevel--;
54310121 1968 }
1969}
1970
1971#
1972# htmlify - converts a pod section specification to a suitable section
2a28b791 1973# specification for HTML. Note that we keep spaces and special characters
1974# except ", ? (Netscape problem) and the hyphen (writer's problem...).
54310121 1975#
1976sub htmlify {
2a28b791 1977 my( $heading) = @_;
1978 $heading =~ s/(\s+)/ /g;
1979 $heading =~ s/\s+\Z//;
1980 $heading =~ s/\A\s+//;
1981 # The hyphen is a disgrace to the English language.
1982 $heading =~ s/[-"?]//g;
1983 $heading = lc( $heading );
1984 return $heading;
1985}
54310121 1986
2a28b791 1987#
776d31fe 1988# similar to htmlify, but turns non-alphanumerics into underscores
0d396dd4 1989#
1990sub anchorify {
1991 my ($anchor) = @_;
1992 $anchor = htmlify($anchor);
776d31fe 1993 $anchor =~ s/\W/_/g;
0d396dd4 1994 return $anchor;
1995}
1996
1997#
2a28b791 1998# depod - convert text by eliminating all interior sequences
1999# Note: can be called with copy or modify semantics
2000#
2001my %E2c;
67398a75 2002$E2c{lt} = '<';
2003$E2c{gt} = '>';
2004$E2c{sol} = '/';
2a28b791 2005$E2c{verbar} = '|';
67398a75 2006$E2c{amp} = '&'; # in Tk's pods
2a28b791 2007
c68ea5d1 2008sub depod1($;$$);
7ba65c74 2009
2a28b791 2010sub depod($){
2011 my $string;
2012 if( ref( $_[0] ) ){
2013 $string = ${$_[0]};
2014 ${$_[0]} = depod1( \$string );
2015 } else {
2016 $string = $_[0];
2017 depod1( \$string );
59ecbafa 2018 }
2a28b791 2019}
54310121 2020
c68ea5d1 2021sub depod1($;$$){
2022 my( $rstr, $func, $closing ) = @_;
2a28b791 2023 my $res = '';
228a48a5 2024 return $res unless defined $$rstr;
2a28b791 2025 if( ! defined( $func ) ){
2026 # skip to next begin of an interior sequence
c68ea5d1 2027 while( $$rstr =~ s/\A(.*?)([BCEFILSXZ])<(<+[^\S\n]+)?// ){
2a28b791 2028 # recurse into its text
c68ea5d1 2029 $res .= $1 . depod1( $rstr, $2, closing $3);
2a28b791 2030 }
2031 $res .= $$rstr;
2032 } elsif( $func eq 'E' ){
2033 # E<x> - convert to character
be3174d2 2034 $$rstr =~ s/^([^>]*)>//;
228a48a5 2035 $res .= $E2c{$1} || "";
2a28b791 2036 } elsif( $func eq 'X' ){
2037 # X<> - ignore
2038 $$rstr =~ s/^[^>]*>//;
2039 } elsif( $func eq 'Z' ){
59ecbafa 2040 # Z<> - empty
2a28b791 2041 $$rstr =~ s/^>//;
2042 } else {
2043 # all others: either recurse into new function or
2044 # terminate at closing angle bracket
c68ea5d1 2045 my $term = pattern $closing;
2046 while( $$rstr =~ s/\A(.*?)(([BCEFILSXZ])<(<+[^\S\n]+)?|$term)// ){
2a28b791 2047 $res .= $1;
c68ea5d1 2048 last unless $3;
2049 $res .= depod1( $rstr, $3, closing $4 );
2a28b791 2050 }
2051 ## If we're here and $2 ne '>': undelimited interior sequence.
2052 ## Ignored, as this is called without proper indication of where we are.
2053 ## Rely on process_text to produce diagnostics.
2054 }
2055 return $res;
2056}
54310121 2057
2a28b791 2058#
2059# fragment_id - construct a fragment identifier from:
2060# a) =item text
2061# b) contents of C<...>
2062#
69348ecf 2063my @HC;
2a28b791 2064sub fragment_id {
2065 my $text = shift();
2066 $text =~ s/\s+\Z//s;
2067 if( $text ){
2068 # a method or function?
2069 return $1 if $text =~ /(\w+)\s*\(/;
2070 return $1 if $text =~ /->\s*(\w+)\s*\(?/;
2071
2072 # a variable name?
2073 return $1 if $text =~ /^([$@%*]\S+)/;
2074
2075 # some pattern matching operator?
2076 return $1 if $text =~ m|^(\w+/).*/\w*$|;
2077
2078 # fancy stuff... like "do { }"
2079 return $1 if $text =~ m|^(\w+)\s*{.*}$|;
2080
2081 # honour the perlfunc manpage: func [PAR[,[ ]PAR]...]
2082 # and some funnies with ... Module ...
ac094485 2083 return $1 if $text =~ m{^([a-z\d_]+)(\s+[A-Z\d,/& ]+)?$};
2a28b791 2084 return $1 if $text =~ m{^([a-z\d]+)\s+Module(\s+[A-Z\d,/& ]+)?$};
2085
2086 # text? normalize!
2087 $text =~ s/\s+/_/sg;
2088 $text =~ s{(\W)}{
69348ecf 2089 defined( $HC[ord($1)] ) ? $HC[ord($1)]
2090 : ( $HC[ord($1)] = sprintf( "%%%02X", ord($1) ) ) }gxe;
2a28b791 2091 $text = substr( $text, 0, 50 );
2092 } else {
2093 return undef();
2094 }
54310121 2095}
2096
2a28b791 2097#
2098# make_URL_href - generate HTML href from URL
2099# Special treatment for CGI queries.
2100#
2101sub make_URL_href($){
2102 my( $url ) = @_;
59ecbafa 2103 if( $url !~
2104 s{^(http:[-\w/#~:.+=&%@!]+)(\?.*)$}{<a href="$1$2">$1</a>}i ){
2105 $url = "<a href=\"$url\">$url</a>";
2a28b791 2106 }
2107 return $url;
54310121 2108}
2109
21101;