summaryrefslogtreecommitdiff
path: root/cpan/Pod-Checker
diff options
context:
space:
mode:
authorChris 'BinGOs' Williams <chris@bingosnet.co.uk>2013-02-05 22:45:57 +0000
committerChris 'BinGOs' Williams <chris@bingosnet.co.uk>2013-02-05 23:37:03 +0000
commit0c50187808c90649d93897951eb5d95c20c23588 (patch)
tree96c169e080e202962f784f62cfdf138873a464b0 /cpan/Pod-Checker
parent4612967b70e727d981e2d25157ad29ceb2608c3c (diff)
downloadperl-0c50187808c90649d93897951eb5d95c20c23588.tar.gz
Update Pod-Parser to CPAN version 1.60
[DELTA] [Pod-Parser]   31-Jan-2013           Marek Rouchal                        <marekr@cpan.org>   -----------------------------------------------------------------------------   Version 1.60   + removed Pod::Checker and Pod::Usage from this distribution - they are now     separate distros and are subjects to be refactored, to be based upon     Pod::Simple. Thanks to rjbs for reminding me of this. Add Pod-Checker and Pod-Usage to cpan/ These have been split out of Pod-Parser [Pod-Checker] 27-Jan-2013           Marek Rouchal                        <marekr@cpan.org> ----------------------------------------------------------------------------- Version 1.60 + preparation of changing this module to use Pod::Simple    refactored the Pod-Parser distribution and moved all things Pod::Checker    to this new distribution package + CPAN RT#79535: Pod::Checker synopsis for podchecker is opposite    corrected the POD synopsis + factored this distribution out of Pod-Parser-1.51 [Pod-Usage] 1.61 (marekr) - fix empty META.yml (CPAN RT#83118: META.yml is empty) - update outdated test expected data (CPAN RT#83111: fails test)   1.60 (marekr) - moved POD behind __END__ for slighlty quicker loading - CPAN RT#81387: 2 suggestions for module Pod::Usage    added example of how to use FindBin to locate the script;    added $Pod::Usage::Formatter to allow a different base class - CPAN RT#75598: [PATCH] Don't use perldoc if it is missing    implemented as suggested in the RT ticket - factored Pod::Usageout of the Pod-Parser distribution into a separate one,    in order to prepare the rewrite based upon Pod::Simple - thanks to rjbs for driving this
Diffstat (limited to 'cpan/Pod-Checker')
-rw-r--r--cpan/Pod-Checker/.gitignore1
-rw-r--r--cpan/Pod-Checker/lib/Pod/Checker.pm1330
-rw-r--r--cpan/Pod-Checker/scripts/podchecker.PL186
-rw-r--r--cpan/Pod-Checker/t/pod/contains_bad_pod.xr5
-rw-r--r--cpan/Pod-Checker/t/pod/empty.xr0
-rw-r--r--cpan/Pod-Checker/t/pod/podchkenc.t29
-rw-r--r--cpan/Pod-Checker/t/pod/podchkenc.xr1
-rw-r--r--cpan/Pod-Checker/t/pod/poderrs.t241
-rw-r--r--cpan/Pod-Checker/t/pod/poderrs.xr53
-rw-r--r--cpan/Pod-Checker/t/pod/selfcheck.t45
-rw-r--r--cpan/Pod-Checker/t/pod/testcmp.pl94
-rw-r--r--cpan/Pod-Checker/t/pod/testpchk.pl130
12 files changed, 2115 insertions, 0 deletions
diff --git a/cpan/Pod-Checker/.gitignore b/cpan/Pod-Checker/.gitignore
new file mode 100644
index 0000000000..48f56f3a35
--- /dev/null
+++ b/cpan/Pod-Checker/.gitignore
@@ -0,0 +1 @@
+/podchecker*
diff --git a/cpan/Pod-Checker/lib/Pod/Checker.pm b/cpan/Pod-Checker/lib/Pod/Checker.pm
new file mode 100644
index 0000000000..ba47e6fa71
--- /dev/null
+++ b/cpan/Pod-Checker/lib/Pod/Checker.pm
@@ -0,0 +1,1330 @@
+#############################################################################
+# Pod/Checker.pm -- check pod documents for syntax errors
+#
+# Copyright (C) 1994-2000 by Bradford Appleton. All rights reserved.
+# This file is part of "PodParser". PodParser is free software;
+# you can redistribute it and/or modify it under the same terms
+# as Perl itself.
+#############################################################################
+
+package Pod::Checker;
+use strict;
+
+use vars qw($VERSION @ISA @EXPORT %VALID_COMMANDS %VALID_SEQUENCES);
+$VERSION = '1.60'; ## Current version of this package
+require 5.005; ## requires this Perl version or later
+
+use Pod::ParseUtils; ## for hyperlinks and lists
+
+=head1 NAME
+
+Pod::Checker, podchecker() - check pod documents for syntax errors
+
+=head1 SYNOPSIS
+
+ use Pod::Checker;
+
+ $num_errors = podchecker($filepath, $outputpath, %options);
+
+ my $checker = new Pod::Checker %options;
+ $checker->parse_from_file($filepath, \*STDERR);
+
+=head1 OPTIONS/ARGUMENTS
+
+C<$filepath> is the input POD to read and C<$outputpath> is
+where to write POD syntax error messages. Either argument may be a scalar
+indicating a file-path, or else a reference to an open filehandle.
+If unspecified, the input-file it defaults to C<\*STDIN>, and
+the output-file defaults to C<\*STDERR>.
+
+=head2 podchecker()
+
+This function can take a hash of options:
+
+=over 4
+
+=item B<-warnings> =E<gt> I<val>
+
+Turn warnings on/off. I<val> is usually 1 for on, but higher values
+trigger additional warnings. See L<"Warnings">.
+
+=back
+
+=head1 DESCRIPTION
+
+B<podchecker> will perform syntax checking of Perl5 POD format documentation.
+
+Curious/ambitious users are welcome to propose additional features they wish
+to see in B<Pod::Checker> and B<podchecker> and verify that the checks are
+consistent with L<perlpod>.
+
+The following checks are currently performed:
+
+=over 4
+
+=item *
+
+Unknown '=xxxx' commands, unknown 'XE<lt>...E<gt>' interior-sequences,
+and unterminated interior sequences.
+
+=item *
+
+Check for proper balancing of C<=begin> and C<=end>. The contents of such
+a block are generally ignored, i.e. no syntax checks are performed.
+
+=item *
+
+Check for proper nesting and balancing of C<=over>, C<=item> and C<=back>.
+
+=item *
+
+Check for same nested interior-sequences (e.g.
+C<LE<lt>...LE<lt>...E<gt>...E<gt>>).
+
+=item *
+
+Check for malformed or non-existing entities C<EE<lt>...E<gt>>.
+
+=item *
+
+Check for correct syntax of hyperlinks C<LE<lt>...E<gt>>. See L<perlpod>
+for details.
+
+=item *
+
+Check for unresolved document-internal links. This check may also reveal
+misspelled links that seem to be internal links but should be links
+to something else.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=head2 Errors
+
+=over 4
+
+=item * empty =headn
+
+A heading (C<=head1> or C<=head2>) without any text? That ain't no
+heading!
+
+=item * =over on line I<N> without closing =back
+
+The C<=over> command does not have a corresponding C<=back> before the
+next heading (C<=head1> or C<=head2>) or the end of the file.
+
+=item * =item without previous =over
+
+=item * =back without previous =over
+
+An C<=item> or C<=back> command has been found outside a
+C<=over>/C<=back> block.
+
+=item * No argument for =begin
+
+A C<=begin> command was found that is not followed by the formatter
+specification.
+
+=item * =end without =begin
+
+A standalone C<=end> command was found.
+
+=item * Nested =begin's
+
+There were at least two consecutive C<=begin> commands without
+the corresponding C<=end>. Only one C<=begin> may be active at
+a time.
+
+=item * =for without formatter specification
+
+There is no specification of the formatter after the C<=for> command.
+
+=item * Apparent command =foo not preceded by blank line
+
+A command which has ended up in the middle of a paragraph or other command,
+such as
+
+ =item one
+ =item two <-- bad
+
+=item * unresolved internal link I<NAME>
+
+The given link to I<NAME> does not have a matching node in the current
+POD. This also happened when a single word node name is not enclosed in
+C<"">.
+
+=item * Unknown command "I<CMD>"
+
+An invalid POD command has been found. Valid are C<=head1>, C<=head2>,
+C<=head3>, C<=head4>, C<=over>, C<=item>, C<=back>, C<=begin>, C<=end>,
+C<=for>, C<=pod>, C<=cut>
+
+=item * Unknown interior-sequence "I<SEQ>"
+
+An invalid markup command has been encountered. Valid are:
+C<BE<lt>E<gt>>, C<CE<lt>E<gt>>, C<EE<lt>E<gt>>, C<FE<lt>E<gt>>,
+C<IE<lt>E<gt>>, C<LE<lt>E<gt>>, C<SE<lt>E<gt>>, C<XE<lt>E<gt>>,
+C<ZE<lt>E<gt>>
+
+=item * nested commands I<CMD>E<lt>...I<CMD>E<lt>...E<gt>...E<gt>
+
+Two nested identical markup commands have been found. Generally this
+does not make sense.
+
+=item * garbled entity I<STRING>
+
+The I<STRING> found cannot be interpreted as a character entity.
+
+=item * Entity number out of range
+
+An entity specified by number (dec, hex, oct) is out of range (1-255).
+
+=item * malformed link LE<lt>E<gt>
+
+The link found cannot be parsed because it does not conform to the
+syntax described in L<perlpod>.
+
+=item * nonempty ZE<lt>E<gt>
+
+The C<ZE<lt>E<gt>> sequence is supposed to be empty.
+
+=item * empty XE<lt>E<gt>
+
+The index entry specified contains nothing but whitespace.
+
+=item * Spurious text after =pod / =cut
+
+The commands C<=pod> and C<=cut> do not take any arguments.
+
+=item * Spurious =cut command
+
+A C<=cut> command was found without a preceding POD paragraph.
+
+=item * Spurious =pod command
+
+A C<=pod> command was found after a preceding POD paragraph.
+
+=item * Spurious character(s) after =back
+
+The C<=back> command does not take any arguments.
+
+=back
+
+=head2 Warnings
+
+These may not necessarily cause trouble, but indicate mediocre style.
+
+=over 4
+
+=item * multiple occurrence of link target I<name>
+
+The POD file has some C<=item> and/or C<=head> commands that have
+the same text. Potential hyperlinks to such a text cannot be unique then.
+This warning is printed only with warning level greater than one.
+
+=item * line containing nothing but whitespace in paragraph
+
+There is some whitespace on a seemingly empty line. POD is very sensitive
+to such things, so this is flagged. B<vi> users switch on the B<list>
+option to avoid this problem.
+
+=begin _disabled_
+
+=item * file does not start with =head
+
+The file starts with a different POD directive than head.
+This is most probably something you do not want.
+
+=end _disabled_
+
+=item * previous =item has no contents
+
+There is a list C<=item> right above the flagged line that has no
+text contents. You probably want to delete empty items.
+
+=item * preceding non-item paragraph(s)
+
+A list introduced by C<=over> starts with a text or verbatim paragraph,
+but continues with C<=item>s. Move the non-item paragraph out of the
+C<=over>/C<=back> block.
+
+=item * =item type mismatch (I<one> vs. I<two>)
+
+A list started with e.g. a bullet-like C<=item> and continued with a
+numbered one. This is obviously inconsistent. For most translators the
+type of the I<first> C<=item> determines the type of the list.
+
+=item * I<N> unescaped C<E<lt>E<gt>> in paragraph
+
+Angle brackets not written as C<E<lt>ltE<gt>> and C<E<lt>gtE<gt>>
+can potentially cause errors as they could be misinterpreted as
+markup commands. This is only printed when the -warnings level is
+greater than 1.
+
+=item * Unknown entity
+
+A character entity was found that does not belong to the standard
+ISO set or the POD specials C<verbar> and C<sol>.
+
+=item * No items in =over
+
+The list opened with C<=over> does not contain any items.
+
+=item * No argument for =item
+
+C<=item> without any parameters is deprecated. It should either be followed
+by C<*> to indicate an unordered list, by a number (optionally followed
+by a dot) to indicate an ordered (numbered) list or simple text for a
+definition list.
+
+=item * empty section in previous paragraph
+
+The previous section (introduced by a C<=head> command) does not contain
+any text. This usually indicates that something is missing. Note: A
+C<=head1> followed immediately by C<=head2> does not trigger this warning.
+
+=item * Verbatim paragraph in NAME section
+
+The NAME section (C<=head1 NAME>) should consist of a single paragraph
+with the script/module name, followed by a dash `-' and a very short
+description of what the thing is good for.
+
+=item * =headI<n> without preceding higher level
+
+For example if there is a C<=head2> in the POD file prior to a
+C<=head1>.
+
+=back
+
+=head2 Hyperlinks
+
+There are some warnings with respect to malformed hyperlinks:
+
+=over 4
+
+=item * ignoring leading/trailing whitespace in link
+
+There is whitespace at the beginning or the end of the contents of
+LE<lt>...E<gt>.
+
+=item * (section) in '$page' deprecated
+
+There is a section detected in the page name of LE<lt>...E<gt>, e.g.
+C<LE<lt>passwd(2)E<gt>>. POD hyperlinks may point to POD documents only.
+Please write C<CE<lt>passwd(2)E<gt>> instead. Some formatters are able
+to expand this to appropriate code. For links to (builtin) functions,
+please say C<LE<lt>perlfunc/mkdirE<gt>>, without ().
+
+=item * alternative text/node '%s' contains non-escaped | or /
+
+The characters C<|> and C</> are special in the LE<lt>...E<gt> context.
+Although the hyperlink parser does its best to determine which "/" is
+text and which is a delimiter in case of doubt, one ought to escape
+these literal characters like this:
+
+ / E<sol>
+ | E<verbar>
+
+=back
+
+=head1 RETURN VALUE
+
+B<podchecker> returns the number of POD syntax errors found or -1 if
+there were no POD commands at all found in the file.
+
+=head1 EXAMPLES
+
+See L</SYNOPSIS>
+
+=head1 INTERFACE
+
+While checking, this module collects document properties, e.g. the nodes
+for hyperlinks (C<=headX>, C<=item>) and index entries (C<XE<lt>E<gt>>).
+POD translators can use this feature to syntax-check and get the nodes in
+a first pass before actually starting to convert. This is expensive in terms
+of execution time, but allows for very robust conversions.
+
+Since PodParser-1.24 the B<Pod::Checker> module uses only the B<poderror>
+method to print errors and warnings. The summary output (e.g.
+"Pod syntax OK") has been dropped from the module and has been included in
+B<podchecker> (the script). This allows users of B<Pod::Checker> to
+control completely the output behavior. Users of B<podchecker> (the script)
+get the well-known behavior.
+
+=cut
+
+#############################################################################
+
+#use diagnostics;
+use Carp qw(croak);
+use Exporter;
+use Pod::Parser;
+
+@ISA = qw(Pod::Parser);
+@EXPORT = qw(&podchecker);
+
+my %VALID_COMMANDS = (
+ 'pod' => 1,
+ 'cut' => 1,
+ 'head1' => 1,
+ 'head2' => 1,
+ 'head3' => 1,
+ 'head4' => 1,
+ 'over' => 1,
+ 'back' => 1,
+ 'item' => 1,
+ 'for' => 1,
+ 'begin' => 1,
+ 'end' => 1,
+ 'encoding' => 1,
+);
+
+my %VALID_SEQUENCES = (
+ 'I' => 1,
+ 'B' => 1,
+ 'S' => 1,
+ 'C' => 1,
+ 'L' => 1,
+ 'F' => 1,
+ 'X' => 1,
+ 'Z' => 1,
+ 'E' => 1,
+);
+
+# stolen from HTML::Entities
+my %ENTITIES = (
+ # Some normal chars that have special meaning in SGML context
+ amp => '&', # ampersand
+'gt' => '>', # greater than
+'lt' => '<', # less than
+ quot => '"', # double quote
+
+ # PUBLIC ISO 8879-1986//ENTITIES Added Latin 1//EN//HTML
+ AElig => 'Æ', # capital AE diphthong (ligature)
+ Aacute => 'Á', # capital A, acute accent
+ Acirc => 'Â', # capital A, circumflex accent
+ Agrave => 'À', # capital A, grave accent
+ Aring => 'Å', # capital A, ring
+ Atilde => 'Ã', # capital A, tilde
+ Auml => 'Ä', # capital A, dieresis or umlaut mark
+ Ccedil => 'Ç', # capital C, cedilla
+ ETH => 'Ð', # capital Eth, Icelandic
+ Eacute => 'É', # capital E, acute accent
+ Ecirc => 'Ê', # capital E, circumflex accent
+ Egrave => 'È', # capital E, grave accent
+ Euml => 'Ë', # capital E, dieresis or umlaut mark
+ Iacute => 'Í', # capital I, acute accent
+ Icirc => 'Î', # capital I, circumflex accent
+ Igrave => 'Ì', # capital I, grave accent
+ Iuml => 'Ï', # capital I, dieresis or umlaut mark
+ Ntilde => 'Ñ', # capital N, tilde
+ Oacute => 'Ó', # capital O, acute accent
+ Ocirc => 'Ô', # capital O, circumflex accent
+ Ograve => 'Ò', # capital O, grave accent
+ Oslash => 'Ø', # capital O, slash
+ Otilde => 'Õ', # capital O, tilde
+ Ouml => 'Ö', # capital O, dieresis or umlaut mark
+ THORN => 'Þ', # capital THORN, Icelandic
+ Uacute => 'Ú', # capital U, acute accent
+ Ucirc => 'Û', # capital U, circumflex accent
+ Ugrave => 'Ù', # capital U, grave accent
+ Uuml => 'Ü', # capital U, dieresis or umlaut mark
+ Yacute => 'Ý', # capital Y, acute accent
+ aacute => 'á', # small a, acute accent
+ acirc => 'â', # small a, circumflex accent
+ aelig => 'æ', # small ae diphthong (ligature)
+ agrave => 'à', # small a, grave accent
+ aring => 'å', # small a, ring
+ atilde => 'ã', # small a, tilde
+ auml => 'ä', # small a, dieresis or umlaut mark
+ ccedil => 'ç', # small c, cedilla
+ eacute => 'é', # small e, acute accent
+ ecirc => 'ê', # small e, circumflex accent
+ egrave => 'è', # small e, grave accent
+ eth => 'ð', # small eth, Icelandic
+ euml => 'ë', # small e, dieresis or umlaut mark
+ iacute => 'í', # small i, acute accent
+ icirc => 'î', # small i, circumflex accent
+ igrave => 'ì', # small i, grave accent
+ iuml => 'ï', # small i, dieresis or umlaut mark
+ ntilde => 'ñ', # small n, tilde
+ oacute => 'ó', # small o, acute accent
+ ocirc => 'ô', # small o, circumflex accent
+ ograve => 'ò', # small o, grave accent
+ oslash => 'ø', # small o, slash
+ otilde => 'õ', # small o, tilde
+ ouml => 'ö', # small o, dieresis or umlaut mark
+ szlig => 'ß', # small sharp s, German (sz ligature)
+ thorn => 'þ', # small thorn, Icelandic
+ uacute => 'ú', # small u, acute accent
+ ucirc => 'û', # small u, circumflex accent
+ ugrave => 'ù', # small u, grave accent
+ uuml => 'ü', # small u, dieresis or umlaut mark
+ yacute => 'ý', # small y, acute accent
+ yuml => 'ÿ', # small y, dieresis or umlaut mark
+
+ # Some extra Latin 1 chars that are listed in the HTML3.2 draft (21-May-96)
+ copy => '©', # copyright sign
+ reg => '®', # registered sign
+ nbsp => "\240", # non breaking space
+
+ # Additional ISO-8859/1 entities listed in rfc1866 (section 14)
+ iexcl => '¡',
+ cent => '¢',
+ pound => '£',
+ curren => '¤',
+ yen => '¥',
+ brvbar => '¦',
+ sect => '§',
+ uml => '¨',
+ ordf => 'ª',
+ laquo => '«',
+'not' => '¬', # not is a keyword in perl
+ shy => '­',
+ macr => '¯',
+ deg => '°',
+ plusmn => '±',
+ sup1 => '¹',
+ sup2 => '²',
+ sup3 => '³',
+ acute => '´',
+ micro => 'µ',
+ para => '¶',
+ middot => '·',
+ cedil => '¸',
+ ordm => 'º',
+ raquo => '»',
+ frac14 => '¼',
+ frac12 => '½',
+ frac34 => '¾',
+ iquest => '¿',
+'times' => '×', # times is a keyword in perl
+ divide => '÷',
+
+# some POD special entities
+ verbar => '|',
+ sol => '/'
+);
+
+##---------------------------------------------------------------------------
+
+##---------------------------------
+## Function definitions begin here
+##---------------------------------
+
+sub podchecker {
+ my ($infile, $outfile, %options) = @_;
+ local $_;
+
+ ## Set defaults
+ $infile ||= \*STDIN;
+ $outfile ||= \*STDERR;
+
+ ## Now create a pod checker
+ my $checker = new Pod::Checker(%options);
+
+ ## Now check the pod document for errors
+ $checker->parse_from_file($infile, $outfile);
+
+ ## Return the number of errors found
+ return $checker->num_errors();
+}
+
+##---------------------------------------------------------------------------
+
+##-------------------------------
+## Method definitions begin here
+##-------------------------------
+
+##################################
+
+=over 4
+
+=item C<Pod::Checker-E<gt>new( %options )>
+
+Return a reference to a new Pod::Checker object that inherits from
+Pod::Parser and is used for calling the required methods later. The
+following options are recognized:
+
+C<-warnings =E<gt> num>
+ Print warnings if C<num> is true. The higher the value of C<num>,
+the more warnings are printed. Currently there are only levels 1 and 2.
+
+C<-quiet =E<gt> num>
+ If C<num> is true, do not print any errors/warnings. This is useful
+when Pod::Checker is used to munge POD code into plain text from within
+POD formatters.
+
+=cut
+
+## sub new {
+## my $this = shift;
+## my $class = ref($this) || $this;
+## my %params = @_;
+## my $self = {%params};
+## bless $self, $class;
+## $self->initialize();
+## return $self;
+## }
+
+sub initialize {
+ my $self = shift;
+ ## Initialize number of errors, and setup an error function to
+ ## increment this number and then print to the designated output.
+ $self->{_NUM_ERRORS} = 0;
+ $self->{_NUM_WARNINGS} = 0;
+ $self->{-quiet} ||= 0;
+ # set the error handling subroutine
+ $self->errorsub($self->{-quiet} ? sub { 1; } : 'poderror');
+ $self->{_commands} = 0; # total number of POD commands encountered
+ $self->{_list_stack} = []; # stack for nested lists
+ $self->{_have_begin} = ''; # stores =begin
+ $self->{_links} = []; # stack for internal hyperlinks
+ $self->{_nodes} = []; # stack for =head/=item nodes
+ $self->{_index} = []; # text in X<>
+ # print warnings?
+ $self->{-warnings} = 1 unless(defined $self->{-warnings});
+ $self->{_current_head1} = ''; # the current =head1 block
+ $self->parseopts(-process_cut_cmd => 1, -warnings => $self->{-warnings});
+}
+
+##################################
+
+=item C<$checker-E<gt>poderror( @args )>
+
+=item C<$checker-E<gt>poderror( {%opts}, @args )>
+
+Internal method for printing errors and warnings. If no options are
+given, simply prints "@_". The following options are recognized and used
+to form the output:
+
+ -msg
+
+A message to print prior to C<@args>.
+
+ -line
+
+The line number the error occurred in.
+
+ -file
+
+The file (name) the error occurred in.
+
+ -severity
+
+The error level, should be 'WARNING' or 'ERROR'.
+
+=cut
+
+# Invoked as $self->poderror( @args ), or $self->poderror( {%opts}, @args )
+sub poderror {
+ my $self = shift;
+ my %opts = (ref $_[0]) ? %{shift()} : ();
+
+ ## Retrieve options
+ chomp( my $msg = ($opts{-msg} || '')."@_" );
+ my $line = (exists $opts{-line}) ? " at line $opts{-line}" : '';
+ my $file = (exists $opts{-file}) ? " in file $opts{-file}" : '';
+ unless (exists $opts{-severity}) {
+ ## See if can find severity in message prefix
+ $opts{-severity} = $1 if ( $msg =~ s/^\**\s*([A-Z]{3,}):\s+// );
+ }
+ my $severity = (exists $opts{-severity}) ? "*** $opts{-severity}: " : '';
+
+ ## Increment error count and print message "
+ ++($self->{_NUM_ERRORS})
+ if(!%opts || ($opts{-severity} && $opts{-severity} eq 'ERROR'));
+ ++($self->{_NUM_WARNINGS})
+ if(!%opts || ($opts{-severity} && $opts{-severity} eq 'WARNING'));
+ unless($self->{-quiet}) {
+ my $out_fh = $self->output_handle() || \*STDERR;
+ print $out_fh ($severity, $msg, $line, $file, "\n")
+ if($self->{-warnings} || !%opts || $opts{-severity} ne 'WARNING');
+ }
+}
+
+##################################
+
+=item C<$checker-E<gt>num_errors()>
+
+Set (if argument specified) and retrieve the number of errors found.
+
+=cut
+
+sub num_errors {
+ return (@_ > 1) ? ($_[0]->{_NUM_ERRORS} = $_[1]) : $_[0]->{_NUM_ERRORS};
+}
+
+##################################
+
+=item C<$checker-E<gt>num_warnings()>
+
+Set (if argument specified) and retrieve the number of warnings found.
+
+=cut
+
+sub num_warnings {
+ return (@_ > 1) ? ($_[0]->{_NUM_WARNINGS} = $_[1]) : $_[0]->{_NUM_WARNINGS};
+}
+
+##################################
+
+=item C<$checker-E<gt>name()>
+
+Set (if argument specified) and retrieve the canonical name of POD as
+found in the C<=head1 NAME> section.
+
+=cut
+
+sub name {
+ return (@_ > 1 && $_[1]) ?
+ ($_[0]->{-name} = $_[1]) : $_[0]->{-name};
+}
+
+##################################
+
+=item C<$checker-E<gt>node()>
+
+Add (if argument specified) and retrieve the nodes (as defined by C<=headX>
+and C<=item>) of the current POD. The nodes are returned in the order of
+their occurrence. They consist of plain text, each piece of whitespace is
+collapsed to a single blank.
+
+=cut
+
+sub node {
+ my ($self,$text) = @_;
+ if(defined $text) {
+ $text =~ s/\s+$//s; # strip trailing whitespace
+ $text =~ s/\s+/ /gs; # collapse whitespace
+ # add node, order important!
+ push(@{$self->{_nodes}}, $text);
+ # keep also a uniqueness counter
+ $self->{_unique_nodes}->{$text}++ if($text !~ /^\s*$/s);
+ return $text;
+ }
+ @{$self->{_nodes}};
+}
+
+##################################
+
+=item C<$checker-E<gt>idx()>
+
+Add (if argument specified) and retrieve the index entries (as defined by
+C<XE<lt>E<gt>>) of the current POD. They consist of plain text, each piece
+of whitespace is collapsed to a single blank.
+
+=cut
+
+# set/return index entries of current POD
+sub idx {
+ my ($self,$text) = @_;
+ if(defined $text) {
+ $text =~ s/\s+$//s; # strip trailing whitespace
+ $text =~ s/\s+/ /gs; # collapse whitespace
+ # add node, order important!
+ push(@{$self->{_index}}, $text);
+ # keep also a uniqueness counter
+ $self->{_unique_nodes}->{$text}++ if($text !~ /^\s*$/s);
+ return $text;
+ }
+ @{$self->{_index}};
+}
+
+##################################
+
+=item C<$checker-E<gt>hyperlink()>
+
+Add (if argument specified) and retrieve the hyperlinks (as defined by
+C<LE<lt>E<gt>>) of the current POD. They consist of a 2-item array: line
+number and C<Pod::Hyperlink> object.
+
+=back
+
+=cut
+
+# set/return hyperlinks of the current POD
+sub hyperlink {
+ my $self = shift;
+ if($_[0]) {
+ push(@{$self->{_links}}, $_[0]);
+ return $_[0];
+ }
+ @{$self->{_links}};
+}
+
+## overrides for Pod::Parser
+
+sub end_pod {
+ ## Do some final checks and
+ ## print the number of errors found
+ my $self = shift;
+ my $infile = $self->input_file();
+
+ if(@{$self->{_list_stack}}) {
+ my $list;
+ while(($list = $self->_close_list('EOF',$infile)) &&
+ $list->indent() ne 'auto') {
+ $self->poderror({ -line => 'EOF', -file => $infile,
+ -severity => 'ERROR', -msg => '=over on line ' .
+ $list->start() . ' without closing =back' });
+ }
+ }
+
+ # check validity of document internal hyperlinks
+ # first build the node names from the paragraph text
+ my %nodes;
+ foreach($self->node()) {
+ $nodes{$_} = 1;
+ if(/^(\S+)\s+\S/) {
+ # we have more than one word. Use the first as a node, too.
+ # This is used heavily in perlfunc.pod
+ $nodes{$1} ||= 2; # derived node
+ }
+ }
+ foreach($self->idx()) {
+ $nodes{$_} = 3; # index node
+ }
+ foreach($self->hyperlink()) {
+ my ($line,$link) = @$_;
+ # _TODO_ what if there is a link to the page itself by the name,
+ # e.g. in Tk::Pod : L<Tk::Pod/"DESCRIPTION">
+ if($link->node() && !$link->page() && $link->type() ne 'hyperlink') {
+ my $node = $self->_check_ptree($self->parse_text($link->node(),
+ $line), $line, $infile, 'L');
+ if($node && !$nodes{$node}) {
+ $self->poderror({ -line => $line || '', -file => $infile,
+ -severity => 'ERROR',
+ -msg => "unresolved internal link '$node'"});
+ }
+ }
+ }
+
+ # check the internal nodes for uniqueness. This pertains to
+ # =headX, =item and X<...>
+ if($self->{-warnings} && $self->{-warnings}>1) {
+ foreach(grep($self->{_unique_nodes}->{$_} > 1,
+ keys %{$self->{_unique_nodes}})) {
+ $self->poderror({ -line => '-', -file => $infile,
+ -severity => 'WARNING',
+ -msg => "multiple occurrence of link target '$_'"});
+ }
+ }
+
+ # no POD found here
+ $self->num_errors(-1) if($self->{_commands} == 0);
+}
+
+# check a POD command directive
+sub command {
+ my ($self, $cmd, $paragraph, $line_num, $pod_para) = @_;
+ my ($file, $line) = $pod_para->file_line;
+ ## Check the command syntax
+ my $arg; # this will hold the command argument
+ if (! $VALID_COMMANDS{$cmd}) {
+ $self->poderror({ -line => $line, -file => $file, -severity => 'ERROR',
+ -msg => "Unknown command '$cmd'" });
+ }
+ else { # found a valid command
+ $self->{_commands}++; # delete this line if below is enabled again
+
+ $self->_commands_in_paragraphs($paragraph, $pod_para);
+
+ ##### following check disabled due to strong request
+ #if(!$self->{_commands}++ && $cmd !~ /^head/) {
+ # $self->poderror({ -line => $line, -file => $file,
+ # -severity => 'WARNING',
+ # -msg => "file does not start with =head" });
+ #}
+
+ # check syntax of particular command
+ if($cmd eq 'over') {
+ # check for argument
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ my $indent = 4; # default
+ if($arg && $arg =~ /^\s*(\d+)\s*$/) {
+ $indent = $1;
+ }
+ # start a new list
+ $self->_open_list($indent,$line,$file);
+ }
+ elsif($cmd eq 'item') {
+ # are we in a list?
+ unless(@{$self->{_list_stack}}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => '=item without previous =over' });
+ # auto-open in case we encounter many more
+ $self->_open_list('auto',$line,$file);
+ }
+ my $list = $self->{_list_stack}->[0];
+ # check whether the previous item had some contents
+ if(defined $self->{_list_item_contents} &&
+ $self->{_list_item_contents} == 0) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'previous =item has no contents' });
+ }
+ if($list->{_has_par}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'preceding non-item paragraph(s)' });
+ delete $list->{_has_par};
+ }
+ # check for argument
+ $arg = $self->interpolate_and_check($paragraph, $line, $file);
+ if($arg && $arg =~ /(\S+)/) {
+ $arg =~ s/[\s\n]+$//;
+ my $type;
+ if($arg =~ /^[*]\s*(\S*.*)/) {
+ $type = 'bullet';
+ $self->{_list_item_contents} = $1 ? 1 : 0;
+ $arg = $1;
+ }
+ elsif($arg =~ /^\d+\.?\s+(\S*)/) {
+ $type = 'number';
+ $self->{_list_item_contents} = $1 ? 1 : 0;
+ $arg = $1;
+ }
+ else {
+ $type = 'definition';
+ $self->{_list_item_contents} = 1;
+ }
+ my $first = $list->type();
+ if($first && $first ne $type) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => "=item type mismatch ('$first' vs. '$type')"});
+ }
+ else { # first item
+ $list->type($type);
+ }
+ }
+ else {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'No argument for =item' });
+ $arg = ' '; # empty
+ $self->{_list_item_contents} = 0;
+ }
+ # add this item
+ $list->item($arg);
+ # remember this node
+ $self->node($arg);
+ }
+ elsif($cmd eq 'back') {
+ # check if we have an open list
+ unless(@{$self->{_list_stack}}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => '=back without previous =over' });
+ }
+ else {
+ # check for spurious characters
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ if($arg && $arg =~ /\S/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'Spurious character(s) after =back' });
+ }
+ # close list
+ my $list = $self->_close_list($line,$file);
+ # check for empty lists
+ if(!$list->item() && $self->{-warnings}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'No items in =over (at line ' .
+ $list->start() . ') / =back list'});
+ }
+ }
+ }
+ elsif($cmd =~ /^head(\d+)/) {
+ my $hnum = $1;
+ $self->{"_have_head_$hnum"}++; # count head types
+ if($hnum > 1 && !$self->{'_have_head_'.($hnum -1)}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => "=head$hnum without preceding higher level"});
+ }
+ # check whether the previous =head section had some contents
+ if(defined $self->{_commands_in_head} &&
+ $self->{_commands_in_head} == 0 &&
+ defined $self->{_last_head} &&
+ $self->{_last_head} >= $hnum) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'empty section in previous paragraph'});
+ }
+ $self->{_commands_in_head} = -1;
+ $self->{_last_head} = $hnum;
+ # check if there is an open list
+ if(@{$self->{_list_stack}}) {
+ my $list;
+ while(($list = $self->_close_list($line,$file)) &&
+ $list->indent() ne 'auto') {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => '=over on line '. $list->start() .
+ " without closing =back (at $cmd)" });
+ }
+ }
+ # remember this node
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ $arg =~ s/[\s\n]+$//s;
+ $self->node($arg);
+ unless(length($arg)) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => "empty =$cmd"});
+ }
+ if($cmd eq 'head1') {
+ $self->{_current_head1} = $arg;
+ } else {
+ $self->{_current_head1} = '';
+ }
+ }
+ elsif($cmd eq 'begin') {
+ if($self->{_have_begin}) {
+ # already have a begin
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => q{Nested =begin's (first at line } .
+ $self->{_have_begin} . ')'});
+ }
+ else {
+ # check for argument
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ unless($arg && $arg =~ /(\S+)/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'No argument for =begin'});
+ }
+ # remember the =begin
+ $self->{_have_begin} = "$line:$1";
+ }
+ }
+ elsif($cmd eq 'end') {
+ if($self->{_have_begin}) {
+ # close the existing =begin
+ $self->{_have_begin} = '';
+ # check for spurious characters
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ # the closing argument is optional
+ #if($arg && $arg =~ /\S/) {
+ # $self->poderror({ -line => $line, -file => $file,
+ # -severity => 'WARNING',
+ # -msg => "Spurious character(s) after =end" });
+ #}
+ }
+ else {
+ # don't have a matching =begin
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => '=end without =begin' });
+ }
+ }
+ elsif($cmd eq 'for') {
+ unless($paragraph =~ /\s*(\S+)\s*/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => '=for without formatter specification' });
+ }
+ $arg = ''; # do not expand paragraph below
+ }
+ elsif($cmd =~ /^(pod|cut)$/) {
+ # check for argument
+ $arg = $self->interpolate_and_check($paragraph, $line,$file);
+ if($arg && $arg =~ /(\S+)/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => "Spurious text after =$cmd"});
+ }
+ if($cmd eq 'cut' && (!$self->{_PREVIOUS} || $self->{_PREVIOUS} eq 'cut')) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => "Spurious =cut command"});
+ }
+ if($cmd eq 'pod' && $self->{_PREVIOUS} && $self->{_PREVIOUS} ne 'cut') {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => "Spurious =pod command"});
+ }
+ }
+ $self->{_commands_in_head}++;
+ ## Check the interior sequences in the command-text
+ $self->interpolate_and_check($paragraph, $line,$file)
+ unless(defined $arg);
+ }
+}
+
+sub _open_list
+{
+ my ($self,$indent,$line,$file) = @_;
+ my $list = Pod::List->new(
+ -indent => $indent,
+ -start => $line,
+ -file => $file);
+ unshift(@{$self->{_list_stack}}, $list);
+ undef $self->{_list_item_contents};
+ $list;
+}
+
+sub _close_list
+{
+ my ($self,$line,$file) = @_;
+ my $list = shift(@{$self->{_list_stack}});
+ if(defined $self->{_list_item_contents} &&
+ $self->{_list_item_contents} == 0) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'previous =item has no contents' });
+ }
+ undef $self->{_list_item_contents};
+ $list;
+}
+
+# process a block of some text
+sub interpolate_and_check {
+ my ($self, $paragraph, $line, $file) = @_;
+ ## Check the interior sequences in the command-text
+ # and return the text
+ $self->_check_ptree(
+ $self->parse_text($paragraph,$line), $line, $file, '');
+}
+
+sub _check_ptree {
+ my ($self,$ptree,$line,$file,$nestlist) = @_;
+ local($_);
+ my $text = '';
+ # process each node in the parse tree
+ foreach(@$ptree) {
+ # regular text chunk
+ unless(ref) {
+ # count the unescaped angle brackets
+ # complain only when warning level is greater than 1
+ if($self->{-warnings} && $self->{-warnings}>1) {
+ my $count;
+ if($count = tr/<>/<>/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => "$count unescaped <> in paragraph" });
+ }
+ }
+ $text .= $_;
+ next;
+ }
+ # have an interior sequence
+ my $cmd = $_->cmd_name();
+ my $contents = $_->parse_tree();
+ ($file,$line) = $_->file_line();
+ # check for valid tag
+ if (! $VALID_SEQUENCES{$cmd}) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => qq(Unknown interior-sequence '$cmd')});
+ # expand it anyway
+ $text .= $self->_check_ptree($contents, $line, $file, "$nestlist$cmd");
+ next;
+ }
+ if(index($nestlist, $cmd) != -1) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => "nested commands $cmd<...$cmd<...>...>"});
+ # _TODO_ should we add the contents anyway?
+ # expand it anyway, see below
+ }
+ if($cmd eq 'E') {
+ # preserve entities
+ if(@$contents > 1 || ref $$contents[0] || $$contents[0] !~ /^\w+$/) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'garbled entity ' . $_->raw_text()});
+ next;
+ }
+ my $ent = $$contents[0];
+ my $val;
+ if($ent =~ /^0x[0-9a-f]+$/i) {
+ # hexadec entity
+ $val = hex($ent);
+ }
+ elsif($ent =~ /^0\d+$/) {
+ # octal
+ $val = oct($ent);
+ }
+ elsif($ent =~ /^\d+$/) {
+ # numeric entity
+ $val = $ent;
+ }
+ if(defined $val) {
+ if($val>0 && $val<256) {
+ $text .= chr($val);
+ }
+ else {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'Entity number out of range ' . $_->raw_text()});
+ }
+ }
+ elsif($ENTITIES{$ent}) {
+ # known ISO entity
+ $text .= $ENTITIES{$ent};
+ }
+ else {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'Unknown entity ' . $_->raw_text()});
+ $text .= "E<$ent>";
+ }
+ }
+ elsif($cmd eq 'L') {
+ # try to parse the hyperlink
+ my $link = Pod::Hyperlink->new($contents->raw_text());
+ unless(defined $link) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'malformed link ' . $_->raw_text() ." : $@"});
+ next;
+ }
+ $link->line($line); # remember line
+ if($self->{-warnings}) {
+ foreach my $w ($link->warning()) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => $w });
+ }
+ }
+ # check the link text
+ $text .= $self->_check_ptree($self->parse_text($link->text(),
+ $line), $line, $file, "$nestlist$cmd");
+ # remember link
+ $self->hyperlink([$line,$link]);
+ }
+ elsif($cmd =~ /[BCFIS]/) {
+ # add the guts
+ $text .= $self->_check_ptree($contents, $line, $file, "$nestlist$cmd");
+ }
+ elsif($cmd eq 'Z') {
+ if(length($contents->raw_text())) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'Nonempty Z<>'});
+ }
+ }
+ elsif($cmd eq 'X') {
+ my $idx = $self->_check_ptree($contents, $line, $file, "$nestlist$cmd");
+ if($idx =~ /^\s*$/s) {
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => 'Empty X<>'});
+ }
+ else {
+ # remember this node
+ $self->idx($idx);
+ }
+ }
+ else {
+ # not reached
+ croak 'internal error';
+ }
+ }
+ $text;
+}
+
+# process a block of verbatim text
+sub verbatim {
+ ## Nothing particular to check
+ my ($self, $paragraph, $line_num, $pod_para) = @_;
+
+ $self->_preproc_par($paragraph);
+ $self->_commands_in_paragraphs($paragraph, $pod_para);
+
+ if($self->{_current_head1} eq 'NAME') {
+ my ($file, $line) = $pod_para->file_line;
+ $self->poderror({ -line => $line, -file => $file,
+ -severity => 'WARNING',
+ -msg => 'Verbatim paragraph in NAME section' });
+ }
+}
+
+# process a block of regular text
+sub textblock {
+ my ($self, $paragraph, $line_num, $pod_para) = @_;
+ my ($file, $line) = $pod_para->file_line;
+
+ $self->_preproc_par($paragraph);
+ $self->_commands_in_paragraphs($paragraph, $pod_para);
+
+ # skip this paragraph if in a =begin block
+ unless($self->{_have_begin}) {
+ my $block = $self->interpolate_and_check($paragraph, $line,$file);
+ if($self->{_current_head1} eq 'NAME') {
+ if($block =~ /^\s*(\S+?)\s*[,-]/) {
+ # this is the canonical name
+ $self->{-name} = $1 unless(defined $self->{-name});
+ }
+ }
+ }
+}
+
+sub _preproc_par
+{
+ my $self = shift;
+ $_[0] =~ s/[\s\n]+$//;
+ if($_[0]) {
+ $self->{_commands_in_head}++;
+ $self->{_list_item_contents}++ if(defined $self->{_list_item_contents});
+ if(@{$self->{_list_stack}} && !$self->{_list_stack}->[0]->item()) {
+ $self->{_list_stack}->[0]->{_has_par} = 1;
+ }
+ }
+}
+
+# look for =foo commands at the start of a line within a paragraph, as for
+# instance the following which prints as "* one =item two".
+#
+# =item one
+# =item two
+#
+# Examples of =foo written in docs are expected to be indented in a verbatim
+# or marked up C<=foo> so won't be caught. A double-angle C<< =foo >> could
+# have the =foo at the start of a line, but that should be unlikely and is
+# easily enough dealt with by not putting a newline after the C<<.
+#
+sub _commands_in_paragraphs {
+ my ($self, $str, $pod_para) = @_;
+ while ($str =~ /[^\n]\n=([a-z][a-z0-9]+)/sg) {
+ my $cmd = $1;
+ my $pos = pos($str);
+ if ($VALID_COMMANDS{$cmd}) {
+ my ($file, $line) = $pod_para->file_line;
+ my $part = substr($str, 0, $pos);
+ $line += ($part =~ tr/\n//); # count of newlines
+
+ $self->poderror
+ ({ -line => $line, -file => $file,
+ -severity => 'ERROR',
+ -msg => "Apparent command =$cmd not preceded by blank line"});
+ }
+ }
+}
+
+1;
+
+__END__
+
+=head1 AUTHOR
+
+Please report bugs using L<http://rt.cpan.org>.
+
+Brad Appleton E<lt>bradapp@enteract.comE<gt> (initial version),
+Marek Rouchal E<lt>marekr@cpan.orgE<gt>
+
+Based on code for B<Pod::Text::pod2text()> written by
+Tom Christiansen E<lt>tchrist@mox.perl.comE<gt>
+
+B<Pod::Checker> is part of the Pod-Checker distribution, and is based on
+L<Pod::Parser>.
+
+=cut
+
diff --git a/cpan/Pod-Checker/scripts/podchecker.PL b/cpan/Pod-Checker/scripts/podchecker.PL
new file mode 100644
index 0000000000..2c33e8caad
--- /dev/null
+++ b/cpan/Pod-Checker/scripts/podchecker.PL
@@ -0,0 +1,186 @@
+#!/usr/local/bin/perl
+
+use Config;
+use File::Basename qw(&basename &dirname);
+use Cwd;
+
+# List explicitly here the variables you want Configure to
+# generate. Metaconfig only looks for shell variables, so you
+# have to mention them as if they were shell variables, not
+# %Config entries. Thus you write
+# $startperl
+# to ensure Configure will look for $Config{startperl}.
+
+# This forces PL files to create target in same directory as PL file.
+# This is so that make depend always knows where to find PL derivatives.
+$origdir = cwd;
+chdir(dirname($0));
+($file = basename($0)) =~ s/\.PL$//;
+$file =~ s/\.pl$//
+ if ($^O eq 'VMS' or $^O eq 'os2' or $^O eq 'dos'); # "case-forgiving"
+$file .= '.com' if $^O eq 'VMS';
+
+open OUT,">$file" or die "Can't create $file: $!";
+
+print "Extracting $file (with variable substitutions)\n";
+
+# In this section, perl variables will be expanded during extraction.
+# You can use $Config{...} to use Configure variables.
+
+print OUT <<"!GROK!THIS!";
+$Config{'startperl'}
+ eval 'exec perl -S \$0 "\$@"'
+ if 0;
+!GROK!THIS!
+
+# In the following, perl variables are not expanded during extraction.
+
+print OUT <<'!NO!SUBS!';
+#############################################################################
+# podchecker -- command to invoke the podchecker function in Pod::Checker
+#
+# Copyright (c) 1998-2000 by Bradford Appleton. All rights reserved.
+# This file is part of "PodParser". PodParser is free software;
+# you can redistribute it and/or modify it under the same terms
+# as Perl itself.
+#############################################################################
+
+use strict;
+#use diagnostics;
+
+=head1 NAME
+
+podchecker - check the syntax of POD format documentation files
+
+=head1 SYNOPSIS
+
+B<podchecker> [B<-help>] [B<-man>] [B<-(no)warnings>] [I<file>S< >...]
+
+=head1 OPTIONS AND ARGUMENTS
+
+=over 8
+
+=item B<-help>
+
+Print a brief help message and exit.
+
+=item B<-man>
+
+Print the manual page and exit.
+
+=item B<-warnings> B<-nowarnings>
+
+Turn on/off printing of warnings. Repeating B<-warnings> increases the
+warning level, i.e. more warnings are printed. Currently increasing to
+level two causes flagging of unescaped "E<lt>,E<gt>" characters.
+
+=item I<file>
+
+The pathname of a POD file to syntax-check (defaults to standard input).
+
+=back
+
+=head1 DESCRIPTION
+
+B<podchecker> will read the given input files looking for POD
+syntax errors in the POD documentation and will print any errors
+it find to STDERR. At the end, it will print a status message
+indicating the number of errors found.
+
+Directories are ignored, an appropriate warning message is printed.
+
+B<podchecker> invokes the B<podchecker()> function exported by B<Pod::Checker>
+Please see L<Pod::Checker/podchecker()> for more details.
+
+=head1 RETURN VALUE
+
+B<podchecker> returns a 0 (zero) exit status if all specified
+POD files are ok.
+
+=head1 ERRORS
+
+B<podchecker> returns the exit status 1 if at least one of
+the given POD files has syntax errors.
+
+The status 2 indicates that at least one of the specified
+files does not contain I<any> POD commands.
+
+Status 1 overrides status 2. If you want unambiguous
+results, call B<podchecker> with one single argument only.
+
+=head1 SEE ALSO
+
+L<Pod::Parser> and L<Pod::Checker>
+
+=head1 AUTHORS
+
+Please report bugs using L<http://rt.cpan.org>.
+
+Brad Appleton E<lt>bradapp@enteract.comE<gt>,
+Marek Rouchal E<lt>marekr@cpan.orgE<gt>
+
+Based on code for B<Pod::Text::pod2text(1)> written by
+Tom Christiansen E<lt>tchrist@mox.perl.comE<gt>
+
+=cut
+
+
+use Pod::Checker;
+use Pod::Usage;
+use Getopt::Long;
+
+## Define options
+my %options;
+
+## Parse options
+GetOptions(\%options, qw(help man warnings+ nowarnings)) || pod2usage(2);
+pod2usage(1) if ($options{help});
+pod2usage(-verbose => 2) if ($options{man});
+
+if($options{nowarnings}) {
+ $options{warnings} = 0;
+}
+elsif(!defined $options{warnings}) {
+ $options{warnings} = 1; # default is warnings on
+}
+
+## Dont default to STDIN if connected to a terminal
+pod2usage(2) if ((@ARGV == 0) && (-t STDIN));
+
+## Invoke podchecker()
+my $status = 0;
+@ARGV = qw(-) unless(@ARGV);
+for my $podfile (@ARGV) {
+ if($podfile eq '-') {
+ $podfile = '<&STDIN';
+ }
+ elsif(-d $podfile) {
+ warn "podchecker: Warning: Ignoring directory '$podfile'\n";
+ next;
+ }
+ my $errors =
+ podchecker($podfile, undef, '-warnings' => $options{warnings});
+ if($errors > 0) {
+ # errors occurred
+ $status = 1;
+ printf STDERR ("%s has %d pod syntax %s.\n",
+ $podfile, $errors,
+ ($errors == 1) ? 'error' : 'errors');
+ }
+ elsif($errors < 0) {
+ # no pod found
+ $status = 2 unless($status);
+ print STDERR "$podfile does not contain any pod commands.\n";
+ }
+ else {
+ print STDERR "$podfile pod syntax OK.\n";
+ }
+}
+exit $status;
+
+!NO!SUBS!
+
+close OUT or die "Can't close $file: $!";
+chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
+exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';
+chdir $origdir;
diff --git a/cpan/Pod-Checker/t/pod/contains_bad_pod.xr b/cpan/Pod-Checker/t/pod/contains_bad_pod.xr
new file mode 100644
index 0000000000..c7907963d9
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/contains_bad_pod.xr
@@ -0,0 +1,5 @@
+=head foo
+
+bar baz.
+
+=cut
diff --git a/cpan/Pod-Checker/t/pod/empty.xr b/cpan/Pod-Checker/t/pod/empty.xr
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/empty.xr
diff --git a/cpan/Pod-Checker/t/pod/podchkenc.t b/cpan/Pod-Checker/t/pod/podchkenc.t
new file mode 100644
index 0000000000..e7a5d7a14f
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/podchkenc.t
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+BEGIN {
+ use File::Basename;
+ my $THISDIR = dirname $0;
+ unshift @INC, $THISDIR;
+ require "testpchk.pl";
+ import TestPodChecker;
+}
+
+# this tests Pod::Checker accepts =encoding directive
+
+my %options = map { $_ => 1 } @ARGV; ## convert cmdline to options-hash
+my $passed = testpodchecker \%options, $0;
+exit( ($passed == 1) ? 0 : -1 ) unless $ENV{HARNESS_ACTIVE};
+
+__END__
+
+=encoding utf8
+
+=encode utf8
+
+dummy error
+
+=head1 An example.
+
+'Twas brillig, and the slithy toves did gyre and gimble in the wabe.
+
+=cut
+
diff --git a/cpan/Pod-Checker/t/pod/podchkenc.xr b/cpan/Pod-Checker/t/pod/podchkenc.xr
new file mode 100644
index 0000000000..8a21a1272a
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/podchkenc.xr
@@ -0,0 +1 @@
+*** ERROR: Unknown command 'encode' at line 20 in file t/pod/podchkenc.t
diff --git a/cpan/Pod-Checker/t/pod/poderrs.t b/cpan/Pod-Checker/t/pod/poderrs.t
new file mode 100644
index 0000000000..362cbb6575
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/poderrs.t
@@ -0,0 +1,241 @@
+BEGIN {
+ use File::Basename;
+ my $THISDIR = dirname $0;
+ unshift @INC, $THISDIR;
+ require "testpchk.pl";
+ import TestPodChecker;
+}
+
+my %options = map { $_ => 1 } @ARGV; ## convert cmdline to options-hash
+my $passed = testpodchecker \%options, $0;
+exit( ($passed == 1) ? 0 : -1 ) unless $ENV{HARNESS_ACTIVE};
+
+### Deliberately throw in some blank but non-empty lines
+
+### The above line should contain spaces
+
+
+__END__
+
+=head2 This should cause a warning
+
+=head1 NAME
+
+poderrors.t - test Pod::Checker on some pod syntax errors
+
+=unknown1 this is an unknown command with two N<unknownA>
+and D<unknownB> interior sequences.
+
+This is some paragraph text with some unknown interior sequences,
+such as Q<unknown2>,
+A<unknown3>,
+and Y<unknown4 V<unknown5>>.
+
+Now try some unterminated sequences like
+I<hello mudda!
+B<hello fadda!
+
+Here I am at C<camp granada!
+
+Camps is very,
+entertaining.
+And they say we'll have some fun if it stops raining!
+
+Okay, now use a non-empty blank line to terminate a paragraph and make
+sure we get a warning.
+
+The above blank line contains tabs and spaces only
+
+=head1 Additional tests
+
+=head2 item without over
+
+=item oops
+
+=head2 back without over
+
+=back
+
+=head2 over without back
+
+=over 4
+
+=item aaps
+
+=head2 end without begin
+
+=end
+
+=head2 begin and begin
+
+=begin html
+
+=begin text
+
+=end
+
+=end
+
+second one results in end w/o begin
+
+=head2 begin w/o formatter
+
+=begin
+
+=end
+
+=head2 for w/o formatter
+
+=for
+
+something...
+
+=head2 Nested sequences of the same type
+
+C<code I<italic C<code again!>>>
+
+=head2 Garbled entities
+
+E<alea iacta est>
+E<C<auml>>
+E<abcI<bla>>
+E<0x100>
+E<07777>
+E<300>
+
+=head2 Unresolved internal links
+
+L</"begin or begin">
+L<"end with begin">
+L</OoPs>
+
+=head2 Some links with problems
+
+L<abc
+def>
+L<>
+L< aha>
+L<oho >
+L<"Warnings"> this one is ok
+L</unescaped> ok too, this POD has an X of the same name
+L<http://www.perl.org> this is OK
+L<The Perl Home Page|http://www.perl.org> this is also OK
+
+=head2 Warnings
+
+L<passwd(5)>
+L<some text with / in it|perlvar/$|> should give warnings as hell
+
+=over 4
+
+=item bla
+
+=back 200
+
+the 200 is evil
+
+=begin html
+
+What?
+
+=end xml
+
+X<unescaped>see these unescaped < and > in the text?
+
+=head2 Misc
+
+Z<ddd> should be empty
+
+X<> should not be empty
+
+=over four
+
+This paragrapgh is misplaced - it ought to be an item.
+
+=item four should be numeric!
+
+=item
+
+=item blah
+
+=item previous is all empty!!!
+
+=back
+
+All empty over/back:
+
+=over 4
+
+=back
+
+item w/o name
+
+=cut
+
+=pod bla
+
+bla is evil
+
+=cut blub
+
+blub is evil
+
+=head2 reoccurence
+
+=over 4
+
+=item Misc
+
+we already have a head Misc
+
+=back
+
+=head2 some heading
+
+=head2 another one
+
+=head2 the next line should be empty
+=head2 ... but there is a command instead
+
+And here is some text
+=head2 again followed by a command
+
+ verbatim
+=item line missing
+
+previous section is empty!
+
+=head1 LINK TESTS
+
+Due to bug reported by Rafael Garcia-Suarez "rgarciasuarez@free.fr":
+
+The following hyperlinks :
+L<"I/O Operators">
+L<perlop/"I/O Operators">
+trigger a podchecker warning (using bleadperl) :
+ node 'I/O Operators' contains non-escaped | or /
+
+=cut
+
+=pod
+
+=head1 ON-OFF tests
+
+The above =pod is OK. The following =cut is ok, the one after not.
+
+=cut
+
+# some comment or code here, not POD
+
+=cut
+
+# more code
+
+=head2 This opens POD
+
+=pod
+
+And the =pod above is too much.
+
+=cut
+
diff --git a/cpan/Pod-Checker/t/pod/poderrs.xr b/cpan/Pod-Checker/t/pod/poderrs.xr
new file mode 100644
index 0000000000..c1a80c6478
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/poderrs.xr
@@ -0,0 +1,53 @@
+*** WARNING: =head2 without preceding higher level at line 20 in file t/pod/poderrs.t
+*** WARNING: empty section in previous paragraph at line 22 in file t/pod/poderrs.t
+*** ERROR: Unknown command 'unknown1' at line 26 in file t/pod/poderrs.t
+*** ERROR: Unknown interior-sequence 'Q' at line 30 in file t/pod/poderrs.t
+*** ERROR: Unknown interior-sequence 'A' at line 31 in file t/pod/poderrs.t
+*** ERROR: Unknown interior-sequence 'Y' at line 32 in file t/pod/poderrs.t
+*** ERROR: Unknown interior-sequence 'V' at line 32 in file t/pod/poderrs.t
+*** ERROR: unterminated B<...> at line 36 in file t/pod/poderrs.t
+*** ERROR: unterminated I<...> at line 35 in file t/pod/poderrs.t
+*** ERROR: unterminated C<...> at line 38 in file t/pod/poderrs.t
+*** WARNING: line containing nothing but whitespace in paragraph at line 46 in file t/pod/poderrs.t
+*** ERROR: =item without previous =over at line 53 in file t/pod/poderrs.t
+*** ERROR: =back without previous =over at line 57 in file t/pod/poderrs.t
+*** ERROR: =over on line 61 without closing =back (at head2) at line 65 in file t/pod/poderrs.t
+*** ERROR: =end without =begin at line 67 in file t/pod/poderrs.t
+*** ERROR: Nested =begin's (first at line 71:html) at line 73 in file t/pod/poderrs.t
+*** ERROR: =end without =begin at line 77 in file t/pod/poderrs.t
+*** ERROR: No argument for =begin at line 83 in file t/pod/poderrs.t
+*** ERROR: =for without formatter specification at line 89 in file t/pod/poderrs.t
+*** WARNING: nested commands C<...C<...>...> at line 95 in file t/pod/poderrs.t
+*** ERROR: garbled entity E<alea iacta est> at line 99 in file t/pod/poderrs.t
+*** ERROR: garbled entity E<C<auml>> at line 100 in file t/pod/poderrs.t
+*** ERROR: garbled entity E<abcI<bla>> at line 101 in file t/pod/poderrs.t
+*** ERROR: Entity number out of range E<0x100> at line 102 in file t/pod/poderrs.t
+*** ERROR: Entity number out of range E<07777> at line 103 in file t/pod/poderrs.t
+*** ERROR: Entity number out of range E<300> at line 104 in file t/pod/poderrs.t
+*** ERROR: malformed link L<> : empty link at line 116 in file t/pod/poderrs.t
+*** WARNING: ignoring leading whitespace in link at line 117 in file t/pod/poderrs.t
+*** WARNING: ignoring trailing whitespace in link at line 118 in file t/pod/poderrs.t
+*** WARNING: (section) in 'passwd(5)' deprecated at line 126 in file t/pod/poderrs.t
+*** WARNING: node '$|' contains non-escaped | or / at line 127 in file t/pod/poderrs.t
+*** WARNING: alternative text '$|' contains non-escaped | or / at line 127 in file t/pod/poderrs.t
+*** ERROR: Spurious character(s) after =back at line 133 in file t/pod/poderrs.t
+*** ERROR: Nonempty Z<> at line 147 in file t/pod/poderrs.t
+*** ERROR: Empty X<> at line 149 in file t/pod/poderrs.t
+*** WARNING: preceding non-item paragraph(s) at line 155 in file t/pod/poderrs.t
+*** WARNING: No argument for =item at line 157 in file t/pod/poderrs.t
+*** WARNING: previous =item has no contents at line 159 in file t/pod/poderrs.t
+*** WARNING: No items in =over (at line 167) / =back list at line 169 in file t/pod/poderrs.t
+*** ERROR: Spurious text after =pod at line 175 in file t/pod/poderrs.t
+*** ERROR: Spurious text after =cut at line 179 in file t/pod/poderrs.t
+*** WARNING: empty section in previous paragraph at line 195 in file t/pod/poderrs.t
+*** ERROR: Apparent command =head2 not preceded by blank line at line 198 in file t/pod/poderrs.t
+*** WARNING: empty section in previous paragraph at line 197 in file t/pod/poderrs.t
+*** ERROR: Apparent command =head2 not preceded by blank line at line 201 in file t/pod/poderrs.t
+*** ERROR: Apparent command =item not preceded by blank line at line 204 in file t/pod/poderrs.t
+*** ERROR: Spurious =cut command at line 230 in file t/pod/poderrs.t
+*** ERROR: Spurious =pod command at line 236 in file t/pod/poderrs.t
+*** ERROR: unresolved internal link 'begin or begin' at line 108 in file t/pod/poderrs.t
+*** ERROR: unresolved internal link 'end with begin' at line 109 in file t/pod/poderrs.t
+*** ERROR: unresolved internal link 'OoPs' at line 110 in file t/pod/poderrs.t
+*** ERROR: unresolved internal link 'abc def' at line 114 in file t/pod/poderrs.t
+*** ERROR: unresolved internal link 'I/O Operators' at line 213 in file t/pod/poderrs.t
diff --git a/cpan/Pod-Checker/t/pod/selfcheck.t b/cpan/Pod-Checker/t/pod/selfcheck.t
new file mode 100644
index 0000000000..3b6e352d3a
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/selfcheck.t
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+use File::Basename;
+use File::Spec;
+use strict;
+my $THISDIR;
+BEGIN {
+ $THISDIR = dirname $0;
+ unshift @INC, $THISDIR;
+ require "testpchk.pl";
+ import TestPodChecker qw(testpodcheck);
+}
+
+# test that our POD is correct!
+my $path = File::Spec->catfile($THISDIR,(File::Spec->updir()) x 2, 'lib', 'Pod', '*.pm');
+print "THISDIR=$THISDIR PATH=$path\n";
+my @pods = glob($path);
+print "PODS=@pods\n";
+
+print "1..",scalar(@pods),"\n";
+
+my $errs = 0;
+my $testnum = 1;
+foreach my $pod (@pods) {
+ my $out = File::Spec->catfile($THISDIR, basename($pod));
+ $out =~ s{\.pm}{.OUT};
+ my %options = ( -Out => $out );
+ my $failmsg = testpodcheck(-In => $pod, -Out => $out, -Cmp => "$THISDIR/empty.xr");
+ if($failmsg) {
+ if(open(IN, "<$out")) {
+ while(<IN>) {
+ warn "podchecker: $_";
+ }
+ close(IN);
+ } else {
+ warn "Error: Cannot read output file $out: $!\n";
+ }
+ print "not ok $testnum\n";
+ $errs++;
+ } else {
+ print "ok $testnum\n";
+ }
+ $testnum++;
+}
+exit( ($errs == 0) ? 0 : -1 ) unless $ENV{HARNESS_ACTIVE};
+
diff --git a/cpan/Pod-Checker/t/pod/testcmp.pl b/cpan/Pod-Checker/t/pod/testcmp.pl
new file mode 100644
index 0000000000..b8592fcc2a
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/testcmp.pl
@@ -0,0 +1,94 @@
+package TestCompare;
+
+use vars qw(@ISA @EXPORT $MYPKG);
+#use strict;
+#use diagnostics;
+use Carp;
+use Exporter;
+use File::Basename;
+use File::Spec;
+use FileHandle;
+
+@ISA = qw(Exporter);
+@EXPORT = qw(&testcmp);
+$MYPKG = eval { (caller)[0] };
+
+##--------------------------------------------------------------------------
+
+=head1 NAME
+
+testcmp -- compare two files line-by-line
+
+=head1 SYNOPSIS
+
+ $is_diff = testcmp($file1, $file2);
+
+or
+
+ $is_diff = testcmp({-cmplines => \&mycmp}, $file1, $file2);
+
+=head2 DESCRIPTION
+
+Compare two text files line-by-line and return 0 if they are the
+same, 1 if they differ. Each of $file1 and $file2 may be a filenames,
+or a filehandles (in which case it must already be open for reading).
+
+If the first argument is a hashref, then the B<-cmplines> key in the
+hash may have a subroutine reference as its corresponding value.
+The referenced user-defined subroutine should be a line-comparator
+function that takes two pre-chomped text-lines as its arguments
+(the first is from $file1 and the second is from $file2). It should
+return 0 if it considers the two lines equivalent, and non-zero
+otherwise.
+
+=cut
+
+##--------------------------------------------------------------------------
+
+sub testcmp( $ $ ; $) {
+ my %opts = ref($_[0]) eq 'HASH' ? %{shift()} : ();
+ my ($file1, $file2) = @_;
+ my ($fh1, $fh2) = ($file1, $file2);
+ unless (ref $fh1) {
+ $fh1 = FileHandle->new($file1, "r") or die "Can't open $file1: $!";
+ }
+ unless (ref $fh2) {
+ $fh2 = FileHandle->new($file2, "r") or die "Can't open $file2: $!";
+ }
+
+ my $cmplines = $opts{'-cmplines'} || undef;
+ my ($f1text, $f2text) = ("", "");
+ my ($line, $diffs) = (0, 0);
+
+ while ( defined($f1text) and defined($f2text) ) {
+ defined($f1text = <$fh1>) and chomp($f1text);
+ defined($f2text = <$fh2>) and chomp($f2text);
+ ++$line;
+ last unless ( defined($f1text) and defined($f2text) );
+ # kill any extra line endings
+ $f1text =~ s/[\r\n]+$//s;
+ $f2text =~ s/[\r\n]+$//s;
+ $diffs = (ref $cmplines) ? &$cmplines($f1text, $f2text)
+ : ($f1text ne $f2text);
+ last if $diffs;
+ }
+ close($fh1) unless (ref $file1);
+ close($fh2) unless (ref $file2);
+
+ $diffs = 1 if (defined($f1text) or defined($f2text));
+ if ( defined($f1text) and defined($f2text) ) {
+ ## these two lines must be different
+ warn "$file1 and $file2 differ at line $line\n";
+ }
+ elsif (defined($f1text) and (! defined($f1text))) {
+ ## file1 must be shorter
+ warn "$file1 is shorter than $file2\n";
+ }
+ elsif (defined $f2text) {
+ ## file2 must be longer
+ warn "$file1 is shorter than $file2\n";
+ }
+ return $diffs;
+}
+
+1;
diff --git a/cpan/Pod-Checker/t/pod/testpchk.pl b/cpan/Pod-Checker/t/pod/testpchk.pl
new file mode 100644
index 0000000000..0464a9a0fc
--- /dev/null
+++ b/cpan/Pod-Checker/t/pod/testpchk.pl
@@ -0,0 +1,130 @@
+package TestPodChecker;
+
+BEGIN {
+ use File::Basename;
+ use File::Spec;
+ push @INC, '..';
+ my $THISDIR = dirname $0;
+ unshift @INC, $THISDIR;
+ require "testcmp.pl";
+ import TestCompare;
+ my $PARENTDIR = dirname $THISDIR;
+ push @INC, map { File::Spec->catfile($_, 'lib') } ($PARENTDIR, $THISDIR);
+ require VMS::Filespec if $^O eq 'VMS';
+}
+
+use Pod::Checker;
+use vars qw(@ISA @EXPORT $MYPKG);
+#use strict;
+#use diagnostics;
+use Carp;
+use Exporter;
+#use File::Compare;
+
+@ISA = qw(Exporter);
+@EXPORT = qw(&testpodchecker);
+@EXPORT_OK = qw(&testpodcheck);
+$MYPKG = eval { (caller)[0] };
+
+sub stripname( $ ) {
+ local $_ = shift;
+ return /(\w[.\w]*)\s*$/ ? $1 : $_;
+}
+
+sub msgcmp( $ $ ) {
+ ## filter out platform-dependent aspects of error messages
+ my ($line1, $line2) = @_;
+ for ($line1, $line2) {
+ ## remove filenames from error messages to avoid any
+ ## filepath naming differences between OS platforms
+ s/(at line \S+ in file) .*\W(\w+\.[tT])\s*$/$1 \L$2\E/;
+ s/.*\W(\w+\.[tT]) (has \d+ pod syntax error)/\L$1\E $2/;
+ }
+ return ($line1 ne $line2);
+}
+
+sub testpodcheck( @ ) {
+ my %args = @_;
+ my $infile = $args{'-In'} || croak "No input file given!";
+ my $outfile = $args{'-Out'} || croak "No output file given!";
+ my $cmpfile = $args{'-Cmp'} || croak "No compare-result file given!";
+
+ my $different = '';
+ my $testname = basename $infile, '.t', '.xr';
+
+ unless (-e $cmpfile) {
+ my $msg = "*** Can't find comparison file $cmpfile for testing $infile";
+ warn "$msg\n";
+ return $msg;
+ }
+
+ print "# Running podchecker for '$testname'...\n";
+ ## Compare the output against the expected result
+ if ($^O eq 'VMS') {
+ for ($infile, $outfile, $cmpfile) {
+ $_ = VMS::Filespec::unixify($_) unless ref;
+ }
+ }
+ podchecker($infile, $outfile);
+ if ( testcmp({'-cmplines' => \&msgcmp}, $outfile, $cmpfile) ) {
+ $different = "$outfile is different from $cmpfile";
+ }
+ else {
+ unlink($outfile);
+ }
+ return $different;
+}
+
+sub testpodchecker( @ ) {
+ my %opts = (ref $_[0] eq 'HASH') ? %{shift()} : ();
+ my @testpods = @_;
+ my ($testname, $testdir) = ("", "");
+ my ($podfile, $cmpfile) = ("", "");
+ my ($outfile, $errfile) = ("", "");
+ my $passes = 0;
+ my $failed = 0;
+ local $_;
+
+ print "1..", scalar @testpods, "\n" unless ($opts{'-xrgen'});
+
+ for $podfile (@testpods) {
+ ($testname, $_) = fileparse($podfile);
+ $testdir ||= $_;
+ $testname =~ s/\.t$//;
+ $cmpfile = $testdir . $testname . '.xr';
+ $outfile = $testdir . $testname . '.OUT';
+
+ if ($opts{'-xrgen'}) {
+ if ($opts{'-force'} or ! -e $cmpfile) {
+ ## Create the comparison file
+ print "# Creating expected result for \"$testname\"" .
+ " podchecker test ...\n";
+ podchecker($podfile, $cmpfile);
+ }
+ else {
+ print "# File $cmpfile already exists" .
+ " (use '-force' to regenerate it).\n";
+ }
+ next;
+ }
+
+ my $failmsg = testpodcheck
+ -In => $podfile,
+ -Out => $outfile,
+ -Cmp => $cmpfile;
+ if ($failmsg) {
+ ++$failed;
+ print "#\tFAILED. ($failmsg)\n";
+ print "not ok ", $failed+$passes, "\n";
+ }
+ else {
+ ++$passes;
+ unlink($outfile);
+ print "#\tPASSED.\n";
+ print "ok ", $failed+$passes, "\n";
+ }
+ }
+ return $passes;
+}
+
+1;