File Coverage

blib/lib/Pod/Html.pm
Criterion Covered Total %
statement 782 846 92.4
branch 431 550 78.3
condition 108 158 68.3
subroutine 58 60 96.6
pod 6 6 100.0
total 1385 1620 85.4


line stmt bran cond sub pod time code
1             package Pod::Html;
2 3     3   33853 use strict;
  3         5  
  3         158  
3             require Exporter;
4              
5 3     3   16 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  3         5  
  3         422  
6             $VERSION = '1.09_04';
7             @ISA = qw(Exporter);
8             @EXPORT = qw(pod2html htmlify);
9             @EXPORT_OK = qw(anchorify);
10              
11 3     3   19 use Carp;
  3         10  
  3         498  
12 3     3   18 use Config;
  3         6  
  3         212  
13 3     3   17 use Cwd;
  3         5  
  3         202  
14 3     3   15 use File::Spec;
  3         6  
  3         84  
15 3     3   15 use File::Spec::Unix;
  3         5  
  3         86  
16 3     3   3889 use Getopt::Long;
  3         47101  
  3         23  
17              
18 3     3   3319 use locale; # make \w work right in non-ASCII lands
  3         745  
  3         17  
19              
20             my($Cachedir);
21             my($Dircache, $Itemcache);
22             my @Begin_Stack;
23             my @Libpods;
24             my($Htmlroot, $Htmldir, $Htmlfile, $Htmlfileurl);
25             my($Podfile, @Podpath, $Podroot);
26             my $Css;
27              
28             my $Recurse;
29             my $Quiet;
30             my $HiddenDirs;
31             my $Verbose;
32             my $Doindex;
33              
34             my $Backlink;
35             my($Listlevel, @Listtype);
36             my $ListNewTerm;
37 3     3   304 use vars qw($Ignore); # need to localize it later.
  3         5  
  3         55632  
38              
39             my(%Items_Named, @Items_Seen);
40             my($Title, $Header);
41              
42             my $Top;
43             my $Paragraph;
44              
45             my %Sections;
46              
47             # Caches
48             my %Pages = (); # associative array used to find the location
49             # of pages referenced by L<> links.
50             my %Items = (); # associative array used to find the location
51             # of =item directives referenced by C<> links
52              
53             my %Local_Items;
54             my $Is83;
55              
56             my $Curdir = File::Spec->curdir;
57              
58             _init_globals();
59              
60             sub _init_globals {
61 22     22   65 $Cachedir = "."; # The directory to which item and directory
62             # caches will be written.
63              
64 22         53 $Dircache = "pod2htmd.tmp";
65 22         51 $Itemcache = "pod2htmi.tmp";
66              
67 22         46 @Begin_Stack = (); # begin/end stack
68              
69 22         50 @Libpods = (); # files to search for links from C<> directives
70 22         49 $Htmlroot = "/"; # http-server base directory from which all
71             # relative paths in $podpath stem.
72 22         43 $Htmldir = ""; # The directory to which the html pages
73             # will (eventually) be written.
74 22         38 $Htmlfile = ""; # write to stdout by default
75 22         52 $Htmlfileurl = ""; # The url that other files would use to
76             # refer to this file. This is only used
77             # to make relative urls that point to
78             # other files.
79              
80 22         45 $Podfile = ""; # read from stdin by default
81 22         51 @Podpath = (); # list of directories containing library pods.
82 22         43 $Podroot = $Curdir; # filesystem base directory from which all
83             # relative paths in $podpath stem.
84 22         51 $Css = ''; # Cascading style sheet
85 22         49 $Recurse = 1; # recurse on subdirectories in $podpath.
86 22         28 $Quiet = 0; # not quiet by default
87 22         33 $Verbose = 0; # not verbose by default
88 22         39 $Doindex = 1; # non-zero if we should generate an index
89 22         36 $Backlink = ''; # text for "back to top" links
90 22         27 $Listlevel = 0; # current list depth
91 22         41 @Listtype = (); # list types for open lists
92 22         40 $ListNewTerm = 0; # indicates new term in definition list; used
93             # to correctly open/close
tags
94 22         42 $Ignore = 1; # whether or not to format text. we don't
95             # format text until we hit our first pod
96             # directive.
97              
98 22         43 @Items_Seen = (); # for multiples of the same item in perlfunc
99 22         64 %Items_Named = ();
100 22         45 $Header = 0; # produce block header/footer
101 22         44 $Title = ''; # title to give the pod(s)
102 22         40 $Top = 1; # true if we are at the top of the doc. used
103             # to prevent the first
directive.
104 22         44 $Paragraph = ''; # which paragraph we're processing (used
105             # for error messages)
106 22         66 %Sections = (); # sections within this page
107              
108 22         70 %Local_Items = ();
109 22         160 $Is83 = $^O eq 'dos'; # Is it an 8.3 filesystem?
110             }
111              
112             #
113             # _clean_data: global clean-up of pod data
114             #
115             sub _clean_data($){
116 21     21   72 _flush_seen();
117 21         82 my( $dataref ) = @_;
118 21         35 for my $i ( 0..$#{$dataref} ) {
  21         86  
119 529         541 ${$dataref}[$i] =~ s/\s+\Z//;
  529         2057  
120              
121             # have a look for all-space lines
122 529 50 33     569 if( ${$dataref}[$i] =~ /^\s+$/m and $dataref->[$i] !~ /^\s/ ){
  529         1961  
123 0         0 my @chunks = split( /^\s+$/m, ${$dataref}[$i] );
  0         0  
124 0         0 splice( @$dataref, $i, 1, @chunks );
125             }
126             }
127             }
128              
129              
130             sub pod2html {
131 19     19 1 88 local(@ARGV) = @_;
132 19         65 local($/);
133 19         25 local $_;
134              
135 19         77 _init_globals();
136              
137 19 50 33     78 $Is83 = 0 if (defined (&Dos::UseLFN) && Dos::UseLFN());
138              
139             # cache of %Pages and %Items from last time we ran pod2html
140              
141             #undef $opt_help if defined $opt_help;
142              
143             # parse the command-line parameters
144 19         59 _parse_command_line();
145              
146             # escape the backlink argument (same goes for title but is done later...)
147 19 50       89 $Backlink = _html_escape($Backlink) if defined $Backlink;
148              
149             # set some variables to their default values if necessary
150 19         63 local *POD;
151 19 50 33     104 unless (@ARGV && $ARGV[0]) {
152 19 50       56 $Podfile = "-" unless $Podfile; # stdin
153 19 50       850 open(POD, "<$Podfile")
154             || die "$0: cannot open $Podfile file for input: $!\n";
155             } else {
156 0         0 $Podfile = $ARGV[0]; # XXX: might be more filenames
157 0         0 *POD = *ARGV;
158             }
159 19 50       77 $Htmlfile = "-" unless $Htmlfile; # stdout
160 19 100       53 $Htmlroot = "" if $Htmlroot eq "/"; # so we don't get a //
161 19         38 $Htmldir =~ s#/\z## ; # so we don't get a //
162 19 100 66     311 if ( $Htmlroot eq ''
      100        
      100        
163             && defined( $Htmldir )
164             && $Htmldir ne ''
165             && substr( $Htmlfile, 0, length( $Htmldir ) ) eq $Htmldir
166             )
167             {
168             # Set the 'base' url for this file, so that we can use it
169             # as the location from which to calculate relative links
170             # to other files. If this is '', then absolute links will
171             # be used throughout.
172 1         4 $Htmlfileurl= "$Htmldir/" . substr( $Htmlfile, length( $Htmldir ) + 1);
173             }
174              
175             # read the pod a paragraph at a time
176 19 50       46 warn "Scanning for sections in input file(s)\n" if $Verbose;
177 19         59 $/ = "";
178 19         965 my @poddata = ;
179 19         279 close(POD);
180              
181             # be eol agnostic
182 19         52 for (@poddata) {
183 499 100       1044 if (/\r/) {
184 1 50       9 if (/\r\n/) {
185 1         2 @poddata = map { s/\r\n/\n/g;
  1         8  
186 5         15 /\n\n/ ?
187 1 50       9 map { "$_\n\n" } split /\n\n/ :
188             $_ } @poddata;
189             } else {
190 0         0 @poddata = map { s/\r/\n/g;
  0         0  
191 0         0 /\n\n/ ?
192 0 0       0 map { "$_\n\n" } split /\n\n/ :
193             $_ } @poddata;
194             }
195 1         4 last;
196             }
197             }
198              
199 19         90 _clean_data( \@poddata );
200              
201             # scan the pod for =head[1-6] directives and build an index
202 19         99 my $index = _scan_headings(\%Sections, @poddata);
203              
204 19 100       58 unless($index) {
205 2 50       8 warn "No headings in $Podfile\n" if $Verbose;
206             }
207              
208             # open the output file
209 19 50       1982 open(HTML, ">$Htmlfile")
210             || die "$0: cannot open $Htmlfile file for output: $!\n";
211              
212             # put a title in the HTML file if one wasn't specified
213 19 100       77 if ($Title eq '') {
214             TITLE_SEARCH: {
215 18         20 for (my $i = 0; $i < @poddata; $i++) {
  18         78  
216 224 100       698 if ($poddata[$i] =~ /^=head1\s*NAME\b/m) {
217 15         58 for my $para ( @poddata[$i, $i+1] ) {
218             last TITLE_SEARCH
219 30 100       190 if ($Title) = $para =~ /(\S+\s+-+.*\S)/s;
220             }
221             }
222              
223             }
224             }
225             }
226 19 100 66     114 if (!$Title and $Podfile =~ /\.pod\z/) {
227             # probably a split pod so take first =head[12] as title
228 6         22 for (my $i = 0; $i < @poddata; $i++) {
229 72 100       220 last if ($Title) = $poddata[$i] =~ /^=head[12]\s*(.*)/;
230             }
231 6 50 33     28 warn "adopted '$Title' as title for $Podfile\n"
232             if $Verbose and $Title;
233             }
234 19 100       49 if ($Title) {
235 16         42 $Title =~ s/\s*\(.*\)//;
236             } else {
237 3 50       19 warn "$0: no title for $Podfile.\n" unless $Quiet;
238 3         18 $Podfile =~ /^(.*)(\.[^.\/]+)?\z/s;
239 3 50       14 $Title = ($Podfile eq "-" ? 'No Title' : $1);
240 3 50       9 warn "using $Title" if $Verbose;
241             }
242 19         49 $Title = _html_escape($Title);
243              
244 19         36 my $csslink = '';
245 19         34 my $bodystyle = ' style="background-color: white"';
246 19         41 my $tdstyle = ' style="background-color: #cccccc"';
247              
248 19 100       54 if ($Css) {
249 1         5 $csslink = qq(\n);
250 1         3 $csslink =~ s,\\,/,g;
251 1         4 $csslink =~ s,(/.):,$1|,;
252 1         2 $bodystyle = '';
253 1         2 $tdstyle = '';
254             }
255              
256 19 100       64 my $block = $Header ? <
257            
258            
259              $Title
260            
261            
262             END_OF_BLOCK
263              
264 19         430 print HTML <
265            
266            
267            
268            
269             $Title$csslink
270            
271            
272            
273              
274            
275             $block
276             END_OF_HEAD
277              
278             # load/reload/validate/cache %Pages and %Items
279 19         337 _get_cache($Dircache, $Itemcache, \@Podpath, $Podroot, $Recurse);
280              
281             # scan the pod for =item directives
282 19         104 _scan_items( \%Local_Items, "", @poddata);
283              
284             # put an index at the top of the file. note, if $Doindex is 0 we
285             # still generate an index, but surround it with an html comment.
286             # that way some other program can extract it if desired.
287 19         53 $index =~ s/--+/-/g;
288              
289 19 100 100     138 my $hr = ($Doindex and $index) ? qq(
) : "";
290              
291 19 100       59 unless ($Doindex)
292             {
293 2         9 $index = qq(\n);
294             }
295              
296 19         84 print HTML << "END_OF_INDEX";
297              
298            
299            
300            

301             $index
302             $hr
303            
304            
305              
306             END_OF_INDEX
307              
308             # now convert this file
309 19         23 my $after_item; # set to true after an =item
310 19 50       60 warn "Converting input file $Podfile\n" if $Verbose;
311 19         51 foreach my $i (0..$#poddata){
312 503         741 $_ = $poddata[$i];
313 503         566 $Paragraph = $i+1;
314 503 100       1328 if (/^(=.*)/s) { # is it a pod directive?
315 259         319 $Ignore = 0;
316 259         281 $after_item = 0;
317 259         503 $_ = $1;
318 259 100       1217 if (/^=begin\s+(\S+)\s*(.*)/si) {# =begin
    100          
    100          
    100          
319 10         30 _process_begin($1, $2);
320             } elsif (/^=end\s+(\S+)\s*(.*)/si) {# =end
321 9         31 _process_end($1, $2);
322             } elsif (/^=cut/) { # =cut
323 14         41 _process_cut();
324             } elsif (/^=pod/) { # =pod
325 5         18 _process_pod();
326             } else {
327 221 50 33     541 next if @Begin_Stack && $Begin_Stack[-1] ne 'html';
328              
329 221 100       1029 if (/^=(head[1-6])\s+(.*\S)/s) { # =head[1-6] heading
    100          
    100          
    100          
    100          
330 59   66     279 _process_head( $1, $2, $Doindex && $index );
331             } elsif (/^=item\s*(.*\S)?/sm) { # =item text
332 97         186 _process_item( $1 );
333 97         122 $after_item = 1;
334             } elsif (/^=over\s*(.*)/) { # =over N
335 27         62 _process_over();
336             } elsif (/^=back/) { # =back
337 28         69 _process_back();
338             } elsif (/^=for\s+(\S+)\s*(.*)/si) {# =for
339 8         22 _process_for($1,$2);
340             } else {
341 2         7 /^=(\S*)\s*/;
342 2 50       8 warn "$0: $Podfile: unknown pod directive '$1' in "
343             . "paragraph $Paragraph. ignoring.\n" unless $Quiet;
344             }
345             }
346 259         466 $Top = 0;
347             }
348             else {
349 244 100       448 next if $Ignore;
350 242 100 100     565 next if @Begin_Stack && $Begin_Stack[-1] ne 'html';
351 241 100 50     573 print HTML and next if @Begin_Stack && $Begin_Stack[-1] eq 'html';
      66        
352 229         304 my $text = $_;
353              
354             # Open tag for definition list as we have something to put in it
355 229 100       384 if( $ListNewTerm ){
356 67         85 print HTML "
\n";
357 67         77 $ListNewTerm = 0;
358             }
359              
360 229 100       562 if( $text =~ /\A\s+/ ){
361 15         44 _process_pre( \$text );
362 15         38 print HTML "
\n$text
\n";
363              
364             } else {
365 214         374 _process_text( \$text );
366              
367             # experimental: check for a paragraph where all lines
368             # have some ...\t...\t...\n pattern
369 214 100       545 if( $text =~ /\t/ ){
370 4         46 my @lines = split( "\n", $text );
371 4 100       12 if( @lines > 1 ){
372 3         4 my $all = 2;
373 3         4 foreach my $line ( @lines ){
374 10 100 66     56 if( $line =~ /\S/ && $line !~ /\t/ ){
375 3         5 $all--;
376 3 100       9 last if $all == 0;
377             }
378             }
379 3 100       8 if( $all > 0 ){
380 2         28 $text =~ s/\t+//g;
381 2         17 $text =~ s/^/
/gm;
382 2         8 $text = '' .
383             $text . '
';
384             }
385             }
386             }
387             ## end of experimental
388              
389 214         508 print HTML "

$text

\n";
390             }
391 229         386 $after_item = 0;
392             }
393             }
394              
395             # finish off any pending directives
396 19         53 _finish_list();
397              
398             # link to page index
399 19 100 100     158 print HTML "

$Backlink

\n"
      100        
400             if $Doindex and $index and $Backlink;
401              
402 19         52 print HTML <
403             $block
404            
405              
406            
407             END_OF_TAIL
408              
409             # close the html file
410 19         1758 close(HTML);
411              
412 19 50       350 warn "Finished\n" if $Verbose;
413             }
414              
415             ##############################################################################
416              
417             sub usage {
418 0     0 1 0 my $podfile = shift;
419 0 0       0 warn "$0: $podfile: @_\n" if @_;
420 0         0 die <
421             Usage: $0 --help --htmlroot= --infile= --outfile=
422             --podpath=:...: --podroot=
423             --libpods=:...: --recurse --verbose --index
424             --netscape --norecurse --noindex --cachedir=
425              
426             --backlink - set text for "back to top" links (default: none).
427             --cachedir - directory for the item and directory cache files.
428             --css - stylesheet URL
429             --flush - flushes the item and directory caches.
430             --[no]header - produce block header/footer (default is no headers).
431             --help - prints this message.
432             --hiddendirs - search hidden directories in podpath
433             --htmldir - directory for resulting HTML files.
434             --htmlroot - http-server base directory from which all relative paths
435             in podpath stem (default is /).
436             --[no]index - generate an index at the top of the resulting html
437             (default behaviour).
438             --infile - filename for the pod to convert (input taken from stdin
439             by default).
440             --libpods - colon-separated list of pages to search for =item pod
441             directives in as targets of C<> and implicit links (empty
442             by default). note, these are not filenames, but rather
443             page names like those that appear in L<> links.
444             --outfile - filename for the resulting html file (output sent to
445             stdout by default).
446             --podpath - colon-separated list of directories containing library
447             pods (empty by default).
448             --podroot - filesystem base directory from which all relative paths
449             in podpath stem (default is .).
450             --[no]quiet - suppress some benign warning messages (default is off).
451             --[no]recurse - recurse on those subdirectories listed in podpath
452             (default behaviour).
453             --title - title that will appear in resulting html file.
454             --[no]verbose - self-explanatory (off by default).
455             --[no]netscape - deprecated, has no effect. for backwards compatibility only.
456              
457             END_OF_USAGE
458              
459             }
460              
461             sub _parse_command_line {
462 19     19   41 my ($opt_backlink,$opt_cachedir,$opt_css,$opt_flush,$opt_header,$opt_help,
463             $opt_htmldir,$opt_htmlroot,$opt_index,$opt_infile,$opt_libpods,
464             $opt_netscape,$opt_outfile,$opt_podpath,$opt_podroot,$opt_quiet,
465             $opt_recurse,$opt_title,$opt_verbose,$opt_hiddendirs);
466              
467 19 50       4015 unshift @ARGV, split ' ', $Config{pod2html} if $Config{pod2html};
468 19         4859 my $result = GetOptions(
469             'backlink=s' => \$opt_backlink,
470             'cachedir=s' => \$opt_cachedir,
471             'css=s' => \$opt_css,
472             'flush' => \$opt_flush,
473             'header!' => \$opt_header,
474             'help' => \$opt_help,
475             'hiddendirs!'=> \$opt_hiddendirs,
476             'htmldir=s' => \$opt_htmldir,
477             'htmlroot=s' => \$opt_htmlroot,
478             'index!' => \$opt_index,
479             'infile=s' => \$opt_infile,
480             'libpods=s' => \$opt_libpods,
481             'netscape!' => \$opt_netscape,
482             'outfile=s' => \$opt_outfile,
483             'podpath=s' => \$opt_podpath,
484             'podroot=s' => \$opt_podroot,
485             'quiet!' => \$opt_quiet,
486             'recurse!' => \$opt_recurse,
487             'title=s' => \$opt_title,
488             'verbose!' => \$opt_verbose,
489             );
490 19 50       37222 usage("-", "invalid parameters") if not $result;
491              
492 19 50       64 usage("-") if defined $opt_help; # see if the user asked for help
493 19         39 $opt_help = ""; # just to make -w shut-up.
494              
495 19 50       106 @Podpath = split(":", $opt_podpath) if defined $opt_podpath;
496 19 100       69 @Libpods = split(":", $opt_libpods) if defined $opt_libpods;
497              
498 19 100       64 $Backlink = $opt_backlink if defined $opt_backlink;
499 19 100       45 $Cachedir = $opt_cachedir if defined $opt_cachedir;
500 19 100       44 $Css = $opt_css if defined $opt_css;
501 19 100       58 $Header = $opt_header if defined $opt_header;
502 19 100       54 $Htmldir = $opt_htmldir if defined $opt_htmldir;
503 19 100       48 $Htmlroot = $opt_htmlroot if defined $opt_htmlroot;
504 19 100       129 $Doindex = $opt_index if defined $opt_index;
505 19 50       81 $Podfile = $opt_infile if defined $opt_infile;
506 19 50       48 $HiddenDirs = $opt_hiddendirs if defined $opt_hiddendirs;
507 19 50       176 $Htmlfile = $opt_outfile if defined $opt_outfile;
508 19 50       51 $Podroot = $opt_podroot if defined $opt_podroot;
509 19 100       45 $Quiet = $opt_quiet if defined $opt_quiet;
510 19 50       42 $Recurse = $opt_recurse if defined $opt_recurse;
511 19 100       39 $Title = $opt_title if defined $opt_title;
512 19 50       37 $Verbose = $opt_verbose if defined $opt_verbose;
513              
514 19 50 33     49 warn "Flushing item and directory caches\n"
515             if $opt_verbose && defined $opt_flush;
516 19         173 $Dircache = "$Cachedir/pod2htmd.tmp";
517 19         48 $Itemcache = "$Cachedir/pod2htmi.tmp";
518 19 100       76 if (defined $opt_flush) {
519 1         380 1 while unlink($Dircache, $Itemcache);
520             }
521             }
522              
523              
524             my $Saved_Cache_Key;
525              
526             sub _get_cache {
527 19     19   59 my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
528 19         60 my @cache_key_args = @_;
529              
530             # A first-level cache:
531             # Don't bother reading the cache files if they still apply
532             # and haven't changed since we last read them.
533              
534 19         69 my $this_cache_key = _cache_key(@cache_key_args);
535              
536 19 100 100     218 return if $Saved_Cache_Key and $this_cache_key eq $Saved_Cache_Key;
537              
538             # load the cache of %Pages and %Items if possible. $tests will be
539             # non-zero if successful.
540 4         8 my $tests = 0;
541 4 100 66     187 if (-f $dircache && -f $itemcache) {
542 2 50       6 warn "scanning for item cache\n" if $Verbose;
543 2         11 $tests = _load_cache($dircache, $itemcache, $podpath, $podroot);
544             }
545              
546             # if we didn't succeed in loading the cache then we must (re)build
547             # %Pages and %Items.
548 4 100       13 if (!$tests) {
549 2 50       5 warn "scanning directories in pod-path\n" if $Verbose;
550 2         10 _scan_podpath($podroot, $recurse, 0);
551             }
552 4         13 $Saved_Cache_Key = _cache_key(@cache_key_args);
553             }
554              
555             sub _cache_key {
556 23     23   52 my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
557 23         932 return join('!', $dircache, $itemcache, $recurse,
558             @$podpath, $podroot, stat($dircache), stat($itemcache));
559             }
560              
561             #
562             # _load_cache - tries to find if the caches stored in $dircache and $itemcache
563             # are valid caches of %Pages and %Items. if they are valid then it loads
564             # them and returns a non-zero value.
565             #
566             sub _load_cache {
567 2     2   6 my($dircache, $itemcache, $podpath, $podroot) = @_;
568 2         3 my($tests);
569 2         4 local $_;
570              
571 2         5 $tests = 0;
572              
573 2 50       117 open(CACHE, "<$itemcache") ||
574             die "$0: error opening $itemcache for reading: $!\n";
575 2         8 $/ = "\n";
576              
577             # is it the same podpath?
578 2         48 $_ = ;
579 2         6 chomp($_);
580 2 50       10 $tests++ if (join(":", @$podpath) eq $_);
581              
582             # is it the same podroot?
583 2         4 $_ = ;
584 2         5 chomp($_);
585 2 50       7 $tests++ if ($podroot eq $_);
586              
587             # load the cache if its good
588 2 50       6 if ($tests != 2) {
589 0         0 close(CACHE);
590 0         0 return 0;
591             }
592              
593 2 50       6 warn "loading item cache\n" if $Verbose;
594 2         13 while () {
595 4         17 /(.*?) (.*)$/;
596 4         23 $Items{$1} = $2;
597             }
598 2         21 close(CACHE);
599              
600 2 50       7 warn "scanning for directory cache\n" if $Verbose;
601 2 50       72 open(CACHE, "<$dircache") ||
602             die "$0: error opening $dircache for reading: $!\n";
603 2         10 $/ = "\n";
604 2         87 $tests = 0;
605              
606             # is it the same podpath?
607 2         20 $_ = ;
608 2         5 chomp($_);
609 2 50       9 $tests++ if (join(":", @$podpath) eq $_);
610              
611             # is it the same podroot?
612 2         5 $_ = ;
613 2         4 chomp($_);
614 2 50       10 $tests++ if ($podroot eq $_);
615              
616             # load the cache if its good
617 2 50       6 if ($tests != 2) {
618 0         0 close(CACHE);
619 0         0 return 0;
620             }
621              
622 2 50       7 warn "loading directory cache\n" if $Verbose;
623 2         8 while () {
624 62         166 /(.*?) (.*)$/;
625 62         301 $Pages{$1} = $2;
626             }
627              
628 2         21 close(CACHE);
629              
630 2         7 return 1;
631             }
632              
633             #
634             # _scan_podpath - scans the directories specified in @podpath for directories,
635             # .pod files, and .pm files. it also scans the pod files specified in
636             # @Libpods for =item directives.
637             #
638             sub _scan_podpath {
639 2     2   4 my($podroot, $recurse, $append) = @_;
640 2         3 my($pwd, $dir);
641 0         0 my($libpod, $dirname, $pod, @files, @poddata);
642              
643 2 50       5 unless($append) {
644 2         6 %Items = ();
645 2         19 %Pages = ();
646             }
647              
648             # scan each directory listed in @Podpath
649 2         16 $pwd = getcwd();
650 2 50       44 chdir($podroot)
651             || die "$0: error changing to directory $podroot: $!\n";
652 2         7 foreach $dir (@Podpath) {
653 2         8 _scan_dir($dir, $recurse);
654             }
655              
656             # scan the pods listed in @Libpods for =item directives
657 2         6 foreach $libpod (@Libpods) {
658             # if the page isn't defined then we won't know where to find it
659             # on the system.
660 4 100 66     24 next unless defined $Pages{$libpod} && $Pages{$libpod};
661              
662             # if there is a directory then use the .pod and .pm files within it.
663             # NOTE: Only finds the first so-named directory in the tree.
664             # if ($Pages{$libpod} =~ /([^:]*[^(\.pod|\.pm)]):/) {
665 2 100 33     72 if ($Pages{$libpod} =~ /([^:]*(?
    50          
666             # find all the .pod and .pm files within the directory
667 1         3 $dirname = $1;
668 1 50       24 opendir(DIR, $dirname) ||
669             die "$0: error opening directory $dirname: $!\n";
670 1   66     30 @files = grep(/(\.pod|\.pm)\z/ && ! -d $_, readdir(DIR));
671 1         9 closedir(DIR);
672              
673             # scan each .pod and .pm file for =item directives
674 1         2 foreach $pod (@files) {
675 1 50       30 open(POD, "<$dirname/$pod") ||
676             die "$0: error opening $dirname/$pod for input: $!\n";
677 1         54 @poddata = ;
678 1         9 close(POD);
679 1         3 _clean_data( \@poddata );
680              
681 1         5 _scan_items( \%Items, "$dirname/$pod", @poddata);
682             }
683              
684             # use the names of files as =item directives too.
685             ### Don't think this should be done this way - confuses issues.(WL)
686             ### foreach $pod (@files) {
687             ### $pod =~ /^(.*)(\.pod|\.pm)$/;
688             ### $Items{$1} = "$dirname/$1.html" if $1;
689             ### }
690             } elsif ($Pages{$libpod} =~ /([^:]*\.pod):/ ||
691             $Pages{$libpod} =~ /([^:]*\.pm):/) {
692             # scan the .pod or .pm file for =item directives
693 1         4 $pod = $1;
694 1 50       33 open(POD, "<$pod") ||
695             die "$0: error opening $pod for input: $!\n";
696 1         25 @poddata = ;
697 1         10 close(POD);
698 1         6 _clean_data( \@poddata );
699              
700 1         9 _scan_items( \%Items, "$pod", @poddata);
701             } else {
702 0 0       0 warn "$0: shouldn't be here (line ".__LINE__."\n" unless $Quiet;
703             }
704             }
705 2         6 @poddata = (); # clean-up a bit
706              
707 2 50       43 chdir($pwd)
708             || die "$0: error changing to directory $pwd: $!\n";
709              
710             # cache the item list for later use
711 2 50       6 warn "caching items for later use\n" if $Verbose;
712 2 50       165 open(CACHE, ">$Itemcache") ||
713             die "$0: error open $Itemcache for writing: $!\n";
714              
715 2         26 print CACHE join(":", @Podpath) . "\n$podroot\n";
716 2         8 foreach my $key (keys %Items) {
717 4         12 print CACHE "$key $Items{$key}\n";
718             }
719              
720 2         92 close(CACHE);
721              
722             # cache the directory list for later use
723 2 50       9 warn "caching directories for later use\n" if $Verbose;
724 2 50       133 open(CACHE, ">$Dircache") ||
725             die "$0: error open $Dircache for writing: $!\n";
726              
727 2         23 print CACHE join(":", @Podpath) . "\n$podroot\n";
728 2         12 foreach my $key (keys %Pages) {
729 62         90 print CACHE "$key $Pages{$key}\n";
730             }
731              
732 2         66 close(CACHE);
733             }
734              
735             #
736             # _scan_dir - scans the directory specified in $dir for subdirectories, .pod
737             # files, and .pm files. notes those that it finds. this information will
738             # be used later in order to figure out where the pages specified in L<>
739             # links are on the filesystem.
740             #
741             sub _scan_dir {
742 8     8   13 my($dir, $recurse) = @_;
743 8         7 my($t, @subdirs, @pods, $pod, $dirname, @dirs);
744 8         9 local $_;
745              
746 8         12 @subdirs = ();
747 8         10 @pods = ();
748              
749 8 50       163 opendir(DIR, $dir) ||
750             die "$0: error opening directory $dir: $!\n";
751 8         348 while (defined($_ = readdir(DIR))) {
752 116 100 100     3092 if (-d "$dir/$_" && $_ ne "." && $_ ne ".."
    100 100        
    100 33        
    100 66        
    100          
753             && ($HiddenDirs || !/^\./)
754             ) { # directory
755 6 50       26 $Pages{$_} = "" unless defined $Pages{$_};
756 6         18 $Pages{$_} .= "$dir/$_:";
757 6         19 push(@subdirs, $_);
758             } elsif (/\.pod\z/) { # .pod
759 36         92 s/\.pod\z//;
760 36 100       106 $Pages{$_} = "" unless defined $Pages{$_};
761 36         75 $Pages{$_} .= "$dir/$_.pod:";
762 36         128 push(@pods, "$dir/$_.pod");
763             } elsif (/\.html\z/) { # .html
764 38         96 s/\.html\z//;
765 38 100       102 $Pages{$_} = "" unless defined $Pages{$_};
766 38         134 $Pages{$_} .= "$dir/$_.pod:";
767             } elsif (/\.pm\z/) { # .pm
768 2         7 s/\.pm\z//;
769 2 50       10 $Pages{$_} = "" unless defined $Pages{$_};
770 2         8 $Pages{$_} .= "$dir/$_.pm:";
771 2         8 push(@pods, "$dir/$_.pm");
772             } elsif (-T "$dir/$_") { # script(?)
773 18         33 local *F;
774 18 50       450 if (open(F, "$dir/$_")) {
775 18         17 my $line;
776 18         166 while (defined($line = )) {
777 180 100       585 if ($line =~ /^=(?:pod|head1)/) {
778 2 50       12 $Pages{$_} = "" unless defined $Pages{$_};
779 2         7 $Pages{$_} .= "$dir/$_.pod:";
780 2         3 last;
781             }
782             }
783 18         197 close(F);
784             }
785             }
786             }
787 8         76 closedir(DIR);
788              
789             # recurse on the subdirectories if necessary
790 8 50       18 if ($recurse) {
791 8         29 foreach my $subdir (@subdirs) {
792 6         28 _scan_dir("$dir/$subdir", $recurse);
793             }
794             }
795             }
796              
797             #
798             # _scan_headings - scan a pod file for head[1-6] tags, note the tags, and
799             # build an index.
800             #
801             sub _scan_headings {
802 19     19   111 my($sections, @data) = @_;
803 19         38 my($tag, $which_head, $otitle, $listdepth, $index);
804              
805 19         41 local $Ignore = 0;
806              
807 19         28 $listdepth = 0;
808 19         33 $index = "";
809              
810             # scan for =head directives, note their name, and build an index
811             # pointing to each of them.
812 19         46 foreach my $line (@data) {
813 503 100       1317 if ($line =~ /^=(head)([1-6])\s+(.*)/) {
814 59         186 ($tag, $which_head, $otitle) = ($1,$2,$3);
815              
816 59         127 my $title = _depod( $otitle );
817 59         138 my $name = anchorify( $title );
818 59         138 $$sections{$name} = 1;
819 59         139 $title = _process_text( \$otitle );
820              
821 59         194 while ($which_head != $listdepth) {
822 27 100       59 if ($which_head > $listdepth) {
    50          
823 24         73 $index .= "\n" . ("\t" x $listdepth) . "
    \n";
824 24         63 $listdepth++;
825             } elsif ($which_head < $listdepth) {
826 3         4 $listdepth--;
827 3         10 $index .= "\n" . ("\t" x $listdepth) . "\n";
828             }
829             }
830              
831 59         213 $index .= "\n" . ("\t" x $listdepth) . "
  • " .
  • 832             "" .
    833             $title . "";
    834             }
    835             }
    836              
    837             # finish off the lists
    838 19         60 while ($listdepth--) {
    839 21         77 $index .= "\n" . ("\t" x $listdepth) . "\n";
    840             }
    841              
    842             # get rid of bogus lists
    843 19         165 $index =~ s,\t*
      \s*
    \n,,g;
    844              
    845 19         92 return $index;
    846             }
    847              
    848             #
    849             # _scan_items - scans the pod specified by $pod for =item directives. we
    850             # will use this information later on in resolving C<> links.
    851             #
    852             sub _scan_items {
    853 21     21   125 my( $itemref, $pod, @poddata ) = @_;
    854 21         35 my($i, $item);
    855 21         29 local $_;
    856              
    857 21         47 $pod =~ s/\.pod\z//;
    858 21 100       70 $pod .= ".html" if $pod;
    859              
    860 21         74 foreach $i (0..$#poddata) {
    861 529         879 my $txt = _depod( $poddata[$i] );
    862              
    863             # figure out what kind of item it is.
    864             # Build string for referencing this item.
    865 529 100       1963 if ( $txt =~ /\A=item\s+\*\s*(.*)\Z/s ) { # bulleted list
        100          
        100          
    866 16 100       46 next unless $1;
    867 9         17 $item = $1;
    868             } elsif( $txt =~ /\A=item\s+(?>\d+\.?)\s*(.*)\Z/s ) { # numbered list
    869 12         20 $item = $1;
    870             } elsif( $txt =~ /\A=item\s+(.*)\Z/s ) { # definition list
    871 63         118 $item = $1;
    872             } else {
    873 438         718 next;
    874             }
    875 84         156 my $fid = _fragment_id( $item );
    876 84 100       332 $$itemref{$fid} = "$pod" if $fid;
    877             }
    878             }
    879              
    880             #
    881             # _process_head - convert a pod head[1-6] tag and convert it to HTML format.
    882             #
    883             sub _process_head {
    884 59     59   160 my($tag, $heading, $hasindex) = @_;
    885              
    886             # figure out the level of the =head
    887 59         192 $tag =~ /head([1-6])/;
    888 59         90 my $level = $1;
    889              
    890 59         115 _finish_list();
    891              
    892 59         96 print HTML "

    \n";

    893 59 100 100     293 if( $level == 1 && ! $Top ){
    894 33 100 100     151 print HTML "$Backlink\n"
    895             if $hasindex and $Backlink;
    896 33         58 print HTML "

    \n
    \n"
    897             } else {
    898 26         59 print HTML "

    \n";
    899             }
    900              
    901 59         108 my $name = anchorify( _depod( $heading ) );
    902 59         150 my $convert = _process_text( \$heading );
    903 59         245 print HTML "$convert\n";
    904             }
    905              
    906              
    907             #
    908             # _emit_item_tag - print an =item's text
    909             # Note: The global $EmittedItem is used for inhibiting self-references.
    910             #
    911             my $EmittedItem;
    912              
    913             sub _emit_item_tag($$$){
    914 77     77   116 my( $otext, $text, $compact ) = @_;
    915 77         132 my $item = _fragment_id( _depod($text) , -generate);
    916 77 50       212 Carp::confess("Undefined fragment '$text' ("._depod($text).") from _fragment_id() in _emit_item_tag() in $Podfile")
    917             if !defined $item;
    918 77         103 $EmittedItem = $item;
    919             ### print STDERR "_emit_item_tag=$item ($text)\n";
    920              
    921 77         118 print HTML '';
    922 77 50       185 if ($Items_Named{$item}++) {
    923 0         0 print HTML _process_text( \$otext );
    924             } else {
    925 77         94 my $name = $item;
    926 77         139 $name = anchorify($name);
    927 77         231 print HTML qq{}, _process_text( \$otext ), '';
    928             }
    929 77         136 print HTML "";
    930 77         128 undef( $EmittedItem );
    931             }
    932              
    933             sub _new_listitem {
    934 97     97   124 my( $tag ) = @_;
    935             # Open tag for definition list as we have something to put in it
    936 97 50 66     304 if( ($tag ne 'dl') && ($ListNewTerm) ){
    937 0         0 print HTML "
    \n";
    938 0         0 $ListNewTerm = 0;
    939             }
    940              
    941 97 100       293 if( $Items_Seen[$Listlevel]++ == 0 ){
    942             # start of new list
    943 27         51 push( @Listtype, "$tag" );
    944 27         74 print HTML "<$tag>\n";
    945             } else {
    946             # if this is not the first item, close the previous one
    947 70 100       122 if ( $tag eq 'dl' ){
    948 53 100       130 print HTML "\n" unless $ListNewTerm;
    949             } else {
    950 17         30 print HTML "\n";
    951             }
    952             }
    953 97 100       194 my $opentag = $tag eq 'dl' ? 'dt' : 'li';
    954 97         223 print HTML "<$opentag>";
    955             }
    956              
    957             #
    958             # _process_item - convert a pod item tag and convert it to HTML format.
    959             #
    960             sub _process_item {
    961 97     97   169 my( $otext ) = @_;
    962              
    963             # lots of documents start a list without doing an =over. this is
    964             # bad! but, the proper thing to do seems to be to just assume
    965             # they did do an =over. so warn them once and then continue.
    966 97 100       197 if( $Listlevel == 0 ){
    967 1 50       6 warn "$0: $Podfile: unexpected =item directive in paragraph $Paragraph. ignoring.\n" unless $Quiet;
    968 1         2 _process_over();
    969             }
    970              
    971             # remove formatting instructions from the text
    972 97         167 my $text = _depod( $otext );
    973              
    974             # all the list variants:
    975 97 100       371 if( $text =~ /\A\*/ ){ # bullet
        100          
    976 16         35 _new_listitem( 'ul' );
    977 16 100       55 if ($text =~ /\A\*\s+(\S.*)\Z/s ) { # with additional text
    978 9         22 my $tag = $1;
    979 9         30 $otext =~ s/\A\*\s+//;
    980 9         20 _emit_item_tag( $otext, $tag, 1 );
    981 9         14 print HTML "\n";
    982             }
    983              
    984             } elsif( $text =~ /\A\d+/ ){ # numbered list
    985 12         27 _new_listitem( 'ol' );
    986 12 100       53 if ($text =~ /\A(?>\d+\.?)\s*(\S.*)\Z/s ) { # with additional text
    987 9         17 my $tag = $1;
    988 9         33 $otext =~ s/\A\d+\.?\s*//;
    989 9         17 _emit_item_tag( $otext, $tag, 1 );
    990 9         13 print HTML "\n";
    991             }
    992              
    993             } else { # definition list
    994             # _new_listitem takes care of opening the
    tag
    995 69         146 _new_listitem( 'dl' );
    996 69 100       251 if ($text =~ /\A(.+)\Z/s ){ # should have text
    997 59         129 _emit_item_tag( $otext, $text, 1 );
    998             # write the definition term and close
    tag
    999 59         243 print HTML "\n";
    1000             }
    1001             # trigger opening a
    tag for the actual definition; will not
    1002             # happen if next paragraph is also a definition term (=item)
    1003 69         101 $ListNewTerm = 1;
    1004             }
    1005 97         148 print HTML "\n";
    1006             }
    1007              
    1008             #
    1009             # _process_over - process a pod over tag and start a corresponding HTML list.
    1010             #
    1011             sub _process_over {
    1012             # start a new list
    1013 28     28   39 $Listlevel++;
    1014 28         55 push( @Items_Seen, 0 );
    1015             }
    1016              
    1017             #
    1018             # _process_back - process a pod back tag and convert it to HTML format.
    1019             #
    1020             sub _process_back {
    1021 29 100   29   71 if( $Listlevel == 0 ){
    1022 1 50       3 warn "$0: $Podfile: unexpected =back directive in paragraph $Paragraph. ignoring.\n" unless $Quiet;
    1023 1         2 return;
    1024             }
    1025              
    1026             # close off the list. note, I check to see if $Listtype[$Listlevel] is
    1027             # defined because an =item directive may have never appeared and thus
    1028             # $Listtype[$Listlevel] may have never been initialized.
    1029 28         33 $Listlevel--;
    1030 28 100       62 if( defined $Listtype[$Listlevel] ){
    1031 27 100       179 if ( $Listtype[$Listlevel] eq 'dl' ){
    1032 16 100       55 print HTML "\n" unless $ListNewTerm;
    1033             } else {
    1034 11         18 print HTML "\n";
    1035             }
    1036 27         55 print HTML "\n";
    1037 27         28 pop( @Listtype );
    1038 27         52 $ListNewTerm = 0;
    1039             }
    1040              
    1041             # clean up item count
    1042 28         36 pop( @Items_Seen );
    1043             }
    1044              
    1045             #
    1046             # _process_cut - process a pod cut tag, thus start ignoring pod directives.
    1047             #
    1048             sub _process_cut {
    1049 14     14   25 $Ignore = 1;
    1050             }
    1051              
    1052             #
    1053             # _process_pod - process a pod tag, thus stop ignoring pod directives
    1054             # until we see a corresponding cut.
    1055             #
    1056 5     5   7 sub _process_pod {
    1057             # no need to set $Ignore to 0 cause the main loop did it
    1058             }
    1059              
    1060             #
    1061             # _process_for - process a =for pod tag. if it's for html, spit
    1062             # it out verbatim, if illustration, center it, otherwise ignore it.
    1063             #
    1064             sub _process_for {
    1065 8     8   19 my($whom, $text) = @_;
    1066 8 100       37 if ( $whom =~ /^(pod2)?html$/i) {
        100          
    1067 6         14 print HTML $text;
    1068             } elsif ($whom =~ /^illustration$/i) {
    1069 1         6 1 while chomp $text;
    1070 1         40 for my $ext (qw[.png .gif .jpeg .jpg .tga .pcl .bmp]) {
    1071 4 100       62 $text .= $ext, last if -r "$text$ext";
    1072             }
    1073 1         5 print HTML qq{

    $text illustration

    };
    1074             }
    1075             }
    1076              
    1077             #
    1078             # _process_begin - process a =begin pod tag. this pushes
    1079             # whom we're beginning on the begin stack. if there's a
    1080             # begin stack, we only print if it us.
    1081             #
    1082             sub _process_begin {
    1083 10     10   23 my($whom, $text) = @_;
    1084 10         19 $whom = lc($whom);
    1085 10         21 push (@Begin_Stack, $whom);
    1086 10 100       44 if ( $whom =~ /^(pod2)?html$/) {
    1087 8 100       33 print HTML $text if $text;
    1088             }
    1089             }
    1090              
    1091             #
    1092             # _process_end - process a =end pod tag. pop the
    1093             # begin stack. die if we're mismatched.
    1094             #
    1095             sub _process_end {
    1096 9     9   19 my($whom, $text) = @_;
    1097 9         16 $whom = lc($whom);
    1098 9 50 33     49 if (!defined $Begin_Stack[-1] or $Begin_Stack[-1] ne $whom ) {
    1099 0         0 Carp::confess("Unmatched begin/end at chunk $Paragraph in pod $Podfile\n")
    1100             }
    1101 9         13 pop( @Begin_Stack );
    1102             }
    1103              
    1104             #
    1105             # _process_pre - indented paragraph, made into
     
    1106             #
    1107             sub _process_pre {
    1108 15     15   21 my( $text ) = @_;
    1109 15         17 my( $rest );
    1110 15 50       83 return if $Ignore;
    1111              
    1112 15         19 $rest = $$text;
    1113              
    1114             # insert spaces in place of tabs
    1115 15         172 $rest =~ s#(.+)#
    1116 17         206 my $line = $1;
    1117 17         50 1 while $line =~ s/(\t+)/' ' x ((length($1) * 8) - $-[0] % 8)/e;
      9         70  
    1118 17         50 $line;
    1119             #eg;
    1120              
    1121             # convert some special chars to HTML escapes
    1122 15         33 $rest = _html_escape($rest);
    1123              
    1124             # try and create links for all occurrences of perl.* within
    1125             # the preformatted text.
    1126 15         82 $rest =~ s{
    1127             (\s*)(perl\w+)
    1128             }{
    1129 1 50       4 if ( defined $Pages{$2} ){ # is a link
        0          
    1130 1         30 qq($1$2);
    1131             } elsif (defined $Pages{_dosify($2)}) { # is a link
    1132 0         0 qq($1$2);
    1133             } else {
    1134 0         0 "$1$2";
    1135             }
    1136             }xeg;
    1137 15         36 $rest =~ s{
    1138             ( 1139             }{
    1140 1         2 my $url ;
    1141 1 50       5 if ( $Htmlfileurl ne '' ){
    1142             # Here, we take advantage of the knowledge
    1143             # that $Htmlfileurl ne '' implies $Htmlroot eq ''.
    1144             # Since $Htmlroot eq '', we need to prepend $Htmldir
    1145             # on the fron of the link to get the absolute path
    1146             # of the link's target. We check for a leading '/'
    1147             # to avoid corrupting links that are #, file:, etc.
    1148 0         0 my $old_url = $3 ;
    1149 0 0       0 $old_url = "$Htmldir$old_url" if $old_url =~ m{^\/};
    1150 0         0 $url = relativize_url( "$old_url.html", $Htmlfileurl );
    1151             } else {
    1152 1         9 $url = "$3.html" ;
    1153             }
    1154 1         4 "$1$url" ;
    1155             }xeg;
    1156              
    1157             # Look for embedded URLs and make them into links. We don't
    1158             # relativize them since they are best left as the author intended.
    1159              
    1160 15         58 my $urls = '(' . join ('|', qw{
    1161             http
    1162             telnet
    1163             mailto
    1164             news
    1165             gopher
    1166             file
    1167             wais
    1168             ftp
    1169             } )
    1170             . ')';
    1171              
    1172 15         151 my $ltrs = '\w';
    1173 15         16 my $gunk = '/#~:.?+=&%@!\-';
    1174 15         15 my $punc = '.:!?\-;';
    1175 15         23 my $any = "${ltrs}${gunk}${punc}";
    1176              
    1177 15         342 $rest =~ s{
    1178             \b # start at word boundary
    1179             ( # begin $1 {
    1180             $urls : # need resource and a colon
    1181             (?!:) # Ignore File::, among others.
    1182             [$any] +? # followed by one or more of any valid
    1183             # character, but be conservative and
    1184             # take only what you need to....
    1185             ) # end $1 }
    1186             (?=
    1187             " > # maybe pre-quoted ''
    1188             | # or:
    1189             [$punc]* # 0 or more punctuation
    1190             (?: # followed
    1191             [^$any] # by a non-url char
    1192             | # or
    1193             $ # end of the string
    1194             ) #
    1195             | # or else
    1196             $ # then end of the string
    1197             )
    1198             }{$1}igox;
    1199              
    1200             # text should be as it is (verbatim)
    1201 15         37 $$text = $rest;
    1202             }
    1203              
    1204              
    1205             #
    1206             # pure text processing
    1207             #
    1208             # pure_text/inIS_text: differ with respect to automatic C<> recognition.
    1209             # we don't want this to happen within IS
    1210             #
    1211             sub _pure_text($){
    1212 571     571   711 my $text = shift();
    1213 571         857 _process_puretext( $text, 1 );
    1214             }
    1215              
    1216             sub _inIS_text($){
    1217 117     117   5499 my $text = shift();
    1218 117         219 _process_puretext( $text, 0 );
    1219             }
    1220              
    1221             #
    1222             # _process_puretext - process pure text (without pod-escapes) converting
    1223             # double-quotes and handling implicit C<> links.
    1224             #
    1225             sub _process_puretext {
    1226 688     688   991 my($text, $notinIS) = @_;
    1227              
    1228             ## Guessing at func() or [\$\@%&]*var references in plain text is destined
    1229             ## to produce some strange looking ref's. uncomment to disable:
    1230             ## $notinIS = 0;
    1231              
    1232 688         639 my(@words, $lead, $trail);
    1233              
    1234             # keep track of leading and trailing white-space
    1235 688 100       2154 $lead = ($text =~ s/\A(\s+)//s ? $1 : "");
    1236 688 100       2023 $trail = ($text =~ s/(\s+)\Z//s ? $1 : "");
    1237              
    1238             # split at space/non-space boundaries
    1239 688         6767 @words = split( /(?<=\s)(?=\S)|(?<=\S)(?=\s)/, $text );
    1240              
    1241             # process each word individually
    1242 688         1411 foreach my $word (@words) {
    1243             # skip space runs
    1244 2285 100       5854 next if $word =~ /^\s*$/;
    1245             # see if we can infer a link or a function call
    1246             #
    1247             # NOTE: This is a word based search, it won't automatically
    1248             # mark "substr($var, 1, 2)" because the 1st word would be "substr($var"
    1249             # User has to enclose those with proper C<>
    1250              
    1251 1432 100 100     7900 if( $notinIS && $word =~
        100          
        100          
    1252             m/
    1253             ^([a-z_]{2,}) # The function name
    1254             \(
    1255             ([0-9][a-z]* # Manual page(1) or page(1M)
    1256             |[^)]*[\$\@\%][^)]+ # ($foo), (1, @foo), (%hash)
    1257             | # ()
    1258             )
    1259             \)
    1260             ([.,;]?)$ # a possible punctuation follows
    1261             /xi
    1262             ) {
    1263             # has parenthesis so should have been a C<> ref
    1264             ## try for a pagename (perlXXX(1))?
    1265 15   100     87 my( $func, $args, $rest ) = ( $1, $2, $3 || '' );
    1266 15 100       41 if( $args =~ /^\d+$/ ){
    1267 3         5 my $url = _page_sect( $word, '' );
    1268 3 50       9 if( defined $url ){
    1269 0         0 $word = qq(the $word manpage$rest);
    1270 0         0 next;
    1271             }
    1272             }
    1273             ## try function name for a link, append tt'ed argument list
    1274 15         50 $word = _emit_C( $func, '', "($args)") . $rest;
    1275              
    1276             #### disabled. either all (including $\W, $\w+{.*} etc.) or nothing.
    1277             ## } elsif( $notinIS && $word =~ /^[\$\@%&*]+\w+$/) {
    1278             ## # perl variables, should be a C<> ref
    1279             ## $word = _emit_C( $word );
    1280              
    1281             } elsif ($word =~ m,^\w+://\w,) {
    1282             # looks like a URL
    1283             # Don't relativize it: leave it as the author intended
    1284 6         24 $word = qq($word);
    1285             } elsif ($word =~ /[\w.-]+\@[\w-]+\.\w/) {
    1286             # looks like an e-mail address
    1287 6         11 my ($w1, $w2, $w3) = ("", $word, "");
    1288 6 100       22 ($w1, $w2, $w3) = ("(", $1, ")$2") if $word =~ /^\((.*?)\)(,?)/;
    1289 6 100       25 ($w1, $w2, $w3) = ("<", $1, ">$2") if $word =~ /^<(.*?)>(,?)/;
    1290 6         24 $word = qq($w1$w2$w3);
    1291             } else {
    1292 1405 100       4232 $word = _html_escape($word) if $word =~ /["&<>]/;
    1293             }
    1294             }
    1295              
    1296             # put everything back together
    1297 688         2893 return $lead . join( '', @words ) . $trail;
    1298             }
    1299              
    1300              
    1301             #
    1302             # process_text - handles plaintext that appears in the input pod file.
    1303             # there may be pod commands embedded within the text so those must be
    1304             # converted to html commands.
    1305             #
    1306              
    1307             sub _process_text1($$;$$);
    1308 804 100   804   2967 sub _pattern ($) { $_[0] ? '\s+'.('>' x ($_[0] + 1)) : '>' }
    1309 413 100 66 413   854 sub _closing ($) { local($_) = shift; (defined && s/\s+\z//) ? length : 0 }
      413         1709  
    1310              
    1311             sub _process_text {
    1312 409 50   409   759 return if $Ignore;
    1313 409         470 my( $tref ) = @_;
    1314 409         9283 my $res = _process_text1( 0, $tref );
    1315 409         1114 $res =~ s/\s+$//s;
    1316 409         926 $$tref = $res;
    1317             }
    1318              
    1319             sub _process_text_rfc_links {
    1320 409     409   536 my $text = shift;
    1321              
    1322             # For every "RFCnnnn" or "RFC nnn", link it to the authoritative
    1323             # ource. Do not use the /i modifier here. Require "RFC" to be written in
    1324             # in capital letters.
    1325              
    1326 409         546 $text =~ s{
    1327             (?<=[^<>[:alpha:]]) # Make sure this is not an URL already
    1328             (RFC\s*([0-9]{1,5}))(?![0-9]) # max 5 digits
    1329             }
    1330             {$1}gx;
    1331              
    1332 409         837 $text;
    1333             }
    1334              
    1335             sub _process_text1($$;$$){
    1336 666     666   1096 my( $lev, $rstr, $func, $closing ) = @_;
    1337 666         764 my $res = '';
    1338              
    1339 666 100       1272 unless (defined $func) {
    1340 506         509 $func = '';
    1341 506         628 $lev++;
    1342             }
    1343              
    1344 666 100       3326 if( $func eq 'B' ){
        100          
        100          
        100          
        100          
        100          
        100          
        100          
        100          
    1345             # B - boldface
    1346 15         49 $res = '' . _process_text1( $lev, $rstr ) . '';
    1347              
    1348             } elsif( $func eq 'C' ){
    1349             # C - can be a ref or
    1350             # need to extract text
    1351 20         44 my $par = _go_ahead( $rstr, 'C', $closing );
    1352              
    1353             ## clean-up of the link target
    1354 20         41 my $text = _depod( $par );
    1355              
    1356             ### my $x = $par =~ /[BI]
    1357             ### print STDERR "-->call _emit_C($par) lev=$lev, par with BI=$x\n";
    1358              
    1359 20   100     122 $res = _emit_C( $text, $lev > 1 || ($par =~ /[BI]
    1360              
    1361             } elsif( $func eq 'E' ){
    1362             # E - convert to character
    1363 29         118 $$rstr =~ s/^([^>]*)>//;
    1364 29         50 my $escape = $1;
    1365 29         69 $escape =~ s/^(\d+|X[\dA-F]+)$/#$1/i;
    1366 29         58 $res = "&$escape;";
    1367              
    1368             } elsif( $func eq 'F' ){
    1369             # F - italicize
    1370 4         11 $res = '' . _process_text1( $lev, $rstr ) . '';
    1371              
    1372             } elsif( $func eq 'I' ){
    1373             # I - italicize
    1374 16         54 $res = '' . _process_text1( $lev, $rstr ) . '';
    1375              
    1376             } elsif( $func eq 'L' ){
    1377             # L - link
    1378             ## L => produce text, use cross-ref for linking
    1379             ## L => make text from cross-ref
    1380             ## need to extract text
    1381 64         291 my $par = _go_ahead( $rstr, 'L', $closing );
    1382              
    1383             # some L<>'s that shouldn't be:
    1384             # a) full-blown URL's are emitted as-is
    1385 64 100       207 if( $par =~ m{^\w+://}s ){
    1386 2         7 return _make_URL_href( $par );
    1387             }
    1388             # b) C<...> is stripped and treated as C<>
    1389 62 100       156 if( $par =~ /^C<(.*)>$/ ){
    1390 3         8 my $text = _depod( $1 );
    1391 3   66     32 return _emit_C( $text, $lev > 1 || ($par =~ /[BI]
    1392             }
    1393              
    1394             # analyze the contents
    1395 59         99 $par =~ s/\n/ /g; # undo word-wrapped tags
    1396 59         73 my $opar = $par;
    1397 59         58 my $linktext;
    1398 59 100       191 if( $par =~ s{^([^|]+)\|}{} ){
    1399 22         42 $linktext = $1;
    1400             }
    1401              
    1402             # make sure sections start with a /
    1403 59         102 $par =~ s{^"}{/"};
    1404              
    1405 59         72 my( $page, $section, $ident );
    1406              
    1407             # check for link patterns
    1408 59 100       355 if( $par =~ m{^([^/]+?)/(?!")(.*?)$} ){ # name/ident
        100          
        100          
    1409             # we've got a name/ident (no quotes)
    1410 2 50       6 if (length $2) {
    1411 2         5 ( $page, $ident ) = ( $1, $2 );
    1412             } else {
    1413 0         0 ( $page, $section ) = ( $1, $2 );
    1414             }
    1415             ### print STDERR "--> L<$par> to page $page, ident $ident\n";
    1416              
    1417             } elsif( $par =~ m{^(.*?)/"?(.*?)"?$} ){ # [name]/"section"
    1418             # even though this should be a "section", we go for ident first
    1419 36         81 ( $page, $ident ) = ( $1, $2 );
    1420             ### print STDERR "--> L<$par> to page $page, section $section\n";
    1421              
    1422             } elsif( $par =~ /\s/ ){ # this must be a section with missing quotes
    1423 1         2 ( $page, $section ) = ( '', $par );
    1424             ### print STDERR "--> L<$par> to void page, section $section\n";
    1425              
    1426             } else {
    1427 20         42 ( $page, $section ) = ( $par, '' );
    1428             ### print STDERR "--> L<$par> to page $par, void section\n";
    1429             }
    1430              
    1431             # now, either $section or $ident is defined. the convoluted logic
    1432             # below tries to resolve L<> according to what the user specified.
    1433             # failing this, we try to find the next best thing...
    1434 59         67 my( $url, $ltext, $fid );
    1435              
    1436             RESOLVE: {
    1437 59 100       68 if( defined $ident ){
      59         114  
    1438             ## try to resolve $ident as an item
    1439 38         75 ( $url, $fid ) = _coderef( $page, $ident );
    1440 38 100       213 if( $url ){
    1441 18 100       41 if( ! defined( $linktext ) ){
    1442 9         11 $linktext = $ident;
    1443 9 50 33     76 $linktext .= " in " if $ident && $page;
    1444 9 50       19 $linktext .= "the $page manpage" if $page;
    1445             }
    1446             ### print STDERR "got coderef url=$url\n";
    1447 18         158 last RESOLVE;
    1448             }
    1449             ## no luck: go for a section (auto-quoting!)
    1450 20         29 $section = $ident;
    1451             }
    1452             ## now go for a section
    1453 41         82 my $htmlsection = htmlify( $section );
    1454 41         112 $url = _page_sect( $page, $htmlsection );
    1455 41 100       89 if( $url ){
    1456 40 100       80 if( ! defined( $linktext ) ){
    1457 27         42 $linktext = $section;
    1458 27 100 100     81 $linktext .= " in " if $section && $page;
    1459 27 100       87 $linktext .= "the $page manpage" if $page;
    1460             }
    1461             ### print STDERR "got page/section url=$url\n";
    1462 40         72 last RESOLVE;
    1463             }
    1464             ## no luck: go for an ident
    1465 1 50       3 if( $section ){
    1466 0         0 $ident = $section;
    1467             } else {
    1468 1         1 $ident = $page;
    1469 1         2 $page = undef();
    1470             }
    1471 1         3 ( $url, $fid ) = _coderef( $page, $ident );
    1472 1 50       5 if( $url ){
    1473 0 0       0 if( ! defined( $linktext ) ){
    1474 0         0 $linktext = $ident;
    1475 0 0 0     0 $linktext .= " in " if $ident && $page;
    1476 0 0       0 $linktext .= "the $page manpage" if $page;
    1477             }
    1478             ### print STDERR "got section=>coderef url=$url\n";
    1479 0         0 last RESOLVE;
    1480             }
    1481              
    1482             # warning; show some text.
    1483 1 50       3 $linktext = $opar unless defined $linktext;
    1484 1 50       10 warn "$0: $Podfile: cannot resolve L<$opar> in paragraph $Paragraph.\n" unless $Quiet;
    1485             }
    1486              
    1487             # now we have a URL or just plain code
    1488 59         119 $$rstr = $linktext . '>' . $$rstr;
    1489 59 100       98 if( defined( $url ) ){
    1490 58         160 $res = "" . _process_text1( $lev, $rstr ) . '';
    1491             } else {
    1492 1         4 $res = '' . _process_text1( $lev, $rstr ) . '';
    1493             }
    1494              
    1495             } elsif( $func eq 'S' ){
    1496             # S - non-breaking spaces
    1497 3         6 $res = _process_text1( $lev, $rstr );
    1498 3         14 $res =~ s/ / /g;
    1499              
    1500             } elsif( $func eq 'X' ){
    1501             # X<> - ignore
    1502 7 50 66     59 warn "$0: $Podfile: invalid X<> in paragraph $Paragraph.\n"
    1503             unless $$rstr =~ s/^[^>]*>// or $Quiet;
    1504             } elsif( $func eq 'Z' ){
    1505             # Z<> - empty
    1506 2 50 66     25 warn "$0: $Podfile: invalid Z<> in paragraph $Paragraph.\n"
    1507             unless $$rstr =~ s/^>// or $Quiet;
    1508              
    1509             } else {
    1510 506         922 my $term = _pattern $closing;
    1511 506         4610 while( $$rstr =~ s/\A(.*?)(([BCEFILSXZ])<(<+[^\S\n]+)?|$term)//s ){
    1512             # all others: either recurse into new function or
    1513             # terminate at closing angle bracket(s)
    1514 279         530 my $pt = $1;
    1515 279 100 100     891 $pt .= $2 if !$3 && $lev == 1;
    1516 279 100       639 $res .= $lev == 1 ? _pure_text( $pt ) : _inIS_text( $pt );
    1517 279 100 100     1363 return $res if !$3 && $lev > 1;
    1518 182 100       548 if( $3 ){
    1519 160         310 $res .= _process_text1( $lev, $rstr, $3, _closing $4 );
    1520             }
    1521             }
    1522 409 50       728 if( $lev == 1 ){
        0          
    1523 409         727 $res .= _pure_text( $$rstr );
    1524             } elsif( ! $Quiet ) {
    1525 0         0 my $snippet = substr($$rstr,0,60);
    1526 0         0 warn "$0: $Podfile: undelimited $func<> in paragraph $Paragraph: '$snippet'.\n"
    1527            
    1528             }
    1529 409         1061 $res = _process_text_rfc_links($res);
    1530             }
    1531 564         2183 return $res;
    1532             }
    1533              
    1534             #
    1535             # _go_ahead: extract text of an IS (can be nested)
    1536             #
    1537             sub _go_ahead($$$){
    1538 84     84   130 my( $rstr, $func, $closing ) = @_;
    1539 84         92 my $res = '';
    1540 84         133 my @closing = ($closing);
    1541 84         118 while( $$rstr =~
    1542 116         207 s/\A(.*?)(([BCEFILSXZ])<(<+\s+)?|@{[_pattern $closing[0]]})//s ){
    1543 116         317 $res .= $1;
    1544 116 100       204 unless( $3 ){
    1545 100         131 shift @closing;
    1546 100 100       369 return $res unless @closing;
    1547             } else {
    1548 16         31 unshift @closing, _closing $4;
    1549             }
    1550 32         67 $res .= $2;
    1551             }
    1552 0 0       0 unless ($Quiet) {
    1553 0         0 my $snippet = substr($$rstr,0,60);
    1554 0         0 warn "$0: $Podfile: undelimited $func<> in paragraph $Paragraph (_go_ahead): '$snippet'.\n"
    1555             }
    1556 0         0 return $res;
    1557             }
    1558              
    1559             #
    1560             # _emit_C - output result of C
    1561             # $text is the depod-ed text
    1562             #
    1563             sub _emit_C($;$$){
    1564 38     38   63 my( $text, $nocode, $args ) = @_;
    1565 38 100       210 $args = '' unless defined $args;
    1566 38         36 my $res;
    1567 38         87 my( $url, $fid ) = _coderef( undef(), $text );
    1568              
    1569             # need HTML-safe text
    1570 38         98 my $linktext = _html_escape( "$text$args" );
    1571              
    1572 38 100 33     132 if( defined( $url ) &&
          66        
    1573             (!defined( $EmittedItem ) || $EmittedItem ne $fid ) ){
    1574 5         14 $res = "$linktext";
    1575             } elsif( 0 && $nocode ){
    1576             $res = $linktext;
    1577             } else {
    1578 33         68 $res = "$linktext";
    1579             }
    1580 38         123 return $res;
    1581             }
    1582              
    1583             #
    1584             # _html_escape: make text safe for HTML
    1585             #
    1586             sub _html_escape {
    1587 121     121   190 my $rest = $_[0];
    1588 121         191 $rest =~ s/&/&/g;
    1589 121         181 $rest =~ s/
    1590 121         212 $rest =~ s/>/>/g;
    1591 121         194 $rest =~ s/"/"/g;
    1592             # ' is only in XHTML, not HTML4. Be conservative
    1593             #$rest =~ s/'/'/g;
    1594 121         292 return $rest;
    1595             }
    1596              
    1597              
    1598             #
    1599             # dosify - convert filenames to 8.3
    1600             #
    1601             sub _dosify {
    1602 44     44   61 my($str) = @_;
    1603 44 50       191 return lc($str) if $^O eq 'VMS'; # VMS just needs casing
    1604 44 50       88 if ($Is83) {
    1605 0         0 $str = lc $str;
    1606 0         0 $str =~ s/(\.\w+)/substr ($1,0,4)/ge;
      0         0  
    1607 0         0 $str =~ s/(\w+)/substr ($1,0,8)/ge;
      0         0  
    1608             }
    1609 44         92 return $str;
    1610             }
    1611              
    1612             #
    1613             # _page_sect - make a URL from the text of a L<>
    1614             #
    1615             sub _page_sect($$) {
    1616 44     44   67 my( $page, $section ) = @_;
    1617 44         49 my( $linktext, $page83, $link); # work strings
    1618              
    1619             # check if we know that this is a section in this page
    1620 44 50 66     242 if (!defined $Pages{$page} && defined $Sections{$page}) {
    1621 0         0 $section = $page;
    1622 0         0 $page = "";
    1623             ### print STDERR "reset page='', section=$section\n";
    1624             }
    1625              
    1626 44         91 $page83=_dosify($page);
    1627 44 100       105 $page=$page83 if (defined $Pages{$page83});
    1628 44 100       129 if ($page eq "") {
        100          
        100          
    1629 19         33 $link = "#" . anchorify( $section );
    1630             } elsif ( $page =~ /::/ ) {
    1631 14         69 $page =~ s,::,/,g;
    1632             # Search page cache for an entry keyed under the html page name,
    1633             # then look to see what directory that page might be in. NOTE:
    1634             # this will only find one page. A better solution might be to produce
    1635             # an intermediate page that is an index to all such pages.
    1636 14         40 my $page_name = $page ;
    1637 14         65 $page_name =~ s,^.*/,,s ;
    1638 14 50 66     184 if ( defined( $Pages{ $page_name } ) &&
    1639             $Pages{ $page_name } =~ /([^:]*$page)\.(?:pod|pm):/
    1640             ) {
    1641 0         0 $page = $1 ;
    1642             }
    1643             else {
    1644             # NOTE: This branch assumes that all A::B pages are located in
    1645             # $Htmlroot/A/B.html . This is often incorrect, since they are
    1646             # often in $Htmlroot/lib/A/B.html or such like. Perhaps we could
    1647             # analyze the contents of %Pages and figure out where any
    1648             # cousins of A::B are, then assume that. So, if A::B isn't found,
    1649             # but A::C is found in lib/A/C.pm, then A::B is assumed to be in
    1650             # lib/A/B.pm. This is also limited, but it's an improvement.
    1651             # Maybe a hints file so that the links point to the correct places
    1652             # nonetheless?
    1653              
    1654             }
    1655 14         45 $link = "$Htmlroot/$page.html";
    1656 14 100       40 $link .= "#" . anchorify( $section ) if ($section);
    1657             } elsif (!defined $Pages{$page}) {
    1658 4         10 $link = "";
    1659             } else {
    1660 7 100       19 $section = anchorify( $section ) if $section ne "";
    1661             ### print STDERR "...section=$section\n";
    1662              
    1663             # if there is a directory by the name of the page, then assume that an
    1664             # appropriate section will exist in the subdirectory
    1665             # if ($section ne "" && $Pages{$page} =~ /([^:]*[^(\.pod|\.pm)]):/) {
    1666 7 50 66     55 if ($section ne "" && $Pages{$page} =~ /([^:]*(?
    1667 0         0 $link = "$Htmlroot/$1/$section.html";
    1668             ### print STDERR "...link=$link\n";
    1669              
    1670             # since there is no directory by the name of the page, the section will
    1671             # have to exist within a .html of the same name. thus, make sure there
    1672             # is a .pod or .pm that might become that .html
    1673             } else {
    1674 7 100       14 $section = "#$section" if $section;
    1675             ### print STDERR "...section=$section\n";
    1676              
    1677             # check if there is a .pod with the page name.
    1678             # for L, Foo.(pod|pm) is preferred to A/Foo.(pod|pm)
    1679 7 50       41 if ($Pages{$page} =~ /([^:]*)\.(?:pod|pm):/) {
    1680 7         26 $link = "$Htmlroot/$1.html$section";
    1681             } else {
    1682 0         0 $link = "";
    1683             }
    1684             }
    1685             }
    1686              
    1687 44 100       74 if ($link) {
    1688             # Here, we take advantage of the knowledge that $Htmlfileurl ne ''
    1689             # implies $Htmlroot eq ''. This means that the link in question
    1690             # needs a prefix of $Htmldir if it begins with '/'. The test for
    1691             # the initial '/' is done to avoid '#'-only links, and to allow
    1692             # for other kinds of links, like file:, ftp:, etc.
    1693 40         45 my $url ;
    1694 40 100       73 if ( $Htmlfileurl ne '' ) {
    1695 1 50       8 $link = "$Htmldir$link" if $link =~ m{^/}s;
    1696 1         6 $url = relativize_url( $link, $Htmlfileurl );
    1697             # print( " b: [$link,$Htmlfileurl,$url]\n" );
    1698             }
    1699             else {
    1700 39         49 $url = $link ;
    1701             }
    1702 40         98 return $url;
    1703              
    1704             } else {
    1705 4         7 return undef();
    1706             }
    1707             }
    1708              
    1709             sub relativize_url {
    1710 5     5 1 6994 my ($dest,$source) = @_ ;
    1711              
    1712 5         353 my ($dest_volume,$dest_directory,$dest_file) =
    1713             File::Spec::Unix->splitpath( $dest ) ;
    1714 5         65 $dest = File::Spec::Unix->catpath( $dest_volume, $dest_directory, '' ) ;
    1715              
    1716 5         59 my ($source_volume,$source_directory,$source_file) =
    1717             File::Spec::Unix->splitpath( $source ) ;
    1718 5         47 $source = File::Spec::Unix->catpath( $source_volume, $source_directory, '' ) ;
    1719              
    1720 5         17 my $rel_path = '' ;
    1721 5 100       34 if ( $dest ne '' ) {
    1722 4         807 $rel_path = File::Spec::Unix->abs2rel( $dest, $source ) ;
    1723             }
    1724              
    1725 5 100 66     347 if ( $rel_path ne '' &&
          66        
    1726             substr( $rel_path, -1 ) ne '/' &&
    1727             substr( $dest_file, 0, 1 ) ne '#'
    1728             ) {
    1729 4         13 $rel_path .= "/$dest_file" ;
    1730             }
    1731             else {
    1732 1         4 $rel_path .= "$dest_file" ;
    1733             }
    1734              
    1735 5         120 return $rel_path ;
    1736             }
    1737              
    1738              
    1739             #
    1740             # _coderef - make URL from the text of a C<>
    1741             #
    1742             sub _coderef($$){
    1743 77     77   102 my( $page, $item ) = @_;
    1744 77         86 my( $url );
    1745              
    1746 77         161 my $fid = _fragment_id( $item );
    1747            
    1748 77 100 100     276 if( defined( $page ) && $page ne "" ){
    1749             # we have been given a $page...
    1750 2         5 $page =~ s{::}{/}g;
    1751              
    1752 2 50       6 Carp::confess("Undefined fragment '$item' from _fragment_id() in _coderef() in $Podfile")
    1753             if !defined $fid;
    1754             # Do we take it? Item could be a section!
    1755 2   50     15 my $base = $Items{$fid} || "";
    1756 2         3 $base =~ s{[^/]*/}{};
    1757 2 50       7 if( $base ne "$page.html" ){
    1758             ### print STDERR "_coderef( $page, $item ): items{$fid} = $Items{$fid} = $base => discard page!\n";
    1759 2         3 $page = undef();
    1760             }
    1761              
    1762             } else {
    1763             # no page - local items precede cached items
    1764 75 100       154 if( defined( $fid ) ){
    1765 73 100       149 if( exists $Local_Items{$fid} ){
    1766 23         46 $page = $Local_Items{$fid};
    1767             } else {
    1768 50         91 $page = $Items{$fid};
    1769             }
    1770             }
    1771             }
    1772              
    1773             # if there was a pod file that we found earlier with an appropriate
    1774             # =item directive, then create a link to that page.
    1775 77 100       146 if( defined $page ){
    1776 23 50       33 if( $page ){
    1777 0 0 0     0 if( exists $Pages{$page} and $Pages{$page} =~ /([^:.]*)\.[^:]*:/){
    1778 0         0 $page = $1 . '.html';
    1779             }
    1780 0         0 my $link = "$Htmlroot/$page#" . anchorify($fid);
    1781              
    1782             # Here, we take advantage of the knowledge that $Htmlfileurl
    1783             # ne '' implies $Htmlroot eq ''.
    1784 0 0       0 if ( $Htmlfileurl ne '' ) {
    1785 0         0 $link = "$Htmldir$link" ;
    1786 0         0 $url = relativize_url( $link, $Htmlfileurl ) ;
    1787             } else {
    1788 0         0 $url = $link ;
    1789             }
    1790             } else {
    1791 23         51 $url = "#" . anchorify($fid);
    1792             }
    1793              
    1794 23 50       66 confess "url has space: $url" if $url =~ /"[^"]*\s[^"]*"/;
    1795             }
    1796 77         202 return( $url, $fid );
    1797             }
    1798              
    1799              
    1800              
    1801             #
    1802             # Adapted from Nick Ing-Simmons' PodToHtml package.
    1803             sub relative_url {
    1804 0     0 1 0 my $source_file = shift ;
    1805 0         0 my $destination_file = shift;
    1806              
    1807 0         0 my $source = URI::file->new_abs($source_file);
    1808 0         0 my $uo = URI::file->new($destination_file,$source)->abs;
    1809 0         0 return $uo->rel->as_string;
    1810             }
    1811              
    1812              
    1813             #
    1814             # _finish_list - finish off any pending HTML lists. this should be called
    1815             # after the entire pod file has been read and converted.
    1816             #
    1817             sub _finish_list {
    1818 78 100   78   186 if( $Listlevel ){
    1819 1 50       3 warn "$0: $Podfile: unterminated list(s) at =head in paragraph $Paragraph. ignoring.\n" unless $Quiet;
    1820 1         4 while( $Listlevel ){
    1821 1         2 _process_back();
    1822             }
    1823             }
    1824             }
    1825              
    1826             #
    1827             # htmlify - converts a pod section specification to a suitable section
    1828             # specification for HTML. Note that we keep spaces and special characters
    1829             # except ", ? (Netscape problem) and the hyphen (writer's problem...).
    1830             #
    1831             sub htmlify {
    1832 280     280 1 342 my( $heading) = @_;
    1833 280         626 $heading =~ s/(\s+)/ /g;
    1834 280         502 $heading =~ s/\s+\Z//;
    1835 280         448 $heading =~ s/\A\s+//;
    1836             # The hyphen is a disgrace to the English language.
    1837             # $heading =~ s/[-"?]//g;
    1838 280         376 $heading =~ s/["?]//g;
    1839 280         407 $heading = lc( $heading );
    1840 280         571 return $heading;
    1841             }
    1842              
    1843             #
    1844             # similar to htmlify, but turns non-alphanumerics into underscores
    1845             #
    1846             sub anchorify {
    1847 239     239 1 345 my ($anchor) = @_;
    1848 239         404 $anchor = htmlify($anchor);
    1849 239         592 $anchor =~ s/\W/_/g;
    1850 239         449 return $anchor;
    1851             }
    1852              
    1853             #
    1854             # _depod - convert text by eliminating all interior sequences
    1855             # Note: can be called with copy or modify semantics
    1856             #
    1857             my %E2c;
    1858             $E2c{lt} = '<';
    1859             $E2c{gt} = '>';
    1860             $E2c{sol} = '/';
    1861             $E2c{verbar} = '|';
    1862             $E2c{amp} = '&'; # in Tk's pods
    1863              
    1864             sub _depod1($;$$);
    1865              
    1866             sub _depod($){
    1867 845     845   821 my $string;
    1868 845 100       1424 if( ref( $_[0] ) ){
    1869 1         2 $string = ${$_[0]};
      1         7  
    1870 1         11 ${$_[0]} = _depod1( \$string );
      1         7  
    1871             } else {
    1872 844         1064 $string = $_[0];
    1873 844         1422 _depod1( \$string );
    1874             }
    1875             }
    1876              
    1877             sub _depod1($;$$){
    1878 1082     1082   1506 my( $rstr, $func, $closing ) = @_;
    1879 1082         1227 my $res = '';
    1880 1082 100       2141 return $res unless defined $$rstr;
    1881 1072 100       2343 if( ! defined( $func ) ){
        100          
        100          
        100          
    1882             # skip to next begin of an interior sequence
    1883 835         2933 while( $$rstr =~ s/\A(.*?)([BCEFILSXZ])<(<+[^\S\n]+)?//s ){
    1884             # recurse into its text
    1885 198         433 $res .= $1 . _depod1( $rstr, $2, _closing $3);
    1886             }
    1887 835         1366 $res .= $$rstr;
    1888             } elsif( $func eq 'E' ){
    1889             # E - convert to character
    1890 40         151 $$rstr =~ s/^([^>]*)>//;
    1891 40   100     154 $res .= $E2c{$1} || "";
    1892             } elsif( $func eq 'X' ){
    1893             # X<> - ignore
    1894 13         48 $$rstr =~ s/^[^>]*>//;
    1895             } elsif( $func eq 'Z' ){
    1896             # Z<> - empty
    1897 2         8 $$rstr =~ s/^>//;
    1898             } else {
    1899             # all others: either recurse into new function or
    1900             # terminate at closing angle bracket
    1901 182         311 my $term = _pattern $closing;
    1902 182         1788 while( $$rstr =~ s/\A(.*?)(([BCEFILSXZ])<(<+[^\S\n]+)?|$term)//s ){
    1903 221         449 $res .= $1;
    1904 221 100       794 last unless $3;
    1905 39         76 $res .= _depod1( $rstr, $3, _closing $4 );
    1906             }
    1907             ## If we're here and $2 ne '>': undelimited interior sequence.
    1908             ## Ignored, as this is called without proper indication of where we are.
    1909             ## Rely on _process_text to produce diagnostics.
    1910             }
    1911 1072         4077 return $res;
    1912             }
    1913              
    1914             {
    1915             my %seen; # static fragment record hash
    1916              
    1917             sub _flush_seen {
    1918 21     21   65 %seen = ();
    1919             }
    1920              
    1921             sub _fragment_id_readable {
    1922 105     105   147 my $text = shift;
    1923 105         125 my $generate = shift; # optional flag
    1924              
    1925 105         125 my $orig = $text;
    1926              
    1927             # leave the words for the fragment identifier,
    1928             # change everything else to underbars.
    1929 105         344 $text =~ s/[^A-Za-z0-9_]+/_/g; # do not use \W to avoid locale dependency.
    1930 105         156 $text =~ s/_{2,}/_/g;
    1931 105         146 $text =~ s/\A_//;
    1932 105         140 $text =~ s/_\Z//;
    1933              
    1934 105 100       191 unless ($text)
    1935             {
    1936             # Nothing left after removing punctuation, so leave it as is
    1937             # E.g. if option is named: "=item -#"
    1938              
    1939 14         24 $text = $orig;
    1940             }
    1941              
    1942 105 100       179 if ($generate) {
    1943 30 100       61 if ( exists $seen{$text} ) {
    1944             # This already exists, make it unique
    1945 3         5 $seen{$text}++;
    1946 3         7 $text = $text . $seen{$text};
    1947             } else {
    1948 27         63 $seen{$text} = 1; # first time seen this fragment
    1949             }
    1950             }
    1951              
    1952 105         275 $text;
    1953             }}
    1954              
    1955             my @HC;
    1956             sub _fragment_id_obfuscated { # This was the old "_2d_2d__"
    1957 2     2   566 my $text = shift;
    1958 2         4 my $generate = shift; # optional flag
    1959              
    1960             # text? Normalize by obfuscating the fragment id to make it unique
    1961 2         8 $text =~ s/\s+/_/sg;
    1962              
    1963 2         16 $text =~ s{(\W)}{
    1964 6 100       38 defined( $HC[ord($1)] ) ? $HC[ord($1)]
    1965             : ( $HC[ord($1)] = sprintf( "%%%02X", ord($1) ) ) }gxe;
    1966 2         7 $text = substr( $text, 0, 50 );
    1967              
    1968 2         11 $text;
    1969             }
    1970              
    1971             #
    1972             # _fragment_id - construct a fragment identifier from:
    1973             # a) =item text
    1974             # b) contents of C<...>
    1975             #
    1976              
    1977             sub _fragment_id {
    1978 238     238   1657 my $text = shift;
    1979 238         314 my $generate = shift; # optional flag
    1980              
    1981 238         488 $text =~ s/\s+\Z//s;
    1982 238 100       397 if( $text ){
    1983             # a method or function?
    1984 233 100       502 return $1 if $text =~ /(\w+)\s*\(/;
    1985 229 100       475 return $1 if $text =~ /->\s*(\w+)\s*\(?/;
    1986              
    1987             # a variable name?
    1988 215 100       517 return $1 if $text =~ /^([\$\@%*]\S+)/;
    1989              
    1990             # some pattern matching operator?
    1991 200 100       420 return $1 if $text =~ m|^(\w+/).*/\w*$|;
    1992              
    1993             # fancy stuff... like "do { }"
    1994 186 100       479 return $1 if $text =~ m|^(\w+)\s*{.*}$|;
    1995              
    1996             # honour the perlfunc manpage: func [PAR[,[ ]PAR]...]
    1997             # and some funnies with ... Module ...
    1998 172 100       885 return $1 if $text =~ m{^([a-z\d_]+)(\s+[A-Z,/& ][A-Z\d,/& ]*)?$};
    1999 119 100       315 return $1 if $text =~ m{^([a-z\d]+)\s+Module(\s+[A-Z\d,/& ]+)?$};
    2000              
    2001 105         188 return _fragment_id_readable($text, $generate);
    2002             } else {
    2003 5         10 return;
    2004             }
    2005             }
    2006              
    2007             #
    2008             # _make_URL_href - generate HTML href from URL
    2009             # Special treatment for CGI queries.
    2010             #
    2011             sub _make_URL_href($){
    2012 2     2   4 my( $url ) = @_;
    2013 2 100       28 if( $url !~
    2014             s{^(http:[-\w/#~:.+=&%@!]+)(\?.*)$}{$1}i ){
    2015 1         5 $url = "$url";
    2016             }
    2017 2         20 return $url;
    2018             }
    2019              
    2020             1;
    2021             __END__