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