This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Start to reimplement --header
[perl5.git] / ext / Pod-Html / lib / Pod / Html.pm
1 package Pod::Html;
2 use strict;
3 require Exporter;
4
5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
6 $VERSION = 1.11;
7 @ISA = qw(Exporter);
8 @EXPORT = qw(pod2html htmlify);
9 @EXPORT_OK = qw(anchorify);
10
11 use Carp;
12 use Config;
13 use Cwd;
14 use File::Basename;
15 use File::Spec;
16 use File::Spec::Unix;
17 use Getopt::Long;
18 use Pod::Simple::Search;
19 use Pod::Simple::XHTML::LocalPodLinks;
20
21 use locale;     # make \w work right in non-ASCII lands
22
23 =head1 NAME
24
25 Pod::Html - module to convert pod files to HTML
26
27 =head1 SYNOPSIS
28
29     use Pod::Html;
30     pod2html([options]);
31
32 =head1 DESCRIPTION
33
34 Converts files from pod format (see L<perlpod>) to HTML format.  It
35 can automatically generate indexes and cross-references.
36
37 =head1 FUNCTIONS
38
39 =head2 pod2html
40
41     pod2html("pod2html",
42              "--podpath=lib:ext:pod:vms",
43              "--podroot=/usr/src/perl",
44              "--htmlroot=/perl/nmanual",
45              "--libpods=perlfunc:perlguts:perlvar:perlrun:perlop",
46              "--recurse",
47              "--infile=foo.pod",
48              "--outfile=/perl/nmanual/foo.html");
49
50 pod2html takes the following arguments:
51
52 =over 4
53
54 =item backlink
55
56     --backlink
57
58 Turns every C<head1> heading into a link back to the top of the page.
59 By default, no backlinks are generated.
60
61 =item css
62
63     --css=stylesheet
64
65 Specify the URL of a cascading style sheet.  Also disables all HTML/CSS
66 C<style> attributes that are output by default (to avoid conflicts).
67
68 =item header
69
70     --header
71     --noheader
72
73 Creates header and footer blocks containing the text of the C<NAME>
74 section.  By default, no headers are generated.
75
76 =item help
77
78     --help
79
80 Displays the usage message.
81
82 =item htmldir
83
84     --htmldir=name
85
86 Sets the directory in which the resulting HTML file is placed.  This
87 is used to generate relative links to other files. Not passing this
88 causes all links to be absolute, since this is the value that tells
89 Pod::Html the root of the documentation tree.
90
91 =item htmlroot
92
93     --htmlroot=name
94
95 Sets the base URL for the HTML files.  When cross-references are made,
96 the HTML root is prepended to the URL.
97
98 =item index
99
100     --index
101     --noindex
102
103 Generate an index at the top of the HTML file.  This is the default
104 behaviour.
105
106 =item infile
107
108     --infile=name
109
110 Specify the pod file to convert.  Input is taken from STDIN if no
111 infile is specified.
112
113 =item libpods
114
115     --libpods=name:...:name
116
117 List of page names (eg, "perlfunc") which contain linkable C<=item>s.
118
119 =item outfile
120
121     --outfile=name
122
123 Specify the HTML file to create.  Output goes to STDOUT if no outfile
124 is specified.
125
126 =item podpath
127
128     --podpath=name:...:name
129
130 Specify which subdirectories of the podroot contain pod files whose
131 HTML converted forms can be linked to in cross references.
132
133 =item podroot
134
135     --podroot=name
136
137 Specify the base directory for finding library pods.
138
139 =item quiet
140
141     --quiet
142     --noquiet
143
144 Don't display I<mostly harmless> warning messages.  These messages
145 will be displayed by default.  But this is not the same as C<verbose>
146 mode.
147
148 =item recurse
149
150     --recurse
151     --norecurse
152
153 Recurse into subdirectories specified in podpath (default behaviour).
154
155 =item title
156
157     --title=title
158
159 Specify the title of the resulting HTML file.
160
161 =item verbose
162
163     --verbose
164     --noverbose
165
166 Display progress messages.  By default, they won't be displayed.
167
168 =back
169
170 =head2 htmlify
171
172     htmlify($heading);
173
174 Converts a pod section specification to a suitable section specification
175 for HTML. Note that we keep spaces and special characters except
176 C<", ?> (Netscape problem) and the hyphen (writer's problem...).
177
178 =head2 anchorify
179
180     anchorify(@heading);
181
182 Similar to C<htmlify()>, but turns non-alphanumerics into underscores.  Note
183 that C<anchorify()> is not exported by default.
184
185 =head1 ENVIRONMENT
186
187 Uses C<$Config{pod2html}> to setup default options.
188
189 =head1 AUTHOR
190
191 Tom Christiansen, E<lt>tchrist@perl.comE<gt>.
192
193 =head1 SEE ALSO
194
195 L<perlpod>
196
197 =head1 COPYRIGHT
198
199 This program is distributed under the Artistic License.
200
201 =cut
202
203 # NOTES
204 # old pod::html tried to create links in preformatted text of all occurences
205 # of perl.*
206 # and tries to handle implicit
207 # links in C<> text (see process_puretext), and links to RFC
208
209 # write test cases for pod::html to try and understand all the linkings
210
211 my @Libpods;
212 my($Htmlroot, $Htmldir, $Htmlfile);
213 my($Podfile, @Podpath, $Podroot);
214 my $Css;
215
216 my $Recurse;
217 my $Quiet;
218 my $Verbose;
219 my $Doindex;
220
221 my $Backlink;
222
223 my($Title, $Header);
224
225 my %Pages = ();                 # associative array used to find the location
226                                 #   of pages referenced by L<> links.
227
228 my $Curdir = File::Spec->curdir;
229
230 init_globals();
231
232 sub init_globals {
233     @Libpods = ();              # files to search for links to =item directives
234     $Htmlroot = "/";            # http-server base directory from which all
235                                 #   relative paths in $podpath stem.
236     $Htmldir = "";              # The directory to which the html pages
237                                 # will (eventually) be written.
238     $Htmlfile = "";             # write to stdout by default
239
240     $Podfile = "";              # read from stdin by default
241     @Podpath = ();              # list of directories containing library pods.
242     $Podroot = $Curdir;         # filesystem base directory from which all
243                                 #   relative paths in $podpath stem.
244     $Css = '';                  # Cascading style sheet
245     $Recurse = 1;               # recurse on subdirectories in $podpath.
246     $Quiet = 0;                 # not quiet by default
247     $Verbose = 0;               # not verbose by default
248     $Doindex = 1;               # non-zero if we should generate an index
249     $Backlink = 0;              # no backlinks added by default
250     $Header = 0;                # produce block header/footer
251     $Title = '';                # title to give the pod(s)
252 }
253
254 sub pod2html {
255     local(@ARGV) = @_;
256     local $_;
257
258     init_globals();
259     parse_command_line();
260
261     # Get the full path
262     $Podpath = map { $Podroot.$_ } @Podpath;
263
264     # finds all pod modules/pages in podpath, stores in %Pages
265     # --recurse is implemented in _save_page for now (its inefficient right now)
266     # (maybe subclass ::Search to implement instead)
267     Pod::Simple::Search->new->inc(0)->verbose($Verbose)
268         ->callback(\&_save_page)->survey(@Podpath);
269
270     # set options for the parser
271     my $parser = Pod::Simple::XHTML::LocalPodLinks->new();
272     $parser->pages(\%Pages);
273     $parser->backlink($Backlink);
274     $parser->index($Doindex);
275     $parser->output_string(\my $output); # written to file later
276     $parser->quiet($Quiet);
277     $parser->verbose($Verbose);
278
279      # TODO: implement default title generator in here/::xhtml
280     $Title = html_escape($Title);
281
282     if ($Css) {
283         $csslink = qq(\n<link rel="stylesheet" href="$Css" type="text/css" />);
284         $csslink =~ s,\\,/,g;
285         $csslink =~ s,(/.):,$1|,;
286     }
287
288     # left off here - determine what to do with class="(_)block"
289     # do $parser->html_footer with $block
290
291     my $block = $Header ? <<END_OF_BLOCK : '';
292 <table border="0" width="100%" cellspacing="0" cellpadding="3">
293 <tr><td class="_block" valign="middle">
294 <big><strong><span class="block">&nbsp;$Title</span></strong></big>
295 </td></tr>
296 </table>
297 END_OF_BLOCK
298
299     # need to create own header/footer because of --header
300     $parser->html_header(<<"HTMLHEAD");
301 <?xml version="1.0" ?>
302 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
303 <html xmlns="http://www.w3.org/1999/xhtml">
304 <head>
305 <title>$Title</title>$csslink
306 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
307 <link rev="made" href="mailto:$Config{perladmin}" />
308 </head>
309
310 <body>
311 $block
312 HTMLHEAD
313
314     my $input;
315     unless (@ARGV && $ARGV[0]) {
316         if ($Podfile and $Podfile ne '-') {
317             $input = $Podfile;
318         } else {
319             $input = '-'; # note: make a test case for this
320         }
321     } else {
322         $Podfile = $ARGV[0];
323         $input = *ARGV;
324     }
325
326     warn "Converting input file $Podfile\n" if $Verbose;
327     $parser->parse_file($input);
328
329     # Write output to file
330     $Htmlfile = "-" unless $Htmlfile; # stdout
331     my $fhout;
332     if($Htmlfile and $Htmlfile ne '-') {
333         open $fhout, ">", $Htmlfile
334             or die "$0: cannot open $Htmlfile file for output: $!\n";
335     } else {
336         open $fhout, ">-";
337     }
338     print $fhout $output;
339     close $fhout or die "Failed to close $Htmlfile: $!";
340 }
341
342 ##############################################################################
343
344 sub usage {
345     my $podfile = shift;
346     warn "$0: $podfile: @_\n" if @_;
347     die <<END_OF_USAGE;
348 Usage:  $0 --help --htmlroot=<name> --infile=<name> --outfile=<name>
349            --podpath=<name>:...:<name> --podroot=<name>
350            --libpods=<name>:...:<name> --recurse --verbose --index
351            --norecurse --noindex --backlink
352
353   --backlink     - turn =head1 directives into links pointing to the top of
354                    the page (off by default).
355   --css          - stylesheet URL
356   --[no]header   - produce block header/footer (default is no headers).
357   --help         - prints this message.
358   --htmldir      - directory for resulting HTML files.
359   --htmlroot     - http-server base directory from which all relative paths
360                    in podpath stem (default is /).
361   --[no]index    - generate an index at the top of the resulting html
362                    (default behaviour).
363   --infile       - filename for the pod to convert (input taken from stdin
364                    by default).
365   --libpods      - colon-separated list of pages to search for =item pod
366                    directives in as targets of C<> and implicit links (empty
367                    by default).  note, these are not filenames, but rather
368                    page names like those that appear in L<> links.
369   --outfile      - filename for the resulting html file (output sent to
370                    stdout by default).
371   --podpath      - colon-separated list of directories containing library
372                    pods (empty by default).
373   --podroot      - filesystem base directory from which all relative paths
374                    in podpath stem (default is .).
375   --[no]quiet    - suppress some benign warning messages (default is off).
376   --[no]recurse  - recurse on those subdirectories listed in podpath
377                    (default behaviour).
378   --title        - title that will appear in resulting html file.
379   --[no]verbose  - self-explanatory (off by default).
380
381 END_OF_USAGE
382
383 }
384
385 sub parse_command_line {
386     my ($opt_backlink,$opt_css,$opt_header,$opt_help,
387         $opt_htmldir,$opt_htmlroot,$opt_index,$opt_infile,$opt_libpods,
388         $opt_outfile,$opt_podpath,$opt_podroot,$opt_quiet,
389         $opt_recurse,$opt_title,$opt_verbose);
390
391     unshift @ARGV, split ' ', $Config{pod2html} if $Config{pod2html};
392     my $result = GetOptions(
393                             'backlink!' => \$opt_backlink,
394                             'css=s'      => \$opt_css,
395                             'help'       => \$opt_help,
396                             'header!'    => \$opt_header,
397                             'htmldir=s'  => \$opt_htmldir,
398                             'htmlroot=s' => \$opt_htmlroot,
399                             'index!'     => \$opt_index,
400                             'infile=s'   => \$opt_infile,
401                             'libpods=s'  => \$opt_libpods,
402                             'outfile=s'  => \$opt_outfile,
403                             'podpath=s'  => \$opt_podpath,
404                             'podroot=s'  => \$opt_podroot,
405                             'quiet!'     => \$opt_quiet,
406                             'recurse!'   => \$opt_recurse,
407                             'title=s'    => \$opt_title,
408                             'verbose!'   => \$opt_verbose,
409                            );
410     usage("-", "invalid parameters") if not $result;
411
412     usage("-") if defined $opt_help;    # see if the user asked for help
413     $opt_help = "";                     # just to make -w shut-up.
414
415     @Podpath  = split(":", $opt_podpath) if defined $opt_podpath;
416     @Libpods  = split(":", $opt_libpods) if defined $opt_libpods;
417
418     $Backlink = $opt_backlink if defined $opt_backlink;
419     $Css      = $opt_css      if defined $opt_css;
420     $Header   = $opt_header   if defined $opt_header;
421     $Htmldir  = $opt_htmldir  if defined $opt_htmldir;
422     $Htmlroot = $opt_htmlroot if defined $opt_htmlroot;
423     $Doindex  = $opt_index    if defined $opt_index;
424     $Podfile  = $opt_infile   if defined $opt_infile;
425     $Htmlfile = $opt_outfile  if defined $opt_outfile;
426     $Podroot  = $opt_podroot  if defined $opt_podroot;
427     $Quiet    = $opt_quiet    if defined $opt_quiet;
428     $Recurse  = $opt_recurse  if defined $opt_recurse;
429     $Title    = $opt_title    if defined $opt_title;
430     $Verbose  = $opt_verbose  if defined $opt_verbose;
431 }
432
433 #
434 # html_escape: make text safe for HTML
435 #
436 sub html_escape {
437     my $rest = $_[0];
438     $rest   =~ s/&/&amp;/g;
439     $rest   =~ s/</&lt;/g;
440     $rest   =~ s/>/&gt;/g;
441     $rest   =~ s/"/&quot;/g;
442     # &apos; is only in XHTML, not HTML4.  Be conservative
443     #$rest   =~ s/'/&apos;/g;
444     return $rest;
445 }
446
447 #
448 # htmlify - converts a pod section specification to a suitable section
449 # specification for HTML. Note that we keep spaces and special characters
450 # except ", ? (Netscape problem) and the hyphen (writer's problem...).
451 #
452 sub htmlify {
453     my( $heading) = @_;
454     $heading =~ s/(\s+)/ /g;
455     $heading =~ s/\s+\Z//;
456     $heading =~ s/\A\s+//;
457     # The hyphen is a disgrace to the English language.
458     # $heading =~ s/[-"?]//g;
459     $heading =~ s/["?]//g;
460     $heading = lc( $heading );
461     return $heading;
462 }
463
464 #
465 # similar to htmlify, but turns non-alphanumerics into underscores
466 #
467 sub anchorify {
468     my ($anchor) = @_;
469     $anchor = htmlify($anchor);
470     $anchor =~ s/\W/_/g;
471     return $anchor;
472 }
473
474 sub _save_page {
475     my ($modspec, $modname) = @_;
476
477     unless ($Recurse) {
478 #        discard any pages that are below top level dir
479     }
480
481     my ($file, $dir) = fileparse($modspec, qr/\.[^.]*/); #strip .ext
482     $Pages{$modname} = $dir . $file;
483 }
484
485 1;