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