This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to podlators-2.04
[perl5.git] / lib / Pod / ParseUtils.pm
1 #############################################################################
2 # Pod/ParseUtils.pm -- helpers for POD parsing and conversion
3 #
4 # Copyright (C) 1999-2000 by Marek Rouchal. All rights reserved.
5 # This file is part of "PodParser". PodParser is free software;
6 # you can redistribute it and/or modify it under the same terms
7 # as Perl itself.
8 #############################################################################
9
10 package Pod::ParseUtils;
11
12 use vars qw($VERSION);
13 $VERSION = 1.33;   ## Current version of this package
14 require  5.005;    ## requires this Perl version or later
15
16 =head1 NAME
17
18 Pod::ParseUtils - helpers for POD parsing and conversion
19
20 =head1 SYNOPSIS
21
22   use Pod::ParseUtils;
23
24   my $list = new Pod::List;
25   my $link = Pod::Hyperlink->new('Pod::Parser');
26
27 =head1 DESCRIPTION
28
29 B<Pod::ParseUtils> contains a few object-oriented helper packages for
30 POD parsing and processing (i.e. in POD formatters and translators).
31
32 =cut
33
34 #-----------------------------------------------------------------------------
35 # Pod::List
36 #
37 # class to hold POD list info (=over, =item, =back)
38 #-----------------------------------------------------------------------------
39
40 package Pod::List;
41
42 use Carp;
43
44 =head2 Pod::List
45
46 B<Pod::List> can be used to hold information about POD lists
47 (written as =over ... =item ... =back) for further processing.
48 The following methods are available:
49
50 =over 4
51
52 =item Pod::List-E<gt>new()
53
54 Create a new list object. Properties may be specified through a hash
55 reference like this:
56
57   my $list = Pod::List->new({ -start => $., -indent => 4 });
58
59 See the individual methods/properties for details.
60
61 =cut
62
63 sub new {
64     my $this = shift;
65     my $class = ref($this) || $this;
66     my %params = @_;
67     my $self = {%params};
68     bless $self, $class;
69     $self->initialize();
70     return $self;
71 }
72
73 sub initialize {
74     my $self = shift;
75     $self->{-file} ||= 'unknown';
76     $self->{-start} ||= 'unknown';
77     $self->{-indent} ||= 4; # perlpod: "should be the default"
78     $self->{_items} = [];
79     $self->{-type} ||= '';
80 }
81
82 =item $list-E<gt>file()
83
84 Without argument, retrieves the file name the list is in. This must
85 have been set before by either specifying B<-file> in the B<new()>
86 method or by calling the B<file()> method with a scalar argument.
87
88 =cut
89
90 # The POD file name the list appears in
91 sub file {
92    return (@_ > 1) ? ($_[0]->{-file} = $_[1]) : $_[0]->{-file};
93 }
94
95 =item $list-E<gt>start()
96
97 Without argument, retrieves the line number where the list started.
98 This must have been set before by either specifying B<-start> in the
99 B<new()> method or by calling the B<start()> method with a scalar
100 argument.
101
102 =cut
103
104 # The line in the file the node appears
105 sub start {
106    return (@_ > 1) ? ($_[0]->{-start} = $_[1]) : $_[0]->{-start};
107 }
108
109 =item $list-E<gt>indent()
110
111 Without argument, retrieves the indent level of the list as specified
112 in C<=over n>. This must have been set before by either specifying
113 B<-indent> in the B<new()> method or by calling the B<indent()> method
114 with a scalar argument.
115
116 =cut
117
118 # indent level
119 sub indent {
120    return (@_ > 1) ? ($_[0]->{-indent} = $_[1]) : $_[0]->{-indent};
121 }
122
123 =item $list-E<gt>type()
124
125 Without argument, retrieves the list type, which can be an arbitrary value,
126 e.g. C<OL>, C<UL>, ... when thinking the HTML way.
127 This must have been set before by either specifying
128 B<-type> in the B<new()> method or by calling the B<type()> method
129 with a scalar argument.
130
131 =cut
132
133 # The type of the list (UL, OL, ...)
134 sub type {
135    return (@_ > 1) ? ($_[0]->{-type} = $_[1]) : $_[0]->{-type};
136 }
137
138 =item $list-E<gt>rx()
139
140 Without argument, retrieves a regular expression for simplifying the 
141 individual item strings once the list type has been determined. Usage:
142 E.g. when converting to HTML, one might strip the leading number in
143 an ordered list as C<E<lt>OLE<gt>> already prints numbers itself.
144 This must have been set before by either specifying
145 B<-rx> in the B<new()> method or by calling the B<rx()> method
146 with a scalar argument.
147
148 =cut
149
150 # The regular expression to simplify the items
151 sub rx {
152    return (@_ > 1) ? ($_[0]->{-rx} = $_[1]) : $_[0]->{-rx};
153 }
154
155 =item $list-E<gt>item()
156
157 Without argument, retrieves the array of the items in this list.
158 The items may be represented by any scalar.
159 If an argument has been given, it is pushed on the list of items.
160
161 =cut
162
163 # The individual =items of this list
164 sub item {
165     my ($self,$item) = @_;
166     if(defined $item) {
167         push(@{$self->{_items}}, $item);
168         return $item;
169     }
170     else {
171         return @{$self->{_items}};
172     }
173 }
174
175 =item $list-E<gt>parent()
176
177 Without argument, retrieves information about the parent holding this
178 list, which is represented as an arbitrary scalar.
179 This must have been set before by either specifying
180 B<-parent> in the B<new()> method or by calling the B<parent()> method
181 with a scalar argument.
182
183 =cut
184
185 # possibility for parsers/translators to store information about the
186 # lists's parent object
187 sub parent {
188    return (@_ > 1) ? ($_[0]->{-parent} = $_[1]) : $_[0]->{-parent};
189 }
190
191 =item $list-E<gt>tag()
192
193 Without argument, retrieves information about the list tag, which can be
194 any scalar.
195 This must have been set before by either specifying
196 B<-tag> in the B<new()> method or by calling the B<tag()> method
197 with a scalar argument.
198
199 =back
200
201 =cut
202
203 # possibility for parsers/translators to store information about the
204 # list's object
205 sub tag {
206    return (@_ > 1) ? ($_[0]->{-tag} = $_[1]) : $_[0]->{-tag};
207 }
208
209 #-----------------------------------------------------------------------------
210 # Pod::Hyperlink
211 #
212 # class to manipulate POD hyperlinks (L<>)
213 #-----------------------------------------------------------------------------
214
215 package Pod::Hyperlink;
216
217 =head2 Pod::Hyperlink
218
219 B<Pod::Hyperlink> is a class for manipulation of POD hyperlinks. Usage:
220
221   my $link = Pod::Hyperlink->new('alternative text|page/"section in page"');
222
223 The B<Pod::Hyperlink> class is mainly designed to parse the contents of the
224 C<LE<lt>...E<gt>> sequence, providing a simple interface for accessing the
225 different parts of a POD hyperlink for further processing. It can also be
226 used to construct hyperlinks.
227
228 =over 4
229
230 =item Pod::Hyperlink-E<gt>new()
231
232 The B<new()> method can either be passed a set of key/value pairs or a single
233 scalar value, namely the contents of a C<LE<lt>...E<gt>> sequence. An object
234 of the class C<Pod::Hyperlink> is returned. The value C<undef> indicates a
235 failure, the error message is stored in C<$@>.
236
237 =cut
238
239 use Carp;
240
241 sub new {
242     my $this = shift;
243     my $class = ref($this) || $this;
244     my $self = +{};
245     bless $self, $class;
246     $self->initialize();
247     if(defined $_[0]) {
248         if(ref($_[0])) {
249             # called with a list of parameters
250             %$self = %{$_[0]};
251             $self->_construct_text();
252         }
253         else {
254             # called with L<> contents
255             return undef unless($self->parse($_[0]));
256         }
257     }
258     return $self;
259 }
260
261 sub initialize {
262     my $self = shift;
263     $self->{-line} ||= 'undef';
264     $self->{-file} ||= 'undef';
265     $self->{-page} ||= '';
266     $self->{-node} ||= '';
267     $self->{-alttext} ||= '';
268     $self->{-type} ||= 'undef';
269     $self->{_warnings} = [];
270 }
271
272 =item $link-E<gt>parse($string)
273
274 This method can be used to (re)parse a (new) hyperlink, i.e. the contents
275 of a C<LE<lt>...E<gt>> sequence. The result is stored in the current object.
276 Warnings are stored in the B<warnings> property.
277 E.g. sections like C<LE<lt>open(2)E<gt>> are deprecated, as they do not point
278 to Perl documents. C<LE<lt>DBI::foo(3p)E<gt>> is wrong as well, the manpage
279 section can simply be dropped.
280
281 =cut
282
283 sub parse {
284     my $self = shift;
285     local($_) = $_[0];
286     # syntax check the link and extract destination
287     my ($alttext,$page,$node,$type,$quoted) = (undef,'','','',0);
288
289     $self->{_warnings} = [];
290
291     # collapse newlines with whitespace
292     s/\s*\n+\s*/ /g;
293
294     # strip leading/trailing whitespace
295     if(s/^[\s\n]+//) {
296         $self->warning("ignoring leading whitespace in link");
297     }
298     if(s/[\s\n]+$//) {
299         $self->warning("ignoring trailing whitespace in link");
300     }
301     unless(length($_)) {
302         _invalid_link("empty link");
303         return undef;
304     }
305
306     ## Check for different possibilities. This is tedious and error-prone
307     # we match all possibilities (alttext, page, section/item)
308     #warn "DEBUG: link=$_\n";
309
310     # only page
311     # problem: a lot of people use (), or (1) or the like to indicate
312     # man page sections. But this collides with L<func()> that is supposed
313     # to point to an internal funtion...
314     my $page_rx = '[\w.-]+(?:::[\w.-]+)*(?:[(](?:\d\w*|)[)]|)';
315     # page name only
316     if(m!^($page_rx)$!o) {
317         $page = $1;
318         $type = 'page';
319     }
320     # alttext, page and "section"
321     elsif(m!^(.*?)\s*[|]\s*($page_rx)\s*/\s*"(.+)"$!o) {
322         ($alttext, $page, $node) = ($1, $2, $3);
323         $type = 'section';
324         $quoted = 1; #... therefore | and / are allowed
325     }
326     # alttext and page
327     elsif(m!^(.*?)\s*[|]\s*($page_rx)$!o) {
328         ($alttext, $page) = ($1, $2);
329         $type = 'page';
330     }
331     # alttext and "section"
332     elsif(m!^(.*?)\s*[|]\s*(?:/\s*|)"(.+)"$!) {
333         ($alttext, $node) = ($1,$2);
334         $type = 'section';
335         $quoted = 1;
336     }
337     # page and "section"
338     elsif(m!^($page_rx)\s*/\s*"(.+)"$!o) {
339         ($page, $node) = ($1, $2);
340         $type = 'section';
341         $quoted = 1;
342     }
343     # page and item
344     elsif(m!^($page_rx)\s*/\s*(.+)$!o) {
345         ($page, $node) = ($1, $2);
346         $type = 'item';
347     }
348     # only "section"
349     elsif(m!^/?"(.+)"$!) {
350         $node = $1;
351         $type = 'section';
352         $quoted = 1;
353     }
354     # only item
355     elsif(m!^\s*/(.+)$!) {
356         $node = $1;
357         $type = 'item';
358     }
359     # non-standard: Hyperlink
360     elsif(m!^(\w+:[^:\s]\S*)$!i) {
361         $node = $1;
362         $type = 'hyperlink';
363     }
364     # alttext, page and item
365     elsif(m!^(.*?)\s*[|]\s*($page_rx)\s*/\s*(.+)$!o) {
366         ($alttext, $page, $node) = ($1, $2, $3);
367         $type = 'item';
368     }
369     # alttext and item
370     elsif(m!^(.*?)\s*[|]\s*/(.+)$!) {
371         ($alttext, $node) = ($1,$2);
372     }
373     # nonstandard: alttext and hyperlink
374     elsif(m!^(.*?)\s*[|]\s*(\w+:[^:\s]\S*)$!) {
375         ($alttext, $node) = ($1,$2);
376         $type = 'hyperlink';
377     }
378     # must be an item or a "malformed" section (without "")
379     else {
380         $node = $_;
381         $type = 'item';
382     }
383     # collapse whitespace in nodes
384     $node =~ s/\s+/ /gs;
385
386     # empty alternative text expands to node name
387     if(defined $alttext) {
388         if(!length($alttext)) {
389           $alttext = $node | $page;
390         }
391     }
392     else {
393         $alttext = '';
394     }
395
396     if($page =~ /[(]\w*[)]$/) {
397         $self->warning("(section) in '$page' deprecated");
398     }
399     if(!$quoted && $node =~ m:[|/]: && $type ne 'hyperlink') {
400         $self->warning("node '$node' contains non-escaped | or /");
401     }
402     if($alttext =~ m:[|/]:) {
403         $self->warning("alternative text '$node' contains non-escaped | or /");
404     }
405     $self->{-page} = $page;
406     $self->{-node} = $node;
407     $self->{-alttext} = $alttext;
408     #warn "DEBUG: page=$page section=$section item=$item alttext=$alttext\n";
409     $self->{-type} = $type;
410     $self->_construct_text();
411     1;
412 }
413
414 sub _construct_text {
415     my $self = shift;
416     my $alttext = $self->alttext();
417     my $type = $self->type();
418     my $section = $self->node();
419     my $page = $self->page();
420     my $page_ext = '';
421     $page =~ s/([(]\w*[)])$// && ($page_ext = $1);
422     if($alttext) {
423         $self->{_text} = $alttext;
424     }
425     elsif($type eq 'hyperlink') {
426         $self->{_text} = $section;
427     }
428     else {
429         $self->{_text} = ($section || '') .
430             (($page && $section) ? ' in ' : '') .
431             "$page$page_ext";
432     }
433     # for being marked up later
434     # use the non-standard markers P<> and Q<>, so that the resulting
435     # text can be parsed by the translators. It's their job to put
436     # the correct hypertext around the linktext
437     if($alttext) {
438         $self->{_markup} = "Q<$alttext>";
439     }
440     elsif($type eq 'hyperlink') {
441         $self->{_markup} = "Q<$section>";
442     }
443     else {
444         $self->{_markup} = (!$section ? '' : "Q<$section>") .
445             ($page ? ($section ? ' in ':'') . "P<$page>$page_ext" : '');
446     }
447 }
448
449 =item $link-E<gt>markup($string)
450
451 Set/retrieve the textual value of the link. This string contains special
452 markers C<PE<lt>E<gt>> and C<QE<lt>E<gt>> that should be expanded by the
453 translator's interior sequence expansion engine to the
454 formatter-specific code to highlight/activate the hyperlink. The details
455 have to be implemented in the translator.
456
457 =cut
458
459 #' retrieve/set markuped text
460 sub markup {
461     return (@_ > 1) ? ($_[0]->{_markup} = $_[1]) : $_[0]->{_markup};
462 }
463
464 =item $link-E<gt>text()
465
466 This method returns the textual representation of the hyperlink as above,
467 but without markers (read only). Depending on the link type this is one of
468 the following alternatives (the + and * denote the portions of the text
469 that are marked up):
470
471   +perl+                    L<perl>
472   *$|* in +perlvar+         L<perlvar/$|>
473   *OPTIONS* in +perldoc+    L<perldoc/"OPTIONS">
474   *DESCRIPTION*             L<"DESCRIPTION">
475
476 =cut
477
478 # The complete link's text
479 sub text {
480     $_[0]->{_text};
481 }
482
483 =item $link-E<gt>warning()
484
485 After parsing, this method returns any warnings encountered during the
486 parsing process.
487
488 =cut
489
490 # Set/retrieve warnings
491 sub warning {
492     my $self = shift;
493     if(@_) {
494         push(@{$self->{_warnings}}, @_);
495         return @_;
496     }
497     return @{$self->{_warnings}};
498 }
499
500 =item $link-E<gt>file()
501
502 =item $link-E<gt>line()
503
504 Just simple slots for storing information about the line and the file
505 the link was encountered in. Has to be filled in manually.
506
507 =cut
508
509 # The line in the file the link appears
510 sub line {
511     return (@_ > 1) ? ($_[0]->{-line} = $_[1]) : $_[0]->{-line};
512 }
513
514 # The POD file name the link appears in
515 sub file {
516     return (@_ > 1) ? ($_[0]->{-file} = $_[1]) : $_[0]->{-file};
517 }
518
519 =item $link-E<gt>page()
520
521 This method sets or returns the POD page this link points to.
522
523 =cut
524
525 # The POD page the link appears on
526 sub page {
527     if (@_ > 1) {
528         $_[0]->{-page} = $_[1];
529         $_[0]->_construct_text();
530     }
531     $_[0]->{-page};
532 }
533
534 =item $link-E<gt>node()
535
536 As above, but the destination node text of the link.
537
538 =cut
539
540 # The link destination
541 sub node {
542     if (@_ > 1) {
543         $_[0]->{-node} = $_[1];
544         $_[0]->_construct_text();
545     }
546     $_[0]->{-node};
547 }
548
549 =item $link-E<gt>alttext()
550
551 Sets or returns an alternative text specified in the link.
552
553 =cut
554
555 # Potential alternative text
556 sub alttext {
557     if (@_ > 1) {
558         $_[0]->{-alttext} = $_[1];
559         $_[0]->_construct_text();
560     }
561     $_[0]->{-alttext};
562 }
563
564 =item $link-E<gt>type()
565
566 The node type, either C<section> or C<item>. As an unofficial type,
567 there is also C<hyperlink>, derived from e.g. C<LE<lt>http://perl.comE<gt>>
568
569 =cut
570
571 # The type: item or headn
572 sub type {
573     return (@_ > 1) ? ($_[0]->{-type} = $_[1]) : $_[0]->{-type};
574 }
575
576 =item $link-E<gt>link()
577
578 Returns the link as contents of C<LE<lt>E<gt>>. Reciprocal to B<parse()>.
579
580 =back
581
582 =cut
583
584 # The link itself
585 sub link {
586     my $self = shift;
587     my $link = $self->page() || '';
588     if($self->node()) {
589         my $node = $self->node();
590         $text =~ s/\|/E<verbar>/g;
591         $text =~ s:/:E<sol>:g;
592         if($self->type() eq 'section') {
593             $link .= ($link ? '/' : '') . '"' . $node . '"';
594         }
595         elsif($self->type() eq 'hyperlink') {
596             $link = $self->node();
597         }
598         else { # item
599             $link .= '/' . $node;
600         }
601     }
602     if($self->alttext()) {
603         my $text = $self->alttext();
604         $text =~ s/\|/E<verbar>/g;
605         $text =~ s:/:E<sol>:g;
606         $link = "$text|$link";
607     }
608     $link;
609 }
610
611 sub _invalid_link {
612     my ($msg) = @_;
613     # this sets @_
614     #eval { die "$msg\n" };
615     #chomp $@;
616     $@ = $msg; # this seems to work, too!
617     undef;
618 }
619
620 #-----------------------------------------------------------------------------
621 # Pod::Cache
622 #
623 # class to hold POD page details
624 #-----------------------------------------------------------------------------
625
626 package Pod::Cache;
627
628 =head2 Pod::Cache
629
630 B<Pod::Cache> holds information about a set of POD documents,
631 especially the nodes for hyperlinks.
632 The following methods are available:
633
634 =over 4
635
636 =item Pod::Cache-E<gt>new()
637
638 Create a new cache object. This object can hold an arbitrary number of
639 POD documents of class Pod::Cache::Item.
640
641 =cut
642
643 sub new {
644     my $this = shift;
645     my $class = ref($this) || $this;
646     my $self = [];
647     bless $self, $class;
648     return $self;
649 }
650
651 =item $cache-E<gt>item()
652
653 Add a new item to the cache. Without arguments, this method returns a
654 list of all cache elements.
655
656 =cut
657
658 sub item {
659     my ($self,%param) = @_;
660     if(%param) {
661         my $item = Pod::Cache::Item->new(%param);
662         push(@$self, $item);
663         return $item;
664     }
665     else {
666         return @{$self};
667     }
668 }
669
670 =item $cache-E<gt>find_page($name)
671
672 Look for a POD document named C<$name> in the cache. Returns the
673 reference to the corresponding Pod::Cache::Item object or undef if
674 not found.
675
676 =back
677
678 =cut
679
680 sub find_page {
681     my ($self,$page) = @_;
682     foreach(@$self) {
683         if($_->page() eq $page) {
684             return $_;
685         }
686     }
687     undef;
688 }
689
690 package Pod::Cache::Item;
691
692 =head2 Pod::Cache::Item
693
694 B<Pod::Cache::Item> holds information about individual POD documents,
695 that can be grouped in a Pod::Cache object.
696 It is intended to hold information about the hyperlink nodes of POD
697 documents.
698 The following methods are available:
699
700 =over 4
701
702 =item Pod::Cache::Item-E<gt>new()
703
704 Create a new object.
705
706 =cut
707
708 sub new {
709     my $this = shift;
710     my $class = ref($this) || $this;
711     my %params = @_;
712     my $self = {%params};
713     bless $self, $class;
714     $self->initialize();
715     return $self;
716 }
717
718 sub initialize {
719     my $self = shift;
720     $self->{-nodes} = [] unless(defined $self->{-nodes});
721 }
722
723 =item $cacheitem-E<gt>page()
724
725 Set/retrieve the POD document name (e.g. "Pod::Parser").
726
727 =cut
728
729 # The POD page
730 sub page {
731    return (@_ > 1) ? ($_[0]->{-page} = $_[1]) : $_[0]->{-page};
732 }
733
734 =item $cacheitem-E<gt>description()
735
736 Set/retrieve the POD short description as found in the C<=head1 NAME>
737 section.
738
739 =cut
740
741 # The POD description, taken out of NAME if present
742 sub description {
743    return (@_ > 1) ? ($_[0]->{-description} = $_[1]) : $_[0]->{-description};
744 }
745
746 =item $cacheitem-E<gt>path()
747
748 Set/retrieve the POD file storage path.
749
750 =cut
751
752 # The file path
753 sub path {
754    return (@_ > 1) ? ($_[0]->{-path} = $_[1]) : $_[0]->{-path};
755 }
756
757 =item $cacheitem-E<gt>file()
758
759 Set/retrieve the POD file name.
760
761 =cut
762
763 # The POD file name
764 sub file {
765    return (@_ > 1) ? ($_[0]->{-file} = $_[1]) : $_[0]->{-file};
766 }
767
768 =item $cacheitem-E<gt>nodes()
769
770 Add a node (or a list of nodes) to the document's node list. Note that
771 the order is kept, i.e. start with the first node and end with the last.
772 If no argument is given, the current list of nodes is returned in the
773 same order the nodes have been added.
774 A node can be any scalar, but usually is a pair of node string and
775 unique id for the C<find_node> method to work correctly.
776
777 =cut
778
779 # The POD nodes
780 sub nodes {
781     my ($self,@nodes) = @_;
782     if(@nodes) {
783         push(@{$self->{-nodes}}, @nodes);
784         return @nodes;
785     }
786     else {
787         return @{$self->{-nodes}};
788     }
789 }
790
791 =item $cacheitem-E<gt>find_node($name)
792
793 Look for a node or index entry named C<$name> in the object.
794 Returns the unique id of the node (i.e. the second element of the array
795 stored in the node arry) or undef if not found.
796
797 =cut
798
799 sub find_node {
800     my ($self,$node) = @_;
801     my @search;
802     push(@search, @{$self->{-nodes}}) if($self->{-nodes});
803     push(@search, @{$self->{-idx}}) if($self->{-idx});
804     foreach(@search) {
805         if($_->[0] eq $node) {
806             return $_->[1]; # id
807         }
808     }
809     undef;
810 }
811
812 =item $cacheitem-E<gt>idx()
813
814 Add an index entry (or a list of them) to the document's index list. Note that
815 the order is kept, i.e. start with the first node and end with the last.
816 If no argument is given, the current list of index entries is returned in the
817 same order the entries have been added.
818 An index entry can be any scalar, but usually is a pair of string and
819 unique id.
820
821 =back
822
823 =cut
824
825 # The POD index entries
826 sub idx {
827     my ($self,@idx) = @_;
828     if(@idx) {
829         push(@{$self->{-idx}}, @idx);
830         return @idx;
831     }
832     else {
833         return @{$self->{-idx}};
834     }
835 }
836
837 =head1 AUTHOR
838
839 Please report bugs using L<http://rt.cpan.org>.
840
841 Marek Rouchal E<lt>marekr@cpan.orgE<gt>, borrowing
842 a lot of things from L<pod2man> and L<pod2roff> as well as other POD
843 processing tools by Tom Christiansen, Brad Appleton and Russ Allbery.
844
845 =head1 SEE ALSO
846
847 L<pod2man>, L<pod2roff>, L<Pod::Parser>, L<Pod::Checker>,
848 L<pod2html>
849
850 =cut
851
852 1;