| #!/usr/bin/perl |
| |
| use strict; |
| use warnings; |
| |
| my %SECTIONS; |
| { |
| my $order = 0; |
| %SECTIONS = ( |
| 'NAME' => { |
| required => 1, |
| order => $order++, |
| }, |
| 'SYNOPSIS' => { |
| required => 1, |
| order => $order++, |
| }, |
| 'DESCRIPTION' => { |
| required => 1, |
| order => $order++, |
| }, |
| 'OPTIONS' => { |
| order => $order++, |
| required => 0, |
| }, |
| 'CONFIGURATION' => { |
| order => $order++, |
| }, |
| 'BUGS' => { |
| order => $order++, |
| }, |
| 'SEE ALSO' => { |
| order => $order++, |
| }, |
| 'FILE FORMAT' => { |
| order => $order++, |
| }, |
| 'GIT' => { |
| required => 1, |
| order => $order++, |
| }, |
| ); |
| } |
| my $SECTION_RX = do { |
| my ($names) = join "|", keys %SECTIONS; |
| qr/^($names)$/s; |
| }; |
| |
| my $exit_code = 0; |
| sub report { |
| my ($msg) = @_; |
| print STDERR "$ARGV:$.: $msg\n"; |
| $exit_code = 1; |
| } |
| |
| my $last_was_section; |
| my @actual_order; |
| while (my $line = <>) { |
| chomp $line; |
| if ($line =~ $SECTION_RX) { |
| push @actual_order => $line; |
| $last_was_section = 1; |
| # Have no "last" section yet, processing NAME |
| next if @actual_order == 1; |
| |
| my @expected_order = sort { |
| $SECTIONS{$a}->{order} <=> $SECTIONS{$b}->{order} |
| } @actual_order; |
| |
| my $expected_last = $expected_order[-2]; |
| my $actual_last = $actual_order[-2]; |
| if ($actual_last ne $expected_last) { |
| report("section '$line' incorrectly ordered, comes after '$actual_last'"); |
| } |
| next; |
| } |
| if ($last_was_section) { |
| my $last_section = $actual_order[-1]; |
| if (length $last_section ne length $line) { |
| report("dashes under '$last_section' should match its length!"); |
| } |
| if ($line !~ /^-+$/) { |
| report("dashes under '$last_section' should be '-' dashes!"); |
| } |
| $last_was_section = 0; |
| } |
| |
| if (eof) { |
| # We have both a hash and an array to consider, for |
| # convenience |
| my %actual_sections; |
| @actual_sections{@actual_order} = (); |
| |
| for my $section (sort keys %SECTIONS) { |
| next if !$SECTIONS{$section}->{required} or exists $actual_sections{$section}; |
| report("has no required '$section' section!"); |
| } |
| |
| # Reset per-file state |
| { |
| @actual_order = (); |
| # this resets our $. for each file |
| close ARGV; |
| } |
| } |
| } |
| |
| exit $exit_code; |