xref: /openbsd-src/gnu/usr.bin/perl/cpan/Pod-Checker/lib/Pod/Checker.pm (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
191f110e0Safresh1#############################################################################
291f110e0Safresh1# Pod/Checker.pm -- check pod documents for syntax errors
391f110e0Safresh1#
491f110e0Safresh1# Copyright (C) 1994-2000 by Bradford Appleton. All rights reserved.
55759b3d2Safresh1# This is free software; you can redistribute it and/or modify it under the
65759b3d2Safresh1# same terms as Perl itself.
791f110e0Safresh1#############################################################################
891f110e0Safresh1
991f110e0Safresh1package Pod::Checker;
1091f110e0Safresh1use strict;
115759b3d2Safresh1use warnings;
1291f110e0Safresh1
13*5486feefSafresh1our $VERSION = '1.77';  ## Current version of this package
1491f110e0Safresh1
1591f110e0Safresh1=head1 NAME
1691f110e0Safresh1
175759b3d2Safresh1Pod::Checker - check pod documents for syntax errors
1891f110e0Safresh1
1991f110e0Safresh1=head1 SYNOPSIS
2091f110e0Safresh1
2191f110e0Safresh1  use Pod::Checker;
2291f110e0Safresh1
235759b3d2Safresh1  $syntax_okay = podchecker($filepath, $outputpath, %options);
2491f110e0Safresh1
255759b3d2Safresh1  my $checker = Pod::Checker->new(%options);
2691f110e0Safresh1  $checker->parse_from_file($filepath, \*STDERR);
2791f110e0Safresh1
2891f110e0Safresh1=head1 OPTIONS/ARGUMENTS
2991f110e0Safresh1
3091f110e0Safresh1C<$filepath> is the input POD to read and C<$outputpath> is
3191f110e0Safresh1where to write POD syntax error messages. Either argument may be a scalar
3291f110e0Safresh1indicating a file-path, or else a reference to an open filehandle.
3391f110e0Safresh1If unspecified, the input-file it defaults to C<\*STDIN>, and
3491f110e0Safresh1the output-file defaults to C<\*STDERR>.
3591f110e0Safresh1
3691f110e0Safresh1=head2 podchecker()
3791f110e0Safresh1
3891f110e0Safresh1This function can take a hash of options:
3991f110e0Safresh1
4091f110e0Safresh1=over 4
4191f110e0Safresh1
4291f110e0Safresh1=item B<-warnings> =E<gt> I<val>
4391f110e0Safresh1
4491f110e0Safresh1Turn warnings on/off. I<val> is usually 1 for on, but higher values
4591f110e0Safresh1trigger additional warnings. See L<"Warnings">.
4691f110e0Safresh1
475759b3d2Safresh1=item B<-quiet> =E<gt> I<val>
485759b3d2Safresh1
495759b3d2Safresh1If C<val> is true, do not print any errors/warnings.
505759b3d2Safresh1
5191f110e0Safresh1=back
5291f110e0Safresh1
5391f110e0Safresh1=head1 DESCRIPTION
5491f110e0Safresh1
5591f110e0Safresh1B<podchecker> will perform syntax checking of Perl5 POD format documentation.
5691f110e0Safresh1
5791f110e0Safresh1Curious/ambitious users are welcome to propose additional features they wish
5891f110e0Safresh1to see in B<Pod::Checker> and B<podchecker> and verify that the checks are
5991f110e0Safresh1consistent with L<perlpod>.
6091f110e0Safresh1
6191f110e0Safresh1The following checks are currently performed:
6291f110e0Safresh1
6391f110e0Safresh1=over 4
6491f110e0Safresh1
6591f110e0Safresh1=item *
6691f110e0Safresh1
6791f110e0Safresh1Unknown '=xxxx' commands, unknown 'XE<lt>...E<gt>' interior-sequences,
6891f110e0Safresh1and unterminated interior sequences.
6991f110e0Safresh1
7091f110e0Safresh1=item *
7191f110e0Safresh1
7291f110e0Safresh1Check for proper balancing of C<=begin> and C<=end>. The contents of such
7391f110e0Safresh1a block are generally ignored, i.e. no syntax checks are performed.
7491f110e0Safresh1
7591f110e0Safresh1=item *
7691f110e0Safresh1
7791f110e0Safresh1Check for proper nesting and balancing of C<=over>, C<=item> and C<=back>.
7891f110e0Safresh1
7991f110e0Safresh1=item *
8091f110e0Safresh1
8191f110e0Safresh1Check for same nested interior-sequences (e.g.
8291f110e0Safresh1C<LE<lt>...LE<lt>...E<gt>...E<gt>>).
8391f110e0Safresh1
8491f110e0Safresh1=item *
8591f110e0Safresh1
8691f110e0Safresh1Check for malformed or non-existing entities C<EE<lt>...E<gt>>.
8791f110e0Safresh1
8891f110e0Safresh1=item *
8991f110e0Safresh1
9091f110e0Safresh1Check for correct syntax of hyperlinks C<LE<lt>...E<gt>>. See L<perlpod>
9191f110e0Safresh1for details.
9291f110e0Safresh1
9391f110e0Safresh1=item *
9491f110e0Safresh1
9591f110e0Safresh1Check for unresolved document-internal links. This check may also reveal
9691f110e0Safresh1misspelled links that seem to be internal links but should be links
9791f110e0Safresh1to something else.
9891f110e0Safresh1
9991f110e0Safresh1=back
10091f110e0Safresh1
10191f110e0Safresh1=head1 DIAGNOSTICS
10291f110e0Safresh1
10391f110e0Safresh1=head2 Errors
10491f110e0Safresh1
10591f110e0Safresh1=over 4
10691f110e0Safresh1
10791f110e0Safresh1=item * empty =headn
10891f110e0Safresh1
10991f110e0Safresh1A heading (C<=head1> or C<=head2>) without any text? That ain't no
11091f110e0Safresh1heading!
11191f110e0Safresh1
11291f110e0Safresh1=item * =over on line I<N> without closing =back
11391f110e0Safresh1
1145759b3d2Safresh1=item * You forgot a '=back' before '=headI<N>'
1155759b3d2Safresh1
1165759b3d2Safresh1=item * =over is the last thing in the document?!
1175759b3d2Safresh1
11891f110e0Safresh1The C<=over> command does not have a corresponding C<=back> before the
11991f110e0Safresh1next heading (C<=head1> or C<=head2>) or the end of the file.
12091f110e0Safresh1
1215759b3d2Safresh1=item * '=item' outside of any '=over'
12291f110e0Safresh1
1235759b3d2Safresh1=item * =back without =over
12491f110e0Safresh1
12591f110e0Safresh1An C<=item> or C<=back> command has been found outside a
12691f110e0Safresh1C<=over>/C<=back> block.
12791f110e0Safresh1
1285759b3d2Safresh1=item * Can't have a 0 in =over I<N>
1295759b3d2Safresh1
1305759b3d2Safresh1You need to indent a strictly positive number of spaces, not 0.
1315759b3d2Safresh1
1325759b3d2Safresh1=item * =over should be: '=over' or '=over positive_number'
1335759b3d2Safresh1
1345759b3d2Safresh1Either have an argumentless =over, or have its argument a strictly positive number.
1355759b3d2Safresh1
1365759b3d2Safresh1=item * =begin I<TARGET> without matching =end I<TARGET>
1375759b3d2Safresh1
1385759b3d2Safresh1A C<=begin> command was found that has no matching =end command.
1395759b3d2Safresh1
1405759b3d2Safresh1=item * =begin without a target?
14191f110e0Safresh1
14291f110e0Safresh1A C<=begin> command was found that is not followed by the formatter
14391f110e0Safresh1specification.
14491f110e0Safresh1
1455759b3d2Safresh1=item * =end I<TARGET> without matching =begin.
14691f110e0Safresh1
14791f110e0Safresh1A standalone C<=end> command was found.
14891f110e0Safresh1
1495759b3d2Safresh1=item * '=end' without a target?
15091f110e0Safresh1
1515759b3d2Safresh1'=end' directives need to have a target, just like =begin directives.
15291f110e0Safresh1
1535759b3d2Safresh1=item * '=end I<TARGET>' is invalid.
1545759b3d2Safresh1
1555759b3d2Safresh1I<TARGET> needs to be one word
1565759b3d2Safresh1
1575759b3d2Safresh1=item * =end I<CONTENT> doesn't match =begin I<TARGET>
1585759b3d2Safresh1
1595759b3d2Safresh1I<CONTENT> needs to match =begin's I<TARGET>.
1605759b3d2Safresh1
1615759b3d2Safresh1=item * =for without a target?
16291f110e0Safresh1
16391f110e0Safresh1There is no specification of the formatter after the C<=for> command.
16491f110e0Safresh1
16591f110e0Safresh1=item * unresolved internal link I<NAME>
16691f110e0Safresh1
16791f110e0Safresh1The given link to I<NAME> does not have a matching node in the current
16891f110e0Safresh1POD. This also happened when a single word node name is not enclosed in
16991f110e0Safresh1C<"">.
17091f110e0Safresh1
1715759b3d2Safresh1=item * Unknown directive: I<CMD>
17291f110e0Safresh1
17391f110e0Safresh1An invalid POD command has been found. Valid are C<=head1>, C<=head2>,
17491f110e0Safresh1C<=head3>, C<=head4>, C<=over>, C<=item>, C<=back>, C<=begin>, C<=end>,
17591f110e0Safresh1C<=for>, C<=pod>, C<=cut>
17691f110e0Safresh1
1775759b3d2Safresh1=item * Deleting unknown formatting code I<SEQ>
17891f110e0Safresh1
17991f110e0Safresh1An invalid markup command has been encountered. Valid are:
18091f110e0Safresh1C<BE<lt>E<gt>>, C<CE<lt>E<gt>>, C<EE<lt>E<gt>>, C<FE<lt>E<gt>>,
18191f110e0Safresh1C<IE<lt>E<gt>>, C<LE<lt>E<gt>>, C<SE<lt>E<gt>>, C<XE<lt>E<gt>>,
18291f110e0Safresh1C<ZE<lt>E<gt>>
18391f110e0Safresh1
1845759b3d2Safresh1=item * Unterminated I<SEQ>E<lt>E<gt> sequence
18591f110e0Safresh1
1865759b3d2Safresh1An unclosed formatting code
18791f110e0Safresh1
1885759b3d2Safresh1=item * An EE<lt>...E<gt> surrounding strange content
18991f110e0Safresh1
19091f110e0Safresh1The I<STRING> found cannot be interpreted as a character entity.
19191f110e0Safresh1
1925759b3d2Safresh1=item * An empty EE<lt>E<gt>
19391f110e0Safresh1
1945759b3d2Safresh1=item * An empty C<< LE<lt>E<gt> >>
19591f110e0Safresh1
1965759b3d2Safresh1=item * An empty XE<lt>E<gt>
19791f110e0Safresh1
1985759b3d2Safresh1There needs to be content inside E, L, and X formatting codes.
19991f110e0Safresh1
20091f110e0Safresh1=item * Spurious text after =pod / =cut
20191f110e0Safresh1
20291f110e0Safresh1The commands C<=pod> and C<=cut> do not take any arguments.
20391f110e0Safresh1
2045759b3d2Safresh1=item * =back doesn't take any parameters, but you said =back I<ARGUMENT>
20591f110e0Safresh1
20691f110e0Safresh1The C<=back> command does not take any arguments.
20791f110e0Safresh1
2085759b3d2Safresh1=item * =pod directives shouldn't be over one line long!  Ignoring all I<N> lines of content
2095759b3d2Safresh1
2105759b3d2Safresh1Self explanatory
2115759b3d2Safresh1
2125759b3d2Safresh1=item * =cut found outside a pod block.
2135759b3d2Safresh1
2145759b3d2Safresh1A '=cut' directive found in the middle of non-POD
2155759b3d2Safresh1
2165759b3d2Safresh1=item * Invalid =encoding syntax: I<CONTENT>
2175759b3d2Safresh1
2185759b3d2Safresh1Syntax error in =encoding directive
2195759b3d2Safresh1
22091f110e0Safresh1=back
22191f110e0Safresh1
22291f110e0Safresh1=head2 Warnings
22391f110e0Safresh1
22491f110e0Safresh1These may not necessarily cause trouble, but indicate mediocre style.
22591f110e0Safresh1
22691f110e0Safresh1=over 4
22791f110e0Safresh1
2285759b3d2Safresh1=item * nested commands I<CMD>E<lt>...I<CMD>E<lt>...E<gt>...E<gt>
2295759b3d2Safresh1
2305759b3d2Safresh1Two nested identical markup commands have been found. Generally this
2315759b3d2Safresh1does not make sense.
2325759b3d2Safresh1
2335759b3d2Safresh1=item * multiple occurrences (I<N>) of link target I<name>
23491f110e0Safresh1
23591f110e0Safresh1The POD file has some C<=item> and/or C<=head> commands that have
23691f110e0Safresh1the same text. Potential hyperlinks to such a text cannot be unique then.
23791f110e0Safresh1This warning is printed only with warning level greater than one.
23891f110e0Safresh1
23991f110e0Safresh1=item * line containing nothing but whitespace in paragraph
24091f110e0Safresh1
24191f110e0Safresh1There is some whitespace on a seemingly empty line. POD is very sensitive
24291f110e0Safresh1to such things, so this is flagged. B<vi> users switch on the B<list>
24391f110e0Safresh1option to avoid this problem.
24491f110e0Safresh1
2455759b3d2Safresh1=item * =item has no contents
24691f110e0Safresh1
2475759b3d2Safresh1There is a list C<=item> that has no text contents. You probably want to delete
2485759b3d2Safresh1empty items.
24991f110e0Safresh1
2505759b3d2Safresh1=item * You can't have =items (as at line I<N>) unless the first thing after the =over is an =item
25191f110e0Safresh1
25291f110e0Safresh1A list introduced by C<=over> starts with a text or verbatim paragraph,
25391f110e0Safresh1but continues with C<=item>s. Move the non-item paragraph out of the
25491f110e0Safresh1C<=over>/C<=back> block.
25591f110e0Safresh1
2565759b3d2Safresh1=item * Expected '=item I<EXPECTED VALUE>'
2575759b3d2Safresh1
2585759b3d2Safresh1=item * Expected '=item *'
2595759b3d2Safresh1
2605759b3d2Safresh1=item * Possible =item type mismatch: 'I<x>' found leading a supposed definition =item
26191f110e0Safresh1
26291f110e0Safresh1A list started with e.g. a bullet-like C<=item> and continued with a
26391f110e0Safresh1numbered one. This is obviously inconsistent. For most translators the
26491f110e0Safresh1type of the I<first> C<=item> determines the type of the list.
26591f110e0Safresh1
2665759b3d2Safresh1=item * You have '=item x' instead of the expected '=item I<N>'
26791f110e0Safresh1
2685759b3d2Safresh1Erroneous numbering of =item numbers; they need to ascend consecutively.
26991f110e0Safresh1
2705759b3d2Safresh1=item * Unknown E content in EE<lt>I<CONTENT>E<gt>
27191f110e0Safresh1
27291f110e0Safresh1A character entity was found that does not belong to the standard
2735759b3d2Safresh1ISO set or the POD specials C<verbar> and C<sol>. I<Currently, this warning
2745759b3d2Safresh1only appears if a character entity was found that does not have a Unicode
2755759b3d2Safresh1character. This should be fixed to adhere to the original warning.>
27691f110e0Safresh1
2775759b3d2Safresh1=item * empty =over/=back block
27891f110e0Safresh1
2795759b3d2Safresh1The list opened with C<=over> does not contain anything.
28091f110e0Safresh1
28191f110e0Safresh1=item * empty section in previous paragraph
28291f110e0Safresh1
28391f110e0Safresh1The previous section (introduced by a C<=head> command) does not contain
2845759b3d2Safresh1any valid content. This usually indicates that something is missing. Note: A
28591f110e0Safresh1C<=head1> followed immediately by C<=head2> does not trigger this warning.
28691f110e0Safresh1
28791f110e0Safresh1=item * Verbatim paragraph in NAME section
28891f110e0Safresh1
28991f110e0Safresh1The NAME section (C<=head1 NAME>) should consist of a single paragraph
29091f110e0Safresh1with the script/module name, followed by a dash `-' and a very short
29191f110e0Safresh1description of what the thing is good for.
29291f110e0Safresh1
29391f110e0Safresh1=item * =headI<n> without preceding higher level
29491f110e0Safresh1
29591f110e0Safresh1For example if there is a C<=head2> in the POD file prior to a
29691f110e0Safresh1C<=head1>.
29791f110e0Safresh1
298256a93a4Safresh1=item * A non-empty ZE<lt>E<gt>
299256a93a4Safresh1
300256a93a4Safresh1The C<ZE<lt>E<gt>> sequence is supposed to be empty. Caveat: this issue is
301256a93a4Safresh1detected in L<Pod::Simple> and will be flagged as an I<ERROR> by any client
302256a93a4Safresh1code; any contents of C<ZE<lt>...E<gt>> will be disregarded, anyway.
303256a93a4Safresh1
30491f110e0Safresh1=back
30591f110e0Safresh1
30691f110e0Safresh1=head2 Hyperlinks
30791f110e0Safresh1
30891f110e0Safresh1There are some warnings with respect to malformed hyperlinks:
30991f110e0Safresh1
31091f110e0Safresh1=over 4
31191f110e0Safresh1
31291f110e0Safresh1=item * ignoring leading/trailing whitespace in link
31391f110e0Safresh1
31491f110e0Safresh1There is whitespace at the beginning or the end of the contents of
31591f110e0Safresh1LE<lt>...E<gt>.
31691f110e0Safresh1
31791f110e0Safresh1=item * alternative text/node '%s' contains non-escaped | or /
31891f110e0Safresh1
31991f110e0Safresh1The characters C<|> and C</> are special in the LE<lt>...E<gt> context.
32091f110e0Safresh1Although the hyperlink parser does its best to determine which "/" is
32191f110e0Safresh1text and which is a delimiter in case of doubt, one ought to escape
32291f110e0Safresh1these literal characters like this:
32391f110e0Safresh1
32491f110e0Safresh1  /     E<sol>
32591f110e0Safresh1  |     E<verbar>
32691f110e0Safresh1
32791f110e0Safresh1=back
32891f110e0Safresh1
3295759b3d2Safresh1Note that the line number of the error/warning may refer to the line number of
3305759b3d2Safresh1the start of the paragraph in which the error/warning exists, not the line
3315759b3d2Safresh1number that the error/warning is on. This bug is present in errors/warnings
3325759b3d2Safresh1related to formatting codes. I<This should be fixed.>
3335759b3d2Safresh1
33491f110e0Safresh1=head1 RETURN VALUE
33591f110e0Safresh1
33691f110e0Safresh1B<podchecker> returns the number of POD syntax errors found or -1 if
33791f110e0Safresh1there were no POD commands at all found in the file.
33891f110e0Safresh1
33991f110e0Safresh1=head1 EXAMPLES
34091f110e0Safresh1
34191f110e0Safresh1See L</SYNOPSIS>
34291f110e0Safresh1
3435759b3d2Safresh1=head1 SCRIPTS
3445759b3d2Safresh1
3455759b3d2Safresh1The B<podchecker> script that comes with this distribution is a lean wrapper
3465759b3d2Safresh1around this module. See the online manual with
3475759b3d2Safresh1
3485759b3d2Safresh1  podchecker -help
3495759b3d2Safresh1  podchecker -man
3505759b3d2Safresh1
35191f110e0Safresh1=head1 INTERFACE
35291f110e0Safresh1
35391f110e0Safresh1While checking, this module collects document properties, e.g. the nodes
35491f110e0Safresh1for hyperlinks (C<=headX>, C<=item>) and index entries (C<XE<lt>E<gt>>).
35591f110e0Safresh1POD translators can use this feature to syntax-check and get the nodes in
35691f110e0Safresh1a first pass before actually starting to convert. This is expensive in terms
35791f110e0Safresh1of execution time, but allows for very robust conversions.
35891f110e0Safresh1
3595759b3d2Safresh1Since v1.24 the B<Pod::Checker> module uses only the B<poderror>
36091f110e0Safresh1method to print errors and warnings. The summary output (e.g.
36191f110e0Safresh1"Pod syntax OK") has been dropped from the module and has been included in
36291f110e0Safresh1B<podchecker> (the script). This allows users of B<Pod::Checker> to
36391f110e0Safresh1control completely the output behavior. Users of B<podchecker> (the script)
36491f110e0Safresh1get the well-known behavior.
36591f110e0Safresh1
366256a93a4Safresh1v1.45 inherits from L<Pod::Simple> as opposed to all previous versions
3675759b3d2Safresh1inheriting from Pod::Parser. Do B<not> use Pod::Simple's interface when
3685759b3d2Safresh1using Pod::Checker unless it is documented somewhere on this page. I
3695759b3d2Safresh1repeat, DO B<NOT> USE POD::SIMPLE'S INTERFACE.
3705759b3d2Safresh1
371256a93a4Safresh1The following list documents the overrides to Pod::Simple, primarily to
372256a93a4Safresh1make L<Pod::Coverage> happy:
373256a93a4Safresh1
374256a93a4Safresh1=over 4
375256a93a4Safresh1
376256a93a4Safresh1=item end_B
377256a93a4Safresh1
378256a93a4Safresh1=item end_C
379256a93a4Safresh1
380256a93a4Safresh1=item end_Document
381256a93a4Safresh1
382256a93a4Safresh1=item end_F
383256a93a4Safresh1
384256a93a4Safresh1=item end_I
385256a93a4Safresh1
386256a93a4Safresh1=item end_L
387256a93a4Safresh1
388256a93a4Safresh1=item end_Para
389256a93a4Safresh1
390256a93a4Safresh1=item end_S
391256a93a4Safresh1
392256a93a4Safresh1=item end_X
393256a93a4Safresh1
394256a93a4Safresh1=item end_fcode
395256a93a4Safresh1
396256a93a4Safresh1=item end_for
397256a93a4Safresh1
398256a93a4Safresh1=item end_head
399256a93a4Safresh1
400256a93a4Safresh1=item end_head1
401256a93a4Safresh1
402256a93a4Safresh1=item end_head2
403256a93a4Safresh1
404256a93a4Safresh1=item end_head3
405256a93a4Safresh1
406256a93a4Safresh1=item end_head4
407256a93a4Safresh1
408256a93a4Safresh1=item end_item
409256a93a4Safresh1
410256a93a4Safresh1=item end_item_bullet
411256a93a4Safresh1
412256a93a4Safresh1=item end_item_number
413256a93a4Safresh1
414256a93a4Safresh1=item end_item_text
415256a93a4Safresh1
416256a93a4Safresh1=item handle_pod_and_cut
417256a93a4Safresh1
418256a93a4Safresh1=item handle_text
419256a93a4Safresh1
420256a93a4Safresh1=item handle_whiteline
421256a93a4Safresh1
422256a93a4Safresh1=item hyperlink
423256a93a4Safresh1
424256a93a4Safresh1=item scream
425256a93a4Safresh1
426256a93a4Safresh1=item start_B
427256a93a4Safresh1
428256a93a4Safresh1=item start_C
429256a93a4Safresh1
430256a93a4Safresh1=item start_Data
431256a93a4Safresh1
432256a93a4Safresh1=item start_F
433256a93a4Safresh1
434256a93a4Safresh1=item start_I
435256a93a4Safresh1
436256a93a4Safresh1=item start_L
437256a93a4Safresh1
438256a93a4Safresh1=item start_Para
439256a93a4Safresh1
440256a93a4Safresh1=item start_S
441256a93a4Safresh1
442256a93a4Safresh1=item start_Verbatim
443256a93a4Safresh1
444256a93a4Safresh1=item start_X
445256a93a4Safresh1
446256a93a4Safresh1=item start_fcode
447256a93a4Safresh1
448256a93a4Safresh1=item start_for
449256a93a4Safresh1
450256a93a4Safresh1=item start_head
451256a93a4Safresh1
452256a93a4Safresh1=item start_head1
453256a93a4Safresh1
454256a93a4Safresh1=item start_head2
455256a93a4Safresh1
456256a93a4Safresh1=item start_head3
457256a93a4Safresh1
458256a93a4Safresh1=item start_head4
459256a93a4Safresh1
460256a93a4Safresh1=item start_item_bullet
461256a93a4Safresh1
462256a93a4Safresh1=item start_item_number
463256a93a4Safresh1
464256a93a4Safresh1=item start_item_text
465256a93a4Safresh1
466256a93a4Safresh1=item start_over
467256a93a4Safresh1
468256a93a4Safresh1=item start_over_block
469256a93a4Safresh1
470256a93a4Safresh1=item start_over_bullet
471256a93a4Safresh1
472256a93a4Safresh1=item start_over_empty
473256a93a4Safresh1
474256a93a4Safresh1=item start_over_number
475256a93a4Safresh1
476256a93a4Safresh1=item start_over_text
477256a93a4Safresh1
478256a93a4Safresh1=item whine
479256a93a4Safresh1
480256a93a4Safresh1=back
481256a93a4Safresh1
48291f110e0Safresh1=cut
48391f110e0Safresh1
48491f110e0Safresh1#############################################################################
48591f110e0Safresh1
48691f110e0Safresh1#use diagnostics;
48791f110e0Safresh1use Carp qw(croak);
4885759b3d2Safresh1use Exporter 'import';
4895759b3d2Safresh1use base qw/Pod::Simple::Methody/;
49091f110e0Safresh1
4915759b3d2Safresh1our @EXPORT = qw(&podchecker);
49291f110e0Safresh1
49391f110e0Safresh1##---------------------------------
49491f110e0Safresh1## Function definitions begin here
49591f110e0Safresh1##---------------------------------
49691f110e0Safresh1
49791f110e0Safresh1sub podchecker {
49891f110e0Safresh1    my ($infile, $outfile, %options) = @_;
49991f110e0Safresh1    local $_;
50091f110e0Safresh1
50191f110e0Safresh1    ## Set defaults
50291f110e0Safresh1    $infile  ||= \*STDIN;
50391f110e0Safresh1    $outfile ||= \*STDERR;
50491f110e0Safresh1
50591f110e0Safresh1    ## Now create a pod checker
5065759b3d2Safresh1    my $checker = Pod::Checker->new(%options);
50791f110e0Safresh1
50891f110e0Safresh1    ## Now check the pod document for errors
50991f110e0Safresh1    $checker->parse_from_file($infile, $outfile);
51091f110e0Safresh1
51191f110e0Safresh1    ## Return the number of errors found
51291f110e0Safresh1    return $checker->num_errors();
51391f110e0Safresh1}
51491f110e0Safresh1
5155759b3d2Safresh1
51691f110e0Safresh1##---------------------------------------------------------------------------
51791f110e0Safresh1
51891f110e0Safresh1##-------------------------------
51991f110e0Safresh1## Method definitions begin here
52091f110e0Safresh1##-------------------------------
52191f110e0Safresh1
52291f110e0Safresh1##################################
52391f110e0Safresh1
52491f110e0Safresh1=over 4
52591f110e0Safresh1
52691f110e0Safresh1=item C<Pod::Checker-E<gt>new( %options )>
52791f110e0Safresh1
52891f110e0Safresh1Return a reference to a new Pod::Checker object that inherits from
5295759b3d2Safresh1Pod::Simple and is used for calling the required methods later. The
53091f110e0Safresh1following options are recognized:
53191f110e0Safresh1
53291f110e0Safresh1C<-warnings =E<gt> num>
53391f110e0Safresh1  Print warnings if C<num> is true. The higher the value of C<num>,
53491f110e0Safresh1the more warnings are printed. Currently there are only levels 1 and 2.
53591f110e0Safresh1
53691f110e0Safresh1C<-quiet =E<gt> num>
53791f110e0Safresh1  If C<num> is true, do not print any errors/warnings. This is useful
53891f110e0Safresh1when Pod::Checker is used to munge POD code into plain text from within
53991f110e0Safresh1POD formatters.
54091f110e0Safresh1
54191f110e0Safresh1=cut
54291f110e0Safresh1
5435759b3d2Safresh1sub new {
5445759b3d2Safresh1    my $new = shift->SUPER::new(@_);
5455759b3d2Safresh1    $new->{'output_fh'} ||= *STDERR{IO};
54691f110e0Safresh1
5475759b3d2Safresh1    # Set options
5485759b3d2Safresh1    my %opts = @_;
5495759b3d2Safresh1    $new->{'-warnings'} = defined $opts{'-warnings'} ?
5505759b3d2Safresh1                                  $opts{'-warnings'} : 1; # default on
5515759b3d2Safresh1    $new->{'-quiet'} = $opts{'-quiet'} || 0; # default off
5525759b3d2Safresh1
5535759b3d2Safresh1    # Initialize number of errors/warnings
5545759b3d2Safresh1    $new->{'_NUM_ERRORS'} = 0;
5555759b3d2Safresh1    $new->{'_NUM_WARNINGS'} = 0;
5565759b3d2Safresh1
5575759b3d2Safresh1    # 'current' also means 'most recent' in the follow comments
5585759b3d2Safresh1    $new->{'_thispara'} = '';       # current POD paragraph
5595759b3d2Safresh1    $new->{'_line'} = 0;            # current line number
5605759b3d2Safresh1    $new->{'_head_num'} = 0;        # current =head level (set to 0 to make
5615759b3d2Safresh1                                    #   logic easier down the road)
5625759b3d2Safresh1    $new->{'_cmds_since_head'} = 0; # num of POD directives since prev. =headN
5635759b3d2Safresh1    $new->{'_nodes'} = [];          # stack for =head/=item nodes
5645759b3d2Safresh1    $new->{'_fcode_stack'} = [];    # stack for nested formatting codes
5655759b3d2Safresh1    $new->{'_fcode_pos'} = [];      # stack for position in paragraph of fcodes
5665759b3d2Safresh1    $new->{'_begin_stack'} = [];    # stack for =begins: [line #, target]
5675759b3d2Safresh1    $new->{'_links'} = [];          # stack for hyperlinks to external entities
5685759b3d2Safresh1    $new->{'_internal_links'} = []; # set of linked-to internal sections
5695759b3d2Safresh1    $new->{'_index'} = [];          # stack for text in X<>s
5705759b3d2Safresh1
5715759b3d2Safresh1    $new->accept_targets('*'); # check all =begin/=for blocks
5725759b3d2Safresh1    $new->cut_handler( \&handle_pod_and_cut ); # warn if text after =cut
5735759b3d2Safresh1    $new->pod_handler( \&handle_pod_and_cut ); # warn if text after =pod
5745759b3d2Safresh1    $new->whiteline_handler( \&handle_whiteline ); # warn if whiteline
5755759b3d2Safresh1    $new->parse_empty_lists(1); # warn if they are empty
5765759b3d2Safresh1
5775759b3d2Safresh1    return $new;
57891f110e0Safresh1}
57991f110e0Safresh1
58091f110e0Safresh1##################################
58191f110e0Safresh1
58291f110e0Safresh1=item C<$checker-E<gt>poderror( @args )>
58391f110e0Safresh1
58491f110e0Safresh1=item C<$checker-E<gt>poderror( {%opts}, @args )>
58591f110e0Safresh1
5865759b3d2Safresh1Internal method for printing errors and warnings. If no options are given,
5875759b3d2Safresh1simply prints "@_". The following options are recognized and used to form
5885759b3d2Safresh1the output:
58991f110e0Safresh1
59091f110e0Safresh1  -msg
59191f110e0Safresh1
59291f110e0Safresh1A message to print prior to C<@args>.
59391f110e0Safresh1
59491f110e0Safresh1  -line
59591f110e0Safresh1
59691f110e0Safresh1The line number the error occurred in.
59791f110e0Safresh1
59891f110e0Safresh1  -file
59991f110e0Safresh1
6005759b3d2Safresh1The file (name) the error occurred in. Defaults to the name of the current
6015759b3d2Safresh1file being processed.
60291f110e0Safresh1
60391f110e0Safresh1  -severity
60491f110e0Safresh1
60591f110e0Safresh1The error level, should be 'WARNING' or 'ERROR'.
60691f110e0Safresh1
60791f110e0Safresh1=cut
60891f110e0Safresh1
60991f110e0Safresh1# Invoked as $self->poderror( @args ), or $self->poderror( {%opts}, @args )
61091f110e0Safresh1sub poderror {
61191f110e0Safresh1    my $self = shift;
61291f110e0Safresh1    my %opts = (ref $_[0]) ? %{shift()} : ();
61391f110e0Safresh1
61491f110e0Safresh1    ## Retrieve options
6155759b3d2Safresh1    chomp( my $msg  = ($opts{'-msg'} || '')."@_" );
6165759b3d2Safresh1    my $line = (exists $opts{'-line'}) ? " at line $opts{'-line'}" : '';
6175759b3d2Safresh1    my $file = ' in file ' . ((exists $opts{'-file'})
6185759b3d2Safresh1                              ? $opts{'-file'}
6195759b3d2Safresh1                              : ((defined $self->source_filename)
6205759b3d2Safresh1                                 ? $self->source_filename
6215759b3d2Safresh1                                 : "???"));
6225759b3d2Safresh1    unless (exists $opts{'-severity'}) {
62391f110e0Safresh1       ## See if can find severity in message prefix
6245759b3d2Safresh1       $opts{'-severity'} = $1  if ( $msg =~ s/^\**\s*([A-Z]{3,}):\s+// );
62591f110e0Safresh1    }
6265759b3d2Safresh1    my $severity = (exists $opts{'-severity'}) ? "*** $opts{-severity}: " : '';
62791f110e0Safresh1
62891f110e0Safresh1    ## Increment error count and print message "
6295759b3d2Safresh1    ++($self->{'_NUM_ERRORS'})
6305759b3d2Safresh1        if(!%opts || ($opts{-severity} && $opts{'-severity'} eq 'ERROR'));
6315759b3d2Safresh1    ++($self->{'_NUM_WARNINGS'})
6325759b3d2Safresh1        if(!%opts || ($opts{-severity} && $opts{'-severity'} eq 'WARNING'));
6335759b3d2Safresh1    unless($self->{'-quiet'}) {
6345759b3d2Safresh1      my $out_fh = $self->{'output_fh'} || \*STDERR;
63591f110e0Safresh1      print $out_fh ($severity, $msg, $line, $file, "\n")
6365759b3d2Safresh1        if($self->{'-warnings'} || !%opts || $opts{'-severity'} ne 'WARNING');
63791f110e0Safresh1    }
63891f110e0Safresh1}
63991f110e0Safresh1
64091f110e0Safresh1##################################
64191f110e0Safresh1
64291f110e0Safresh1=item C<$checker-E<gt>num_errors()>
64391f110e0Safresh1
64491f110e0Safresh1Set (if argument specified) and retrieve the number of errors found.
64591f110e0Safresh1
64691f110e0Safresh1=cut
64791f110e0Safresh1
64891f110e0Safresh1sub num_errors {
6495759b3d2Safresh1   return (@_ > 1) ? ($_[0]->{'_NUM_ERRORS'} = $_[1]) : $_[0]->{'_NUM_ERRORS'};
65091f110e0Safresh1}
65191f110e0Safresh1
65291f110e0Safresh1##################################
65391f110e0Safresh1
65491f110e0Safresh1=item C<$checker-E<gt>num_warnings()>
65591f110e0Safresh1
65691f110e0Safresh1Set (if argument specified) and retrieve the number of warnings found.
65791f110e0Safresh1
65891f110e0Safresh1=cut
65991f110e0Safresh1
66091f110e0Safresh1sub num_warnings {
6615759b3d2Safresh1   return (@_ > 1) ? ($_[0]->{'_NUM_WARNINGS'} = $_[1]) :
6625759b3d2Safresh1                      $_[0]->{'_NUM_WARNINGS'};
66391f110e0Safresh1}
66491f110e0Safresh1
66591f110e0Safresh1##################################
66691f110e0Safresh1
66791f110e0Safresh1=item C<$checker-E<gt>name()>
66891f110e0Safresh1
66991f110e0Safresh1Set (if argument specified) and retrieve the canonical name of POD as
67091f110e0Safresh1found in the C<=head1 NAME> section.
67191f110e0Safresh1
67291f110e0Safresh1=cut
67391f110e0Safresh1
67491f110e0Safresh1sub name {
67591f110e0Safresh1    return (@_ > 1 && $_[1]) ?
6765759b3d2Safresh1        ($_[0]->{'_pod_name'} = $_[1]) : $_[0]->{'_pod_name'};
67791f110e0Safresh1}
67891f110e0Safresh1
67991f110e0Safresh1##################################
68091f110e0Safresh1
68191f110e0Safresh1=item C<$checker-E<gt>node()>
68291f110e0Safresh1
68391f110e0Safresh1Add (if argument specified) and retrieve the nodes (as defined by C<=headX>
68491f110e0Safresh1and C<=item>) of the current POD. The nodes are returned in the order of
68591f110e0Safresh1their occurrence. They consist of plain text, each piece of whitespace is
68691f110e0Safresh1collapsed to a single blank.
68791f110e0Safresh1
68891f110e0Safresh1=cut
68991f110e0Safresh1
69091f110e0Safresh1sub node {
69191f110e0Safresh1    my ($self,$text) = @_;
69291f110e0Safresh1    if(defined $text) {
69391f110e0Safresh1        $text =~ s/\s+$//s; # strip trailing whitespace
69491f110e0Safresh1        $text =~ s/\s+/ /gs; # collapse whitespace
69591f110e0Safresh1        # add node, order important!
6965759b3d2Safresh1        push(@{$self->{'_nodes'}}, $text);
69791f110e0Safresh1        # keep also a uniqueness counter
6985759b3d2Safresh1        $self->{'_unique_nodes'}->{$text}++ if($text !~ /^\s*$/s);
69991f110e0Safresh1        return $text;
70091f110e0Safresh1    }
7015759b3d2Safresh1    @{$self->{'_nodes'}};
70291f110e0Safresh1}
70391f110e0Safresh1
70491f110e0Safresh1##################################
70591f110e0Safresh1
70691f110e0Safresh1=item C<$checker-E<gt>idx()>
70791f110e0Safresh1
70891f110e0Safresh1Add (if argument specified) and retrieve the index entries (as defined by
70991f110e0Safresh1C<XE<lt>E<gt>>) of the current POD. They consist of plain text, each piece
71091f110e0Safresh1of whitespace is collapsed to a single blank.
71191f110e0Safresh1
71291f110e0Safresh1=cut
71391f110e0Safresh1
71491f110e0Safresh1# set/return index entries of current POD
71591f110e0Safresh1sub idx {
71691f110e0Safresh1    my ($self,$text) = @_;
71791f110e0Safresh1    if(defined $text) {
71891f110e0Safresh1        $text =~ s/\s+$//s; # strip trailing whitespace
71991f110e0Safresh1        $text =~ s/\s+/ /gs; # collapse whitespace
72091f110e0Safresh1        # add node, order important!
7215759b3d2Safresh1        push(@{$self->{'_index'}}, $text);
72291f110e0Safresh1        # keep also a uniqueness counter
7235759b3d2Safresh1        $self->{'_unique_nodes'}->{$text}++ if($text !~ /^\s*$/s);
72491f110e0Safresh1        return $text;
72591f110e0Safresh1    }
7265759b3d2Safresh1    @{$self->{'_index'}};
72791f110e0Safresh1}
72891f110e0Safresh1
72991f110e0Safresh1##################################
73091f110e0Safresh1
7315759b3d2Safresh1# add a hyperlink to the list of those of the current POD; returns current
7325759b3d2Safresh1# list after the addition has been done
7335759b3d2Safresh1sub hyperlink {
7345759b3d2Safresh1    my $self = shift;
7355759b3d2Safresh1    push(@{$self->{'_links'}}, $_[0]);
7365759b3d2Safresh1    return $_[0];
7375759b3d2Safresh1}
73891f110e0Safresh1
7395759b3d2Safresh1=item C<$checker-E<gt>hyperlinks()>
74091f110e0Safresh1
7415759b3d2Safresh1Retrieve an array containing the hyperlinks to things outside
7425759b3d2Safresh1the current POD (as defined by C<LE<lt>E<gt>>).
7435759b3d2Safresh1
7445759b3d2Safresh1Each is an instance of a class with the following methods:
74591f110e0Safresh1
74691f110e0Safresh1=cut
74791f110e0Safresh1
7485759b3d2Safresh1sub hyperlinks {
7495759b3d2Safresh1    @{shift->{'_links'}};
7505759b3d2Safresh1}
7515759b3d2Safresh1
7525759b3d2Safresh1##################################
7535759b3d2Safresh1
7545759b3d2Safresh1# override Pod::Simple's whine() and scream() to use poderror()
7555759b3d2Safresh1
7565759b3d2Safresh1# Note:
7575759b3d2Safresh1# Ignore $self->{'no_whining'} b/c $self->{'quiet'} takes care of it in poderror
7585759b3d2Safresh1# Don't bother incrementing $self->{'errors_seen'} -- it's not used
7595759b3d2Safresh1# Don't bother pushing to $self->{'errata'} b/c poderror() outputs immediately
7605759b3d2Safresh1# We don't need to set $self->no_errata_section(1) b/c of these overrides
7615759b3d2Safresh1
7625759b3d2Safresh1
7635759b3d2Safresh1sub whine {
7645759b3d2Safresh1    my ($self, $line, $complaint) = @_;
7655759b3d2Safresh1
7665759b3d2Safresh1    my $severity = 'ERROR';
7675759b3d2Safresh1
7685759b3d2Safresh1    if (0) {
7695759b3d2Safresh1      # XXX: Let's standardize what's a warning and what's an error.  Let's not
7705759b3d2Safresh1      # move stuff up and down the severity tree.  -- rjbs, 2013-04-12
7715759b3d2Safresh1      # Convert errors in Pod::Simple that are warnings in Pod::Checker
7725759b3d2Safresh1      # XXX Do differently so the $complaint can be reworded without this breaking
7735759b3d2Safresh1      $severity = 'WARNING' if
7745759b3d2Safresh1          $complaint =~ /^Expected '=item .+?'$/ ||
7755759b3d2Safresh1          $complaint =~ /^You can't have =items \(as at line .+?\) unless the first thing after the =over is an =item$/ ||
7765759b3d2Safresh1          $complaint =~ /^You have '=item .+?' instead of the expected '=item .+?'$/;
7775759b3d2Safresh1    }
7785759b3d2Safresh1
779256a93a4Safresh1    # rt.cpan.org #98326 - errors about Z<> ("non-empty")
780256a93a4Safresh1    $severity = 'WARNING' if $complaint =~ /\bZ\<\>/;
781256a93a4Safresh1
7825759b3d2Safresh1    $self->poderror({ -line => $line,
7835759b3d2Safresh1                      -severity => $severity,
7845759b3d2Safresh1                      -msg => $complaint });
7855759b3d2Safresh1
7865759b3d2Safresh1    return 1; # assume everything is peachy keen
7875759b3d2Safresh1}
7885759b3d2Safresh1
7895759b3d2Safresh1sub scream {
7905759b3d2Safresh1    my ($self, $line, $complaint) = @_;
7915759b3d2Safresh1
7925759b3d2Safresh1    $self->poderror({ -line => $line,
7935759b3d2Safresh1                      -severity => 'ERROR', # consider making severity 'FATAL'
7945759b3d2Safresh1                      -msg => $complaint });
7955759b3d2Safresh1
7965759b3d2Safresh1    return 1;
7975759b3d2Safresh1}
7985759b3d2Safresh1
7995759b3d2Safresh1
8005759b3d2Safresh1##################################
8015759b3d2Safresh1
8025759b3d2Safresh1# Some helper subroutines
8035759b3d2Safresh1
8045759b3d2Safresh1sub _init_event { # assignments done at the start of most events
8055759b3d2Safresh1    $_[0]{'_thispara'} = '';
8065759b3d2Safresh1    $_[0]{'_line'} = $_[1]{'start_line'};
8075759b3d2Safresh1    $_[0]{'_cmds_since_head'}++;
8085759b3d2Safresh1}
8095759b3d2Safresh1
8105759b3d2Safresh1sub _check_fcode {
8115759b3d2Safresh1    my ($self, $inner, $outers) = @_;
8125759b3d2Safresh1    # Check for an fcode inside another of the same fcode
8135759b3d2Safresh1    # XXX line number is the line of the start of the paragraph that the warning
8145759b3d2Safresh1    # is in, not the line that the warning is on. Fix this
8155759b3d2Safresh1
8165759b3d2Safresh1    # Later versions of Pod::Simple forbid nested L<>'s
8175759b3d2Safresh1    return if $inner eq 'L' && $Pod::Simple::VERSION ge '3.33';
8185759b3d2Safresh1
8195759b3d2Safresh1    if (grep { $_ eq $inner } @$outers) {
8205759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
8215759b3d2Safresh1                          -severity => 'WARNING',
8225759b3d2Safresh1                          -msg => "nested commands $inner<...$inner<...>...>"});
8235759b3d2Safresh1    }
8245759b3d2Safresh1}
8255759b3d2Safresh1
8265759b3d2Safresh1##################################
8275759b3d2Safresh1
8285759b3d2Safresh1sub handle_text { $_[0]{'_thispara'} .= $_[1] }
8295759b3d2Safresh1
8305759b3d2Safresh1# whiteline is a seemingly blank line that matches /[^\S\r\n]/
8315759b3d2Safresh1sub handle_whiteline {
8325759b3d2Safresh1    my ($line, $line_n, $self) = @_;
8335759b3d2Safresh1    $self->poderror({
8345759b3d2Safresh1        -line => $line_n,
8355759b3d2Safresh1        -severity => 'WARNING',
8365759b3d2Safresh1        -msg => 'line containing nothing but whitespace in paragraph'});
8375759b3d2Safresh1}
8385759b3d2Safresh1
8395759b3d2Safresh1######## Directives
8405759b3d2Safresh1sub handle_pod_and_cut {
8415759b3d2Safresh1    my ($line, $line_n, $self) = @_;
8425759b3d2Safresh1    $self->{'_cmds_since_head'}++;
8435759b3d2Safresh1    if ($line =~ /=(pod|cut)\s+\S/) {
8445759b3d2Safresh1        $self->poderror({ -line => $line_n,
8455759b3d2Safresh1                          -severity => 'ERROR',
8465759b3d2Safresh1                          -msg => "Spurious text after =$1"});
8475759b3d2Safresh1    }
8485759b3d2Safresh1}
8495759b3d2Safresh1
8505759b3d2Safresh1sub start_Para { shift->_init_event(@_); }
8515759b3d2Safresh1sub end_Para   {
85291f110e0Safresh1    my $self = shift;
8535759b3d2Safresh1    # Get the NAME of the pod document
8545759b3d2Safresh1    if ($self->{'_head_num'} == 1 && $self->{'_head_text'} eq 'NAME') {
8555759b3d2Safresh1        if ($self->{'_thispara'} =~ /^\s*(\S+?)\s*[,-]/) {
8565759b3d2Safresh1            $self->{'_pod_name'} = $1 unless defined $self->{'_pod_name'};
85791f110e0Safresh1        }
8585759b3d2Safresh1    }
85991f110e0Safresh1}
86091f110e0Safresh1
8615759b3d2Safresh1sub start_Verbatim {
86291f110e0Safresh1    my $self = shift;
8635759b3d2Safresh1    $self->_init_event(@_);
86491f110e0Safresh1
8655759b3d2Safresh1    if ($self->{'_head_num'} == 1 && $self->{'_head_text'} eq 'NAME') {
8665759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
8675759b3d2Safresh1                          -severity => 'WARNING',
8685759b3d2Safresh1                          -msg => 'Verbatim paragraph in NAME section' });
8695759b3d2Safresh1    }
8705759b3d2Safresh1}
8715759b3d2Safresh1# Don't need an end_Verbatim
8725759b3d2Safresh1
8735759b3d2Safresh1# Do I need to do anything else with this?
8745759b3d2Safresh1sub start_Data { shift->_init_event() }
8755759b3d2Safresh1
8765759b3d2Safresh1sub start_head1 { shift->start_head(1, @_) }
8775759b3d2Safresh1sub start_head2 { shift->start_head(2, @_) }
8785759b3d2Safresh1sub start_head3 { shift->start_head(3, @_) }
8795759b3d2Safresh1sub start_head4 { shift->start_head(4, @_) }
8805759b3d2Safresh1sub start_head  {
8815759b3d2Safresh1    my $self = shift;
8825759b3d2Safresh1    my $h = shift;
8835759b3d2Safresh1    $self->_init_event(@_);
8845759b3d2Safresh1    my $prev_h = $self->{'_head_num'};
8855759b3d2Safresh1    $self->{'_head_num'} = $h;
8865759b3d2Safresh1    $self->{"_count_head$h"}++;
8875759b3d2Safresh1
8885759b3d2Safresh1    if ($h > 1 && !$self->{'_count_head'.($h-1)}) {
8895759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
8905759b3d2Safresh1                          -severity => 'WARNING',
8915759b3d2Safresh1                          -msg => "=head$h without preceding higher level"});
8925759b3d2Safresh1    }
8935759b3d2Safresh1
8945759b3d2Safresh1    # If this is the first =head of the doc, $prev_h is 0, thus less than $h
8955759b3d2Safresh1    if ($self->{'_cmds_since_head'} == 1 && $prev_h >= $h) {
8965759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
8975759b3d2Safresh1                          -severity => 'WARNING',
8985759b3d2Safresh1                          -msg => 'empty section in previous paragraph'});
89991f110e0Safresh1    }
90091f110e0Safresh1}
90191f110e0Safresh1
9025759b3d2Safresh1sub end_head1 { shift->end_head(@_) }
9035759b3d2Safresh1sub end_head2 { shift->end_head(@_) }
9045759b3d2Safresh1sub end_head3 { shift->end_head(@_) }
9055759b3d2Safresh1sub end_head4 { shift->end_head(@_) }
9065759b3d2Safresh1sub end_head  {
9075759b3d2Safresh1    my $self = shift;
9085759b3d2Safresh1    my $arg = $self->{'_thispara'};
9095759b3d2Safresh1    $arg =~ s/\s+$//;
9105759b3d2Safresh1    $self->{'_head_text'} = $arg;
9115759b3d2Safresh1    $self->{'_cmds_since_head'} = 0;
9125759b3d2Safresh1    my $h = $self->{'_head_num'};
9135759b3d2Safresh1    $self->node($arg); # remember this node
9145759b3d2Safresh1    if ($arg eq '') {
9155759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
9165759b3d2Safresh1                          -severity => 'ERROR',
9175759b3d2Safresh1                          -msg => "empty =head$h" });
9185759b3d2Safresh1    }
9195759b3d2Safresh1}
9205759b3d2Safresh1
9215759b3d2Safresh1sub start_over_bullet { shift->start_over(@_, 'bullet') }
9225759b3d2Safresh1sub start_over_number { shift->start_over(@_, 'number') }
9235759b3d2Safresh1sub start_over_text   { shift->start_over(@_, 'definition') }
9245759b3d2Safresh1sub start_over_block  { shift->start_over(@_, 'block') }
9255759b3d2Safresh1sub start_over_empty  {
9265759b3d2Safresh1    my $self = shift;
9275759b3d2Safresh1    $self->start_over(@_, 'empty');
9285759b3d2Safresh1    $self->poderror({ -line => $self->{'_line'},
9295759b3d2Safresh1                      -severity => 'WARNING',
9305759b3d2Safresh1                      -msg => 'empty =over/=back block' });
9315759b3d2Safresh1}
9325759b3d2Safresh1sub start_over {
9335759b3d2Safresh1    my $self = shift;
9345759b3d2Safresh1    my $type = pop;
9355759b3d2Safresh1    $self->_init_event(@_);
9365759b3d2Safresh1}
9375759b3d2Safresh1
9385759b3d2Safresh1sub start_item_bullet { shift->_init_event(@_) }
9395759b3d2Safresh1sub start_item_number { shift->_init_event(@_) }
9405759b3d2Safresh1sub start_item_text   { shift->_init_event(@_) }
9415759b3d2Safresh1sub end_item_bullet { shift->end_item('bullet') }
9425759b3d2Safresh1sub end_item_number { shift->end_item('number') }
9435759b3d2Safresh1sub end_item_text   { shift->end_item('definition') }
9445759b3d2Safresh1sub end_item {
9455759b3d2Safresh1    my $self = shift;
9465759b3d2Safresh1    my $type = shift;
9475759b3d2Safresh1    # If there is verbatim text in this item, it will show up as part of
9485759b3d2Safresh1    # 'paras', and not part of '_thispara'.  If the first para after this is a
9495759b3d2Safresh1    # verbatim one, it actually will be (part of) the contents for this item.
9505759b3d2Safresh1    if (   $self->{'_thispara'} eq ''
9515759b3d2Safresh1        && (  ! @{$self->{'paras'}}
9525759b3d2Safresh1            ||    $self->{'paras'}[0][0] !~ /Verbatim/i))
9535759b3d2Safresh1    {
9545759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
9555759b3d2Safresh1                          -severity => 'WARNING',
9565759b3d2Safresh1                          -msg => '=item has no contents' });
9575759b3d2Safresh1    }
9585759b3d2Safresh1
9595759b3d2Safresh1    $self->node($self->{'_thispara'}); # remember this node
9605759b3d2Safresh1}
9615759b3d2Safresh1
9625759b3d2Safresh1sub start_for { # =for and =begin directives
9635759b3d2Safresh1    my ($self, $flags) = @_;
9645759b3d2Safresh1    $self->_init_event($flags);
9655759b3d2Safresh1    push @{$self->{'_begin_stack'}}, [$self->{'_line'}, $flags->{'target'}];
9665759b3d2Safresh1}
9675759b3d2Safresh1
9685759b3d2Safresh1sub end_for {
9695759b3d2Safresh1    my ($self, $flags) = @_;
9705759b3d2Safresh1    my ($line, $target) = @{pop @{$self->{'_begin_stack'}}};
9715759b3d2Safresh1    if ($flags->{'fake-closer'}) { # meaning Pod::Simple generated this =end
9725759b3d2Safresh1        $self->poderror({ -line => $line,
9735759b3d2Safresh1                          -severity => 'ERROR',
9745759b3d2Safresh1                          -msg => "=begin $target without matching =end $target"
9755759b3d2Safresh1                        });
9765759b3d2Safresh1    }
9775759b3d2Safresh1}
9785759b3d2Safresh1
9795759b3d2Safresh1sub end_Document {
9805759b3d2Safresh1    # Some final error checks
9815759b3d2Safresh1    my $self = shift;
9825759b3d2Safresh1
9835759b3d2Safresh1    # no POD found here
9845759b3d2Safresh1    $self->num_errors(-1) && return unless $self->content_seen;
9855759b3d2Safresh1
98691f110e0Safresh1    my %nodes;
9875759b3d2Safresh1    for ($self->node()) {
98891f110e0Safresh1        $nodes{$_} = 1;
98991f110e0Safresh1        if(/^(\S+)\s+\S/) {
99091f110e0Safresh1            # we have more than one word. Use the first as a node, too.
99191f110e0Safresh1            # This is used heavily in perlfunc.pod
99291f110e0Safresh1            $nodes{$1} ||= 2; # derived node
99391f110e0Safresh1        }
99491f110e0Safresh1    }
9955759b3d2Safresh1    for ($self->idx()) {
99691f110e0Safresh1        $nodes{$_} = 3; # index node
99791f110e0Safresh1    }
9985759b3d2Safresh1
9995759b3d2Safresh1    # XXX update unresolved internal link POD -- single word not enclosed in ""?
10005759b3d2Safresh1    # I don't know what I was thinking when I made the above TODO, and I don't
10015759b3d2Safresh1    # know what it means...
10025759b3d2Safresh1
10035759b3d2Safresh1    for my $link (@{ $self->{'_internal_links'} }) {
10045759b3d2Safresh1        my ($name, $line) = @$link;
10055759b3d2Safresh1        unless ( $nodes{$name} ) {
10065759b3d2Safresh1            $self->poderror({ -line => $line,
100791f110e0Safresh1                              -severity => 'ERROR',
10085759b3d2Safresh1                              -msg => "unresolved internal link '$name'"});
100991f110e0Safresh1        }
101091f110e0Safresh1    }
101191f110e0Safresh1
101291f110e0Safresh1    # check the internal nodes for uniqueness. This pertains to
101391f110e0Safresh1    # =headX, =item and X<...>
10145759b3d2Safresh1    if ($self->{'-warnings'} > 1 ) {
10155759b3d2Safresh1        for my $node (sort keys %{ $self->{'_unique_nodes'} }) {
10165759b3d2Safresh1            my $count = $self->{'_unique_nodes'}{$node};
10175759b3d2Safresh1            if ($count > 1) { # not unique
10185759b3d2Safresh1                $self->poderror({
10195759b3d2Safresh1                    -line => '-',
102091f110e0Safresh1                    -severity => 'WARNING',
10215759b3d2Safresh1                    -msg => "multiple occurrences ($count) of link target ".
10225759b3d2Safresh1                        "'$node'"});
102391f110e0Safresh1            }
102491f110e0Safresh1        }
102591f110e0Safresh1    }
102691f110e0Safresh1}
102791f110e0Safresh1
10285759b3d2Safresh1########  Formatting codes
10295759b3d2Safresh1
10305759b3d2Safresh1sub start_B { shift->start_fcode('B') }
10315759b3d2Safresh1sub start_C { shift->start_fcode('C') }
10325759b3d2Safresh1sub start_F { shift->start_fcode('F') }
10335759b3d2Safresh1sub start_I { shift->start_fcode('I') }
10345759b3d2Safresh1sub start_S { shift->start_fcode('S') }
10355759b3d2Safresh1sub start_fcode {
10365759b3d2Safresh1    my ($self, $fcode) = @_;
10375759b3d2Safresh1    unshift @{$self->{'_fcode_stack'}}, $fcode;
10385759b3d2Safresh1}
10395759b3d2Safresh1
10405759b3d2Safresh1sub end_B { shift->end_fcode() }
10415759b3d2Safresh1sub end_C { shift->end_fcode() }
10425759b3d2Safresh1sub end_F { shift->end_fcode() }
10435759b3d2Safresh1sub end_I { shift->end_fcode() }
10445759b3d2Safresh1sub end_S { shift->end_fcode() }
10455759b3d2Safresh1sub end_fcode {
104691f110e0Safresh1    my $self = shift;
10475759b3d2Safresh1    $self->_check_fcode(shift @{$self->{'_fcode_stack'}}, # current fcode removed
10485759b3d2Safresh1                        $self->{'_fcode_stack'}); # previous fcodes
10495759b3d2Safresh1}
10505759b3d2Safresh1
10515759b3d2Safresh1sub start_L {
10525759b3d2Safresh1    my ($self, $flags) = @_;
10535759b3d2Safresh1    $self->start_fcode('L');
10545759b3d2Safresh1
10555759b3d2Safresh1    my $link = Pod::Checker::Hyperlink->new($flags, $self);
10565759b3d2Safresh1    if ($link) {
10575759b3d2Safresh1        if (   $link->type eq 'pod'
10585759b3d2Safresh1            && $link->node
10595759b3d2Safresh1                # It's an internal-to-this-page link if no page is given, or
10605759b3d2Safresh1                # if the given one is to our NAME.
10615759b3d2Safresh1            && (! $link->page || (   $self->{'_pod_name'}
10625759b3d2Safresh1                                  && $link->page eq $self->{'_pod_name'})))
10635759b3d2Safresh1        {
10645759b3d2Safresh1            push @{ $self->{'_internal_links'} }, [ $link->{'-raw_node'}, $link->line ];
10655759b3d2Safresh1        }
10665759b3d2Safresh1        else {
10675759b3d2Safresh1            $self->hyperlink($link);
106891f110e0Safresh1        }
106991f110e0Safresh1    }
107091f110e0Safresh1}
107191f110e0Safresh1
10725759b3d2Safresh1sub end_L {
10735759b3d2Safresh1    my $self = shift;
10745759b3d2Safresh1    $self->end_fcode();
10755759b3d2Safresh1}
107691f110e0Safresh1
10775759b3d2Safresh1sub start_X {
10785759b3d2Safresh1    my $self = shift;
10795759b3d2Safresh1    $self->start_fcode('X');
10805759b3d2Safresh1    # keep track of where X<> starts in the paragraph
10815759b3d2Safresh1    # (this is a stack so nested X<>s are handled correctly)
10825759b3d2Safresh1    push @{$self->{'_fcode_pos'}}, length $self->{'_thispara'};
10835759b3d2Safresh1}
10845759b3d2Safresh1sub end_X {
10855759b3d2Safresh1    my $self = shift;
10865759b3d2Safresh1    # extract contents of X<> and replace with ''
10875759b3d2Safresh1    my $start = pop @{$self->{'_fcode_pos'}}; # start at the beginning of X<>
10885759b3d2Safresh1    my $end = length($self->{'_thispara'}) - $start; # end at end of X<>
10895759b3d2Safresh1    my $x = substr($self->{'_thispara'}, $start, $end, '');
10905759b3d2Safresh1    if ($x eq "") {
10915759b3d2Safresh1        $self->poderror({ -line => $self->{'_line'},
109291f110e0Safresh1                          -severity => 'ERROR',
10935759b3d2Safresh1                          -msg => "An empty X<>" });
109491f110e0Safresh1    }
10955759b3d2Safresh1    $self->idx($x); # remember this node
10965759b3d2Safresh1    $self->end_fcode();
109791f110e0Safresh1}
109891f110e0Safresh1
10995759b3d2Safresh1package Pod::Checker::Hyperlink;
110091f110e0Safresh1
11015759b3d2Safresh1# This class is used to represent L<> link structures, so that the individual
11025759b3d2Safresh1# elements are easily accessible.  It is based on code in Pod::Hyperlink
11035759b3d2Safresh1
11045759b3d2Safresh1sub new {
11055759b3d2Safresh1    my ($class,
11065759b3d2Safresh1        $simple_link,   # The link structure returned by Pod::Simple
11075759b3d2Safresh1        $caller         # The caller class
11085759b3d2Safresh1    ) = @_;
11095759b3d2Safresh1
11105759b3d2Safresh1    my $self = +{};
11115759b3d2Safresh1    bless $self, $class;
11125759b3d2Safresh1
11135759b3d2Safresh1    $self->{'-line'} ||= $caller->{'_line'};
11145759b3d2Safresh1    $self->{'-type'} ||= $simple_link->{'type'};
1115f2a19305Safresh1    # preserve raw link text for additional checks
1116f2a19305Safresh1    $self->{'-raw-link-text'} = (exists $simple_link->{'raw'})
1117f2a19305Safresh1                                ? "$simple_link->{'raw'}"
1118f2a19305Safresh1                                : "";
11195759b3d2Safresh1    # Force stringification of page and node.  (This expands any E<>.)
11205759b3d2Safresh1    $self->{'-page'} = exists $simple_link->{'to'} ? "$simple_link->{'to'}" : "";
11215759b3d2Safresh1    $self->{'-node'} = exists $simple_link->{'section'} ? "$simple_link->{'section'}" : "";
11225759b3d2Safresh1
11235759b3d2Safresh1    # Save the unmodified node text, as the .t files are expecting the message
11245759b3d2Safresh1    # for internal link failures to include it (hence this preserves backward
11255759b3d2Safresh1    # compatibility).
11265759b3d2Safresh1    $self->{'-raw_node'} = $self->{'-node'};
11275759b3d2Safresh1
11285759b3d2Safresh1    # Remove leading/trailing white space.  Pod::Simple already warns about
11295759b3d2Safresh1    # these, so if the only error is this, and the link is otherwise correct,
11305759b3d2Safresh1    # only the Pod::Simple warning will be output, avoiding unnecessary
11315759b3d2Safresh1    # confusion.
11325759b3d2Safresh1    $self->{'-page'} =~ s/ ^ \s+ //x;
11335759b3d2Safresh1    $self->{'-page'} =~ s/ \s+ $ //x;
11345759b3d2Safresh1
11355759b3d2Safresh1    $self->{'-node'} =~ s/ ^ \s+ //x;
11365759b3d2Safresh1    $self->{'-node'} =~ s/ \s+ $ //x;
11375759b3d2Safresh1
11385759b3d2Safresh1    # Pod::Simple warns about L<> and L< >, but not L</>
11395759b3d2Safresh1    if ($self->{'-page'} eq "" && $self->{'-node'} eq "") {
11405759b3d2Safresh1        $caller->poderror({ -line => $caller->{'_line'},
11415759b3d2Safresh1                          -severity => 'WARNING',
11425759b3d2Safresh1                          -msg => 'empty link'});
11435759b3d2Safresh1        return;
11445759b3d2Safresh1    }
11455759b3d2Safresh1
11465759b3d2Safresh1    return $self;
11475759b3d2Safresh1}
11485759b3d2Safresh1
11495759b3d2Safresh1=item line()
11505759b3d2Safresh1
11515759b3d2Safresh1Returns the approximate line number in which the link was encountered
11525759b3d2Safresh1
11535759b3d2Safresh1=cut
11545759b3d2Safresh1
11555759b3d2Safresh1sub line {
11565759b3d2Safresh1    return $_[0]->{-line};
11575759b3d2Safresh1}
11585759b3d2Safresh1
11595759b3d2Safresh1=item type()
11605759b3d2Safresh1
11615759b3d2Safresh1Returns the type of the link; one of:
11625759b3d2Safresh1C<"url"> for things like
11635759b3d2Safresh1C<http://www.foo>, C<"man"> for man pages, or C<"pod">.
11645759b3d2Safresh1
11655759b3d2Safresh1=cut
11665759b3d2Safresh1
11675759b3d2Safresh1sub type {
11685759b3d2Safresh1    return  $_[0]->{-type};
11695759b3d2Safresh1}
11705759b3d2Safresh1
11715759b3d2Safresh1=item page()
11725759b3d2Safresh1
11735759b3d2Safresh1Returns the linked-to page or url.
11745759b3d2Safresh1
11755759b3d2Safresh1=cut
11765759b3d2Safresh1
11775759b3d2Safresh1sub page {
11785759b3d2Safresh1    return $_[0]->{-page};
11795759b3d2Safresh1}
11805759b3d2Safresh1
11815759b3d2Safresh1=item node()
11825759b3d2Safresh1
11835759b3d2Safresh1Returns the anchor or node within the linked-to page, or an empty string
11845759b3d2Safresh1(C<"">) if none appears in the link.
11855759b3d2Safresh1
11865759b3d2Safresh1=back
11875759b3d2Safresh1
11885759b3d2Safresh1=cut
11895759b3d2Safresh1
11905759b3d2Safresh1sub node {
11915759b3d2Safresh1    return $_[0]->{-node};
11925759b3d2Safresh1}
119391f110e0Safresh1
119491f110e0Safresh1=head1 AUTHOR
119591f110e0Safresh1
119691f110e0Safresh1Please report bugs using L<http://rt.cpan.org>.
119791f110e0Safresh1
119891f110e0Safresh1Brad Appleton E<lt>bradapp@enteract.comE<gt> (initial version),
11995759b3d2Safresh1Marek Rouchal E<lt>marekr@cpan.orgE<gt>,
12005759b3d2Safresh1Marc Green E<lt>marcgreen@cpan.orgE<gt> (port to Pod::Simple)
12015759b3d2Safresh1Ricardo Signes E<lt>rjbs@cpan.orgE<gt> (more porting to Pod::Simple)
12025759b3d2Safresh1Karl Williamson E<lt>khw@cpan.orgE<gt> (more porting to Pod::Simple)
120391f110e0Safresh1
120491f110e0Safresh1Based on code for B<Pod::Text::pod2text()> written by
120591f110e0Safresh1Tom Christiansen E<lt>tchrist@mox.perl.comE<gt>
120691f110e0Safresh1
120791f110e0Safresh1=cut
120891f110e0Safresh1
12095759b3d2Safresh11
1210